// apps/web/lib/token-manager.ts interface TokenData { access_token: string; refresh_token?: string; expires_at: number; token_type: string; } export class TokenManager { private static readonly ACCESS_TOKEN_KEY = 'oidc_access_token'; private static readonly REFRESH_TOKEN_KEY = 'oidc_refresh_token'; private static readonly EXPIRES_AT_KEY = 'oidc_expires_at'; private static readonly TOKEN_TYPE_KEY = 'oidc_token_type'; /** * 保存令牌到本地存储 */ static saveTokens(tokenResponse: { access_token: string; refresh_token?: string; expires_in: number; token_type: string; }): void { const expiresAt = Date.now() + (tokenResponse.expires_in * 1000); localStorage.setItem(this.ACCESS_TOKEN_KEY, tokenResponse.access_token); localStorage.setItem(this.EXPIRES_AT_KEY, expiresAt.toString()); localStorage.setItem(this.TOKEN_TYPE_KEY, tokenResponse.token_type); if (tokenResponse.refresh_token) { localStorage.setItem(this.REFRESH_TOKEN_KEY, tokenResponse.refresh_token); } } /** * 获取访问令牌 */ static getAccessToken(): string | null { return localStorage.getItem(this.ACCESS_TOKEN_KEY); } /** * 获取刷新令牌 */ static getRefreshToken(): string | null { return localStorage.getItem(this.REFRESH_TOKEN_KEY); } /** * 检查令牌是否过期 */ static isTokenExpired(): boolean { const expiresAt = localStorage.getItem(this.EXPIRES_AT_KEY); if (!expiresAt) return true; return Date.now() >= parseInt(expiresAt); } /** * 获取有效的访问令牌(自动刷新) */ static async getValidAccessToken(): Promise { const accessToken = this.getAccessToken(); if (!accessToken) return null; if (!this.isTokenExpired()) { return accessToken; } // 尝试刷新令牌 const refreshToken = this.getRefreshToken(); if (refreshToken) { try { const { oidcClient } = await import('./oidc-client'); const newTokens = await oidcClient.refreshToken(refreshToken); this.saveTokens(newTokens); return newTokens.access_token; } catch (error) { console.error('刷新令牌失败:', error); this.clearTokens(); return null; } } return null; } /** * 清除所有令牌 */ static clearTokens(): void { localStorage.removeItem(this.ACCESS_TOKEN_KEY); localStorage.removeItem(this.REFRESH_TOKEN_KEY); localStorage.removeItem(this.EXPIRES_AT_KEY); localStorage.removeItem(this.TOKEN_TYPE_KEY); } /** * 获取完整的令牌数据 */ static getTokenData(): TokenData | null { const accessToken = this.getAccessToken(); if (!accessToken) return null; const refreshToken = this.getRefreshToken(); const expiresAt = localStorage.getItem(this.EXPIRES_AT_KEY); const tokenType = localStorage.getItem(this.TOKEN_TYPE_KEY) || 'Bearer'; return { access_token: accessToken, refresh_token: refreshToken || undefined, expires_at: expiresAt ? parseInt(expiresAt) : 0, token_type: tokenType, }; } }