OAuth 客户端 for TypeScript:授权实现完整指南
在现代 Web 开发中,用户安全身份验证是一项关键任务。OAuth 2.0 是一种开放的授权协议,允许应用程序在不传递用户名和密码的情况下获取对用户资源的有限访问权限。在本文中,我们将介绍如何创建一个 TypeScript 的 OAuth 客户端,该客户端可用于任何 Web 应用。该库适用于与 Google、GitHub、Facebook 及其他提供商集成。
什么是 TypeScript 的 OAuth 客户端?
OAuth 客户端是一组实现客户端或服务器端授权流程(授权码流程)的函数和类。使用 TypeScript 的此类客户端提供了严格的类型检查,从而减少错误数量并简化代码维护。客户端的主要任务包括:
- 生成用于将用户重定向到提供商页面的 URL。
- 将临时代码交换为 access token 和 refresh token。
- 存储和更新令牌。
- 验证请求参数(state、nonce)。
库的架构
该库由多个模块组成:
OAuthClient— 管理所有阶段的主类。TokenStore— 用于存储令牌的接口(可在内存、localStorage 或数据库中实现)。ProviderConfig— 特定提供商的配置(clientId、redirectUri、端点)。AuthError— 用于处理授权错误的类。
TypeScript 实现示例
以下是 TypeScript 的 OAuth 客户端基本示例。首先定义类型和接口:
// types.tsexport interface TokenResponse { access_token: string; token_type: string; expires_in: number; refresh_token?: string; scope?: string;}
export interface ProviderConfig { authorizationEndpoint: string; tokenEndpoint: string; clientId: string; redirectUri: string; scope: string;}
export interface TokenStore { getToken(): Promise<string | null>; setToken(token: TokenResponse): Promise<void>; clearToken(): Promise<void>;}现在创建主类 OAuthClient:
// OAuthClient.tsimport { TokenResponse, ProviderConfig, TokenStore } from './types';import { AuthError } from './AuthError';
export class OAuthClient { private config: ProviderConfig; private store: TokenStore;
constructor(config: ProviderConfig, store: TokenStore) { this.config = config; this.store = store; }
// 生成授权 URL getAuthorizationUrl(state: string): string { const params = new URLSearchParams({ client_id: this.config.clientId, redirect_uri: this.config.redirectUri, response_type: 'code', scope: this.config.scope, state: state, }); return `${this.config.authorizationEndpoint}?${params.toString()}`; }
// 将代码交换为令牌 async exchangeCodeForToken(code: string, state: string, expectedState: string): Promise<TokenResponse> { if (state !== expectedState) { throw new AuthError('State mismatch. Possible CSRF attack.'); }
const body = new URLSearchParams({ grant_type: 'authorization_code', code: code, redirect_uri: this.config.redirectUri, client_id: this.config.clientId, });
const response = await fetch(this.config.tokenEndpoint, { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: body, });
if (!response.ok)