carlinx-oauth2-client
v0.1.5
Published
A flexible OAuth2 client for Authorization Code flow, designed to be framework-agnostic and easy to integrate.
Maintainers
Readme
carlinx-oauth2-client
一个灵活的 OAuth2 客户端,支持 Authorization Code 流程,框架无关,易于集成。
特性
- 🎯 框架无关:纯 TypeScript 实现,不依赖 Vue/React/Angular
- 🔧 灵活适配:可自定义 Storage 和 API Client 适配器
- 📦 易于使用:简洁的 API 处理 OAuth2 授权码流程
- 🔒 State 支持:支持传入 state 参数
安装
npm install carlinx-oauth2-client
# 或
pnpm add carlinx-oauth2-client
# 或
yarn add carlinx-oauth2-client快速开始
import { OAuth2Client } from 'carlinx-oauth2-client';
// 初始化 OAuth2 客户端
const oauthClient = new OAuth2Client({
authUrl: 'https://oauth.example.com/oauth2/authorize',
clientId: 'your-client-id',
redirectUri: 'https://your-app.com/oauth/callback',
scope: 'userinfo',
});
// 发起登录(跳转到授权服务器)
oauthClient.login();
// 在回调页面处理回调
const result = await oauthClient.handleCallback();
if (result.success) {
console.log('登录成功,token:', result.token);
console.log('用户信息:', result.user);
} else {
console.error('登录失败:', result.error);
}
// 检查登录状态
if (oauthClient.isLoggedIn()) {
const token = oauthClient.getToken();
}
// 清除 token
oauthClient.clearToken();State 参数
state 参数用于防止 CSRF 攻击。使用时需要自行生成、存储和验证:
// 生成并存储 state
const state = Math.random().toString(36).substring(2, 15);
sessionStorage.setItem('oauth_state', state);
// 登录时传入
oauthClient.login(state);
// 回调时验证
const savedState = sessionStorage.getItem('oauth_state');
const returnedState = new URLSearchParams(window.location.search).get('state');
if (savedState !== returnedState) {
console.error('State 验证失败,拒绝登录');
return;
}
// 验证通过后处理回调
const result = await oauthClient.handleCallback();自定义适配器
Storage 适配器
内置三种存储适配器:
LocalStorageAdapter- 浏览器 localStorage(默认)SessionStorageAdapter- 浏览器 sessionStorageMemoryStorageAdapter- 内存存储(适用于测试)
import { OAuth2Client, SessionStorageAdapter } from 'carlinx-oauth2-client';
const client = new OAuth2Client(config, {
storage: new SessionStorageAdapter(),
});API Client 适配器
默认使用 FetchAPIClient。如果 API 在不同域名,需设置 baseUrl:
import { OAuth2Client, FetchAPIClient } from 'carlinx-oauth2-client';
const client = new OAuth2Client(config, {
apiClient: new FetchAPIClient('https://api.example.com'),
tokenEndpoint: '/auth/login', // 会请求 https://api.example.com/auth/login
});使用 POST 方式换取 token(推荐):
const client = new OAuth2Client(config, {
apiClient: new FetchAPIClient('https://api.example.com'),
tokenEndpoint: '/auth/login',
tokenMethod: 'POST', // POST /auth/login,body: { "code": "xxx" }
});也可以使用 axios:
import { OAuth2Client } from 'carlinx-oauth2-client';
import axios from 'axios';
class AxiosApiClient implements APIClientAdapter {
private baseUrl: string;
constructor(baseUrl: string) { this.baseUrl = baseUrl; }
async get<T>(url: string, params?: Record<string, string>): Promise<T> {
const response = await axios.get(`${this.baseUrl}${url}`, { params });
return response.data;
}
}
const client = new OAuth2Client(config, {
apiClient: new AxiosApiClient('https://api.example.com'),
tokenEndpoint: '/auth/login',
});API 参考
OAuth2Config
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| authUrl | string | ✅ | OAuth2 授权服务器地址 |
| clientId | string | ✅ | 客户端 ID |
| redirectUri | string | ✅ | 回调地址 |
| scope | string | ❌ | 权限范围 |
OAuth2ClientOptions
| 参数 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| storage | StorageAdapter | LocalStorageAdapter | Token 存储适配器 |
| apiClient | APIClientAdapter | FetchAPIClient | API 请求适配器 |
| tokenKey | string | 'accessToken' | Token 存储的 key |
| tokenEndpoint | string | '/auth/login' | 换取 Token 的 API 地址 |
| tokenMethod | 'GET' | 'POST' | 'GET' | Token 请求方式 |
方法
| 方法 | 说明 |
|------|------|
| login(state?) | 跳转到授权服务器 |
| handleCallback() | 处理回调,用 code 换取 token |
| isLoggedIn() | 检查是否已登录 |
| getToken() | 获取存储的 token |
| clearToken() | 清除存储的 token |
| generateAuthUrl(state?) | 生成授权 URL |
OAuth2LoginResult
interface OAuth2LoginResult {
success: boolean;
token?: string;
user?: { userId: string | number; nickname?: string; avatar?: string };
error?: string;
errorDescription?: string;
}错误处理
const result = await oauthClient.handleCallback();
if (!result.success) {
switch (result.error) {
case 'no_code':
// URL 中没有 code
break;
case 'invalid_response':
// API 返回格式错误
break;
case 'api_error':
// API 请求失败
console.error(result.errorDescription);
break;
default:
// 授权服务器返回的错误(如 access_denied)
console.error(result.error, result.errorDescription);
}
}许可证
MIT
