UNPKG

@explorins/pers-sdk

Version:

Platform-agnostic SDK for PERS (Phygital Experience Rewards System)

137 lines (121 loc) 4.47 kB
import { AuthProvider } from './auth-provider.interface'; import { SimpleAuthConfig } from './simple-auth-config.interface'; /** * Creates a platform-agnostic AuthProvider from simple configuration * * This factory function is completely platform-agnostic and can be used * across Angular, React, Vue, Node.js, or any other JavaScript environment. * * Features: * - Token caching with refresh support * - Automatic token refresh on expiration * - Configurable token providers * - Platform-independent (no localStorage assumptions) * * @param config - Simple auth configuration * @returns AuthProvider implementation */ export function createAuthProvider(config: SimpleAuthConfig): AuthProvider { // Store current token for refresh scenarios and caching let currentToken: string | null = config.token || null; let isRefreshing = false; // Prevent concurrent refresh attempts let refreshPromise: Promise<void> | null = null; return { authType: config.authType || 'user', async getToken(): Promise<string | null> { // If currently refreshing, wait for it to complete if (isRefreshing && refreshPromise) { await refreshPromise; return currentToken; } // Use cached current token (updated after refresh) if (currentToken) { return currentToken; } // Custom token provider function (always fresh) if (config.tokenProvider) { const token = await config.tokenProvider(); currentToken = token; // Cache for future calls return token; } // No token available return null; }, async getProjectKey(): Promise<string | null> { return config.projectKey || null; }, async onTokenExpired(): Promise<void> { // Prevent concurrent refresh attempts if (isRefreshing) { if (refreshPromise) { await refreshPromise; } return; } // No refresh logic provided if (!config.onTokenExpired) { console.warn('Token expired but no refresh logic provided'); currentToken = null; // Clear expired token return; } // Start refresh process isRefreshing = true; refreshPromise = (async () => { try { // Execute refresh logic (should update token source) await config.onTokenExpired!(); // After refresh, get the new token if (config.tokenProvider) { const newToken = await config.tokenProvider(); if (newToken && newToken !== currentToken) { currentToken = newToken; // Notify about successful token refresh if (config.onTokenRefreshed) { config.onTokenRefreshed(newToken); } } else { console.warn('Token refresh completed but no new token received'); currentToken = null; } } else { // For static token configs, clear the token since we can't refresh console.warn('Token expired for static token config - clearing token'); currentToken = null; } } catch (error) { console.error('Token refresh failed:', error); currentToken = null; // Clear token on refresh failure throw error; // Re-throw to let SDK handle the error } finally { isRefreshing = false; refreshPromise = null; } })(); await refreshPromise; } }; } /** * Platform-specific localStorage token provider for browsers * This is a convenience function for browser environments */ export function createBrowserTokenProvider(tokenKey: string = 'userJwt'): () => Promise<string | null> { return async () => { if (typeof localStorage !== 'undefined') { return localStorage.getItem(tokenKey); } return null; }; } /** * Platform-specific environment variable token provider for Node.js * This is a convenience function for Node.js environments */ export function createNodeTokenProvider(envVar: string = 'JWT_TOKEN'): () => Promise<string | null> { return async () => { if (typeof process !== 'undefined' && process.env) { return process.env[envVar] || null; } return null; }; }