UNPKG

@vizzly-testing/cli

Version:

Visual review platform for UI developers and designers

117 lines (113 loc) 4.12 kB
/** * Auth Service * Wraps auth operations for use by the HTTP server * * Provides the interface expected by src/server/routers/auth.js: * - isAuthenticated() - Returns boolean, false if no tokens or API call fails * - whoami() - Throws if not authenticated or tokens invalid * - initiateDeviceFlow() - Throws on API error * - pollDeviceAuthorization(deviceCode) - Returns pending status or tokens, throws on error * - completeDeviceFlow(tokens) - Saves tokens to global config * - logout() - Clears local tokens, may warn if server revocation fails * - authenticatedRequest(endpoint, options) - Throws 'Not authenticated' if no tokens * * Error handling: * - isAuthenticated() never throws, returns false on any error * - whoami() throws if tokens are missing/invalid (caller should check isAuthenticated first) * - authenticatedRequest() throws 'Not authenticated' if no access token * - Device flow methods throw on API errors (network, server errors) */ import { createAuthClient } from '../auth/client.js'; import * as authOps from '../auth/operations.js'; import { getApiUrl } from '../utils/environment-config.js'; import { clearAuthTokens, getAuthTokens, saveAuthTokens } from '../utils/global-config.js'; /** * Create an auth service instance * @param {Object} [options] * @param {string} [options.apiUrl] - API base URL (defaults to VIZZLY_API_URL or https://app.vizzly.dev) * @param {Object} [options.httpClient] - Injectable HTTP client (for testing) * @param {Object} [options.tokenStore] - Injectable token store (for testing) * @returns {Object} Auth service */ export function createAuthService(options = {}) { let apiUrl = options.apiUrl || getApiUrl(); // Create HTTP client for API requests (uses auth client for proper auth handling) // Allow injection for testing let httpClient = options.httpClient || createAuthClient({ baseUrl: apiUrl }); // Create token store adapter for global config // Allow injection for testing let tokenStore = options.tokenStore || { getTokens: getAuthTokens, saveTokens: saveAuthTokens, clearTokens: clearAuthTokens }; return { /** * Check if user is authenticated * @returns {Promise<boolean>} */ async isAuthenticated() { return authOps.isAuthenticated(httpClient, tokenStore); }, /** * Get current user information * @returns {Promise<Object>} User and organization data */ async whoami() { return authOps.whoami(httpClient, tokenStore); }, /** * Initiate OAuth device flow * @returns {Promise<Object>} Device code info */ async initiateDeviceFlow() { return authOps.initiateDeviceFlow(httpClient); }, /** * Poll for device authorization * @param {string} deviceCode * @returns {Promise<Object>} Token data or pending status */ async pollDeviceAuthorization(deviceCode) { return authOps.pollDeviceAuthorization(httpClient, deviceCode); }, /** * Complete device flow and save tokens * @param {Object} tokens - Token data * @returns {Promise<Object>} */ async completeDeviceFlow(tokens) { return authOps.completeDeviceFlow(tokenStore, tokens); }, /** * Logout and revoke tokens * @returns {Promise<void>} */ async logout() { return authOps.logout(httpClient, tokenStore); }, /** * Refresh access token * @returns {Promise<Object>} New tokens */ async refresh() { return authOps.refresh(httpClient, tokenStore); }, /** * Make an authenticated request to the API * Used by cloud-proxy router for proxying requests * @param {string} endpoint - API endpoint * @param {Object} options - Fetch options * @returns {Promise<Object>} Response data */ async authenticatedRequest(endpoint, options = {}) { let auth = await tokenStore.getTokens(); if (!auth?.accessToken) { throw new Error('Not authenticated'); } return httpClient.authenticatedRequest(endpoint, auth.accessToken, options); } }; }