@ deliverart/ sdk-js- 认证
交付艺术JavaScript SDK的认证插件. 在所有API请求中自动添加 Bearer 令牌认证.
安装
npm install @deliverart/sdk-js-authentication @deliverart/sdk-js-core
# or
pnpm add @deliverart/sdk-js-authentication @deliverart/sdk-js-core
# or
yarn add @deliverart/sdk-js-authentication @deliverart/sdk-js-core
导出类型
认证 插件
class AuthenticationPlugin implements ApiClientPlugin<AuthenticationExtension>
在所有API请求中添加 JWT Bearer 令牌认证的插件 .
构造器 :
constructor(config: AuthenticationConfig)
认证配置
interface AuthenticationConfig {
getAccessToken: () => Promise<string | null>
}
认证插件的配置对象 .
属性 :
getAccessToken: () => Promise<string | null>(需要) - 返回访问符的 Async 函数, 如果未认证则无效
认证扩展
interface AuthenticationExtension extends ApiExtension {
authentication: {
enable: () => void;
disable: () => void;
}
}
插件安装时添加到API客户端中.
方法:
enable()- 启用认证( 对请求添加授权头)disable()- 禁用认证(skips 添加授权头)
错误
认证错误
class AuthenticationError extends Error
当需要认证时抛出,但没有访问令牌可用.
使用量
基本设置
import { createApiClient } from '@deliverart/sdk-js-core';
import { AuthenticationPlugin } from '@deliverart/sdk-js-authentication';
const getAccessToken = async (): Promise<string | null> => {
// Retrieve token from your storage
return localStorage.getItem('accessToken');
};
const client = createApiClient({
baseUrl: 'https://api.deliverart.com'
})
.addPlugin(new AuthenticationPlugin({ getAccessToken }));
参数:
getAccessToken(需要) - 获取当前访问符的函数
使用不同的托肯存储
本地服务器
const getAccessToken = async (): Promise<string | null> => {
return localStorage.getItem('accessToken');
};
会话集
const getAccessToken = async (): Promise<string | null> => {
return sessionStorage.getItem('accessToken');
};
饼干
import Cookies from 'js-cookie';
const getAccessToken = async (): Promise<string | null> => {
return Cookies.get('accessToken') ?? null;
};
Next.js 有服务器动作
import { cookies } from 'next/headers';
const getAccessToken = async (): Promise<string | null> => {
const cookieStore = await cookies();
return cookieStore.get('accessToken')?.value ?? null;
};
Auth 提供者(例如 Auth0, 办事员)
import { useAuth } from '@clerk/nextjs';
const getAccessToken = async (): Promise<string | null> => {
const { getToken } = useAuth();
return await getToken();
};
启用/禁用认证
// Disable authentication temporarily
client.authentication.disable();
// Make unauthenticated requests
await client.call(new PublicRequest());
// Re-enable authentication
client.authentication.enable();
// Make authenticated requests
await client.call(new ProtectedRequest());
使用大小写 :
- 不需要认证的公用端点
- 来宾离职流量
- 登记/登记申请
带有下个.js的完整示例
// lib/sdk/client.ts
import { createApiClient, type ApiClient } from '@deliverart/sdk-js-core';
import { AuthenticationPlugin } from '@deliverart/sdk-js-authentication';
import { ErrorHandlerPlugin } from '@deliverart/sdk-js-error-handler';
const getAccessToken = async (): Promise<string | null> => {
// Server-side: get from cookies
if (typeof window === 'undefined') {
const { cookies } = await import('next/headers');
const cookieStore = await cookies();
return cookieStore.get('accessToken')?.value ?? null;
}
// Client-side: get from localStorage
return localStorage.getItem('accessToken');
};
export const sdk = createApiClient({
baseUrl: process.env.NEXT_PUBLIC_API_BASE_URL!
})
.addPlugin(new ErrorHandlerPlugin())
.addPlugin(new AuthenticationPlugin({ getAccessToken }));
登录流程示例
import { sdk } from './lib/sdk/client';
import { Login } from '@deliverart/sdk-js-auth';
// Disable authentication for login request
sdk.authentication.disable();
try {
const response = await sdk.call(new Login({
email: '[email protected]',
password: 'password123'
}));
// Store the token
localStorage.setItem('accessToken', response.token);
// Re-enable authentication for subsequent requests
sdk.authentication.enable();
// Now all requests will include the Authorization header
const user = await sdk.call(new GetCurrentUser());
} catch (error) {
console.error('Login failed:', error);
}
日志流程示例
import { sdk } from './lib/sdk/client';
// Clear the token
localStorage.removeItem('accessToken');
// Disable authentication (optional, depends on your needs)
sdk.authentication.disable();
// Redirect to login page
window.location.href = '/login';
托肯刷新示例
import { sdk } from './lib/sdk/client';
import { AuthenticationError } from '@deliverart/sdk-js-authentication';
let tokenRefreshPromise: Promise<string> | null = null;
const getAccessToken = async (): Promise<string | null> => {
let token = localStorage.getItem('accessToken');
// Check if token is expired (you need to implement this)
if (token && isTokenExpired(token)) {
// Ensure only one refresh happens at a time
if (!tokenRefreshPromise) {
tokenRefreshPromise = refreshToken();
}
try {
token = await tokenRefreshPromise;
localStorage.setItem('accessToken', token);
} finally {
tokenRefreshPromise = null;
}
}
return token;
};
async function refreshToken(): Promise<string> {
const refreshToken = localStorage.getItem('refreshToken');
if (!refreshToken) {
throw new AuthenticationError('No refresh token available');
}
// Disable authentication for refresh request
sdk.authentication.disable();
try {
const response = await sdk.call(new RefreshToken({ refreshToken }));
sdk.authentication.enable();
return response.accessToken;
} catch (error) {
// Refresh failed, redirect to login
localStorage.clear();
window.location.href = '/login';
throw error;
}
}
function isTokenExpired(token: string): boolean {
try {
const payload = JSON.parse(atob(token.split('.')[1]));
return payload.exp * 1000 < Date.now();
} catch {
return true;
}
}
处理错误
import { AuthenticationError } from '@deliverart/sdk-js-authentication';
try {
await sdk.call(new GetUser('123'));
} catch (error) {
if (error instanceof AuthenticationError) {
console.error('Authentication failed:', error.message);
// Redirect to login page
window.location.href = '/login';
}
}
如何运作
该插件增加了一个请求中间软件:
- 打电话给你
getAccessToken函数以获取当前符号 - 如果找到一个符号,请添加到
Authorization页眉为Bearer <token> - 如果找不到令牌并启用认证, 则丢出一个
AuthenticationError - 如果禁用认证, 跳过添加授权头
最佳做法
1. 集中托肯管理
创建用于符号管理的专用模块 :
// lib/auth/tokens.ts
export const setAccessToken = (token: string) => {
localStorage.setItem('accessToken', token);
};
export const getAccessToken = (): string | null => {
return localStorage.getItem('accessToken');
};
export const clearTokens = () => {
localStorage.removeItem('accessToken');
localStorage.removeItem('refreshToken');
};
2. 安全托肯存储
- 使用
httpOnly可能时为令牌提供饼干( 更加安全) - 如果可能,避免在生产时将敏感标志储存在当地Storage中
- 考虑为移动应用程序使用安全存储库
3. 处理托肯过期
在令牌过期前执行自动令牌刷新:
const getAccessToken = async (): Promise<string | null> => {
const token = localStorage.getItem('accessToken');
if (token && willExpireSoon(token)) {
return await refreshAccessToken();
}
return token;
};
function willExpireSoon(token: string, bufferSeconds = 60): boolean {
try {
const payload = JSON.parse(atob(token.split('.')[1]));
return payload.exp * 1000 < Date.now() + (bufferSeconds * 1000);
} catch {
return true;
}
}
4. 全球错误处理
为认证错误设置全局错误处理 :
import { ApiError } from '@deliverart/sdk-js-error-handler';
window.addEventListener('unhandledrejection', (event) => {
if (event.reason instanceof ApiError && event.reason.statusCode === 401) {
// Handle unauthorized errors globally
clearTokens();
window.location.href = '/login';
}
});
许可证
麻省理工学院