OAuth 客户端 for TypeScript:教育网站上的授权实现
在现代 Web 开发中,OAuth(开放授权)已成为通过第三方服务对用户进行身份验证的事实标准。对于教育网站 pythonlib.ru,我们准备了一个基于 TypeScript 的通用 OAuth 客户端的描述,该客户端允许通过 GitHub、Google 和其他流行提供商实现登录。
什么是 OAuth 客户端?
OAuth 客户端是一个软件组件,用于管理从用户处获取权限以访问其在第三方服务上的数据的过程。在 TypeScript 的上下文中,此类客户端通常实现 OAuth 2.0 协议,并包括:
- 初始化授权过程
- 处理回调请求
- 将 authorization code 交换为 access token
- 更新令牌(refresh token)
- 处理会话状态
TypeScript 上 OAuth 客户端的架构
我们的客户端基于模块化和类型安全原则构建。主要组件:
1. 提供商的配置
interface OAuthProviderConfig { clientId: string; clientSecret: string; redirectUri: string; authorizationEndpoint: string; tokenEndpoint: string; scopes: string[];}
const githubConfig: OAuthProviderConfig = { clientId: process.env.GITHUB_CLIENT_ID!, clientSecret: process.env.GITHUB_CLIENT_SECRET!, redirectUri: 'https://pythonlib.ru/auth/callback/github', authorizationEndpoint: 'https://github.com/login/oauth/authorize', tokenEndpoint: 'https://github.com/login/oauth/access_token', scopes: ['read:user', 'user:email']};2. 生成授权 URL
function generateAuthUrl(config: OAuthProviderConfig, state: string): string { const params = new URLSearchParams({ client_id: config.clientId, redirect_uri: config.redirectUri, scope: config.scopes.join(' '), state: state, response_type: 'code' }); return `${config.authorizationEndpoint}?${params.toString()}`;}3. 将代码交换为令牌
async function exchangeCodeForToken( config: OAuthProviderConfig, code: string): Promise<OAuthTokenResponse> { const response = await fetch(config.tokenEndpoint, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' }, body: JSON.stringify({ client_id: config.clientId, client_secret: config.clientSecret, code: code, redirect_uri: config.redirectUri }) });
if (!response.ok) { throw new Error(`Token exchange failed: ${response.statusText}`); }
return response.json();}令牌的使用
获取令牌后,必须确保其安全存储和更新。建议使用 httpOnly cookies 或服务器端会话存储。
更新 access token
async function refreshAccessToken( config: OAuthProviderConfig, refreshToken: string): Promise<OAuthTokenResponse> { const response = await fetch(config.tokenEndpoint, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ client_id: config.clientId, client_secret: config.clientSecret, refresh_token: refreshToken, grant_type: 'refresh_token' }) });
return response.json();}GitHub OAuth 的完整实现示例
import { randomBytes } from 'crypto';
class GitHubOAuthClient { private config: OAuthProviderConfig; private stateStore: Map<string, string> = new Map();
constructor() { this.config = { clientId: process.env.GITHUB_CLIENT_ID!, clientSecret: process.env.GITHUB_CLIENT_SECRET!, redirec