205 lines
7.0 KiB
TypeScript
205 lines
7.0 KiB
TypeScript
import type {
|
|
AuthorizationCode,
|
|
AccessToken,
|
|
RefreshToken,
|
|
IDToken,
|
|
} from '../types';
|
|
import type { StorageAdapter } from '../storage';
|
|
|
|
/**
|
|
* 令牌管理器
|
|
* 处理所有令牌相关的存储业务逻辑
|
|
*/
|
|
export class TokenManager {
|
|
constructor(private storage: StorageAdapter) { }
|
|
|
|
// 生成存储键
|
|
private getTokenKey(type: string, token: string): string {
|
|
return `${type}:${token}`;
|
|
}
|
|
|
|
private getUserClientKey(type: string, userId: string, clientId: string): string {
|
|
return `${type}:user:${userId}:client:${clientId}`;
|
|
}
|
|
|
|
// 授权码管理
|
|
async storeAuthorizationCode(authCode: AuthorizationCode): Promise<void> {
|
|
const key = this.getTokenKey('auth_code', authCode.code);
|
|
const ttl = Math.floor((authCode.expires_at.getTime() - Date.now()) / 1000);
|
|
|
|
const data = {
|
|
...authCode,
|
|
expires_at: authCode.expires_at.toISOString(),
|
|
created_at: authCode.created_at.toISOString(),
|
|
};
|
|
|
|
await this.storage.set(key, data, Math.max(ttl, 1));
|
|
}
|
|
|
|
async getAuthorizationCode(code: string): Promise<AuthorizationCode | null> {
|
|
const key = this.getTokenKey('auth_code', code);
|
|
const data = await this.storage.get(key);
|
|
if (!data) return null;
|
|
|
|
return {
|
|
...data,
|
|
expires_at: new Date(data.expires_at),
|
|
created_at: new Date(data.created_at),
|
|
};
|
|
}
|
|
|
|
async deleteAuthorizationCode(code: string): Promise<void> {
|
|
const key = this.getTokenKey('auth_code', code);
|
|
await this.storage.delete(key);
|
|
}
|
|
|
|
// 访问令牌管理
|
|
async storeAccessToken(token: AccessToken): Promise<void> {
|
|
const key = this.getTokenKey('access_token', token.token);
|
|
const userClientKey = this.getUserClientKey('access_tokens', token.user_id, token.client_id);
|
|
const ttl = Math.floor((token.expires_at.getTime() - Date.now()) / 1000);
|
|
|
|
const data = {
|
|
...token,
|
|
expires_at: token.expires_at.toISOString(),
|
|
created_at: token.created_at.toISOString(),
|
|
};
|
|
|
|
// 存储令牌数据
|
|
await this.storage.set(key, data, Math.max(ttl, 1));
|
|
|
|
// 存储用户-客户端索引
|
|
const existingTokens = await this.storage.get<string[]>(userClientKey) || [];
|
|
existingTokens.push(token.token);
|
|
await this.storage.set(userClientKey, existingTokens, Math.max(ttl, 1));
|
|
}
|
|
|
|
async getAccessToken(token: string): Promise<AccessToken | null> {
|
|
const key = this.getTokenKey('access_token', token);
|
|
const data = await this.storage.get(key);
|
|
if (!data) return null;
|
|
|
|
return {
|
|
...data,
|
|
expires_at: new Date(data.expires_at),
|
|
created_at: new Date(data.created_at),
|
|
};
|
|
}
|
|
|
|
async deleteAccessToken(token: string): Promise<void> {
|
|
const key = this.getTokenKey('access_token', token);
|
|
|
|
// 获取令牌数据以清理索引
|
|
const tokenData = await this.storage.get(key);
|
|
if (tokenData) {
|
|
const userClientKey = this.getUserClientKey('access_tokens', tokenData.user_id, tokenData.client_id);
|
|
const tokens = await this.storage.get<string[]>(userClientKey) || [];
|
|
const filteredTokens = tokens.filter(t => t !== token);
|
|
|
|
if (filteredTokens.length > 0) {
|
|
await this.storage.set(userClientKey, filteredTokens);
|
|
} else {
|
|
await this.storage.delete(userClientKey);
|
|
}
|
|
}
|
|
|
|
await this.storage.delete(key);
|
|
}
|
|
|
|
async deleteAccessTokensByUserAndClient(userId: string, clientId: string): Promise<void> {
|
|
const userClientKey = this.getUserClientKey('access_tokens', userId, clientId);
|
|
const tokens = await this.storage.get<string[]>(userClientKey) || [];
|
|
|
|
// 删除所有相关令牌
|
|
for (const token of tokens) {
|
|
await this.storage.delete(this.getTokenKey('access_token', token));
|
|
}
|
|
|
|
// 删除索引
|
|
await this.storage.delete(userClientKey);
|
|
}
|
|
|
|
// 刷新令牌管理
|
|
async storeRefreshToken(token: RefreshToken): Promise<void> {
|
|
const key = this.getTokenKey('refresh_token', token.token);
|
|
const userClientKey = this.getUserClientKey('refresh_tokens', token.user_id, token.client_id);
|
|
const ttl = Math.floor((token.expires_at.getTime() - Date.now()) / 1000);
|
|
|
|
const data = {
|
|
...token,
|
|
expires_at: token.expires_at.toISOString(),
|
|
created_at: token.created_at.toISOString(),
|
|
};
|
|
|
|
// 存储令牌数据
|
|
await this.storage.set(key, data, Math.max(ttl, 1));
|
|
|
|
// 存储用户-客户端索引
|
|
const existingTokens = await this.storage.get<string[]>(userClientKey) || [];
|
|
existingTokens.push(token.token);
|
|
await this.storage.set(userClientKey, existingTokens, Math.max(ttl, 1));
|
|
}
|
|
|
|
async getRefreshToken(token: string): Promise<RefreshToken | null> {
|
|
const key = this.getTokenKey('refresh_token', token);
|
|
const data = await this.storage.get(key);
|
|
if (!data) return null;
|
|
|
|
return {
|
|
...data,
|
|
expires_at: new Date(data.expires_at),
|
|
created_at: new Date(data.created_at),
|
|
};
|
|
}
|
|
|
|
async deleteRefreshToken(token: string): Promise<void> {
|
|
const key = this.getTokenKey('refresh_token', token);
|
|
|
|
// 获取令牌数据以清理索引
|
|
const tokenData = await this.storage.get(key);
|
|
if (tokenData) {
|
|
const userClientKey = this.getUserClientKey('refresh_tokens', tokenData.user_id, tokenData.client_id);
|
|
const tokens = await this.storage.get<string[]>(userClientKey) || [];
|
|
const filteredTokens = tokens.filter(t => t !== token);
|
|
|
|
if (filteredTokens.length > 0) {
|
|
await this.storage.set(userClientKey, filteredTokens);
|
|
} else {
|
|
await this.storage.delete(userClientKey);
|
|
}
|
|
}
|
|
|
|
await this.storage.delete(key);
|
|
}
|
|
|
|
// ID令牌管理
|
|
async storeIDToken(token: IDToken): Promise<void> {
|
|
const key = this.getTokenKey('id_token', token.token);
|
|
const ttl = Math.floor((token.expires_at.getTime() - Date.now()) / 1000);
|
|
|
|
const data = {
|
|
...token,
|
|
expires_at: token.expires_at.toISOString(),
|
|
created_at: token.created_at.toISOString(),
|
|
};
|
|
|
|
await this.storage.set(key, data, Math.max(ttl, 1));
|
|
}
|
|
|
|
async getIDToken(token: string): Promise<IDToken | null> {
|
|
const key = this.getTokenKey('id_token', token);
|
|
const data = await this.storage.get(key);
|
|
if (!data) return null;
|
|
|
|
return {
|
|
...data,
|
|
expires_at: new Date(data.expires_at),
|
|
created_at: new Date(data.created_at),
|
|
};
|
|
}
|
|
|
|
async deleteIDToken(token: string): Promise<void> {
|
|
const key = this.getTokenKey('id_token', token);
|
|
await this.storage.delete(key);
|
|
}
|
|
}
|