UNPKG

sessionize-auth

Version:

A flexible session management library for React, Next.js, Angular, and React Native

187 lines (168 loc) 5.45 kB
import { SSOProvider, SSOProviderConfig, SSOUser } from "../types"; /** * Base SSO Provider class */ export abstract class BaseSSOProvider { protected config: SSOProviderConfig; protected providerId: string; constructor(providerId: string, config: SSOProviderConfig) { this.providerId = providerId; this.config = config; } /** * Get the provider configuration */ getProvider(): SSOProvider { return { id: this.providerId, name: this.getProviderName(), icon: this.getProviderIcon(), color: this.getProviderColor(), scopes: this.getScopes(), authUrl: this.getAuthUrl(), tokenUrl: this.getTokenUrl(), userInfoUrl: this.getUserInfoUrl(), clientId: this.config.clientId, redirectUri: this.config.redirectUri }; } /** * Generate authorization URL */ generateAuthUrl(state: string, returnTo?: string): string { const params = new URLSearchParams({ client_id: this.config.clientId, redirect_uri: this.config.redirectUri, response_type: 'code', scope: this.getScopes().join(' '), state: state, ...this.getCustomParams() }); if (returnTo) { params.set('return_to', returnTo); } return `${this.getAuthUrl()}?${params.toString()}`; } /** * Exchange authorization code for access token */ async exchangeCodeForToken(code: string, state: string): Promise<{ accessToken: string; refreshToken?: string; expiresIn?: number }> { try { const response = await fetch(this.getTokenUrl(), { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'Accept': 'application/json' }, body: new URLSearchParams({ client_id: this.config.clientId, client_secret: this.config.clientSecret || '', code: code, grant_type: 'authorization_code', redirect_uri: this.config.redirectUri }) }); if (!response.ok) { throw new Error(`Token exchange failed: ${response.statusText}`); } const data = await response.json(); return { accessToken: data.access_token, refreshToken: data.refresh_token, expiresIn: data.expires_in }; } catch (error) { throw new SSOErrorClass({ code: 'TOKEN_EXCHANGE_FAILED', message: 'Failed to exchange authorization code for access token', provider: this.providerId, details: error }); } } /** * Get user information from provider */ async getUserInfo(accessToken: string): Promise<SSOUser> { try { const response = await fetch(this.getUserInfoUrl(), { headers: { 'Authorization': `Bearer ${accessToken}`, 'Accept': 'application/json' } }); if (!response.ok) { throw new Error(`Failed to get user info: ${response.statusText}`); } const data = await response.json(); return this.parseUserInfo(data, accessToken); } catch (error) { throw new SSOErrorClass({ code: 'USER_INFO_FAILED', message: 'Failed to get user information', provider: this.providerId, details: error }); } } /** * Refresh access token */ async refreshToken(refreshToken: string): Promise<{ accessToken: string; expiresIn?: number }> { try { const response = await fetch(this.getTokenUrl(), { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'Accept': 'application/json' }, body: new URLSearchParams({ client_id: this.config.clientId, client_secret: this.config.clientSecret || '', refresh_token: refreshToken, grant_type: 'refresh_token' }) }); if (!response.ok) { throw new Error(`Token refresh failed: ${response.statusText}`); } const data = await response.json(); return { accessToken: data.access_token, expiresIn: data.expires_in }; } catch (error) { throw new SSOErrorClass({ code: 'TOKEN_REFRESH_FAILED', message: 'Failed to refresh access token', provider: this.providerId, details: error }); } } // Abstract methods to be implemented by specific providers protected abstract getProviderName(): string; protected abstract getProviderIcon(): string; protected abstract getProviderColor(): string; protected abstract getScopes(): string[]; protected abstract getAuthUrl(): string; protected abstract getTokenUrl(): string; protected abstract getUserInfoUrl(): string; protected abstract parseUserInfo(data: any, accessToken: string): SSOUser; protected abstract getCustomParams(): Record<string, string>; } /** * SSO Error class */ export class SSOErrorClass extends Error { public code: string; public provider?: string; public details?: any; constructor(error: { code: string; message: string; provider?: string; details?: any }) { super(error.message); this.name = 'SSOError'; this.code = error.code; this.provider = error.provider; this.details = error.details; } }