@simple-login/sdk 1.3.0 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -122,9 +122,7 @@ var SimpleLogin = class {
122
122
  );
123
123
  }
124
124
  if (!this.origin) {
125
- throw new Error(
126
- "origin is required. Pass it in config or set SIMPLELOGIN_ORIGIN env var."
127
- );
125
+ throw new Error("origin is required. Pass it in config or set SIMPLELOGIN_ORIGIN env var.");
128
126
  }
129
127
  }
130
128
  /**
@@ -293,7 +291,8 @@ var SimpleLogin = class {
293
291
  if (!response.ok) {
294
292
  throw new AuthorizationError("Failed to fetch public key");
295
293
  }
296
- const pem = await response.text();
294
+ const data = await response.json();
295
+ const pem = data["public_key"];
297
296
  const key = await (0, import_jose.importSPKI)(pem, "RS256");
298
297
  publicKeyCache.set(this.clientId, { key, fetchedAt: Date.now() });
299
298
  return key;
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/client.ts","../src/errors.ts"],"sourcesContent":["export { SimpleLogin } from './client'\nexport { AuthorizationError, SousaError, TokenError } from './errors'\nexport type {\n AccessTokenClaims,\n AuthResult,\n AuthorizationUrlOptions,\n AuthorizationUrlResult,\n CallbackResult,\n SimpleLoginConfig,\n TokenResponse,\n UserInfo,\n} from './types'\n","import { decodeJwt, importSPKI, jwtVerify } from 'jose'\nimport { AuthorizationError, TokenError } from './errors'\nimport type {\n AccessTokenClaims,\n AuthResult,\n AuthorizationUrlOptions,\n AuthorizationUrlResult,\n CallbackResult,\n SimpleLoginConfig,\n TokenResponse,\n UserInfo,\n} from './types'\n\ndeclare const __SIMPLELOGIN_BASE_URL__: string\n\nconst BASE_URL = __SIMPLELOGIN_BASE_URL__\n\nfunction generateState(): string {\n const array = new Uint8Array(32)\n crypto.getRandomValues(array)\n return Array.from(array, (b) => b.toString(16).padStart(2, '0')).join('')\n}\n\nfunction generateCodeVerifier(): string {\n const array = new Uint8Array(32)\n crypto.getRandomValues(array)\n // Base64url encode (RFC 7636)\n return btoa(String.fromCharCode(...array))\n .replace(/\\+/g, '-')\n .replace(/\\//g, '_')\n .replace(/=/g, '')\n}\n\nasync function generateCodeChallenge(verifier: string): Promise<string> {\n const encoder = new TextEncoder()\n const data = encoder.encode(verifier)\n const hash = await crypto.subtle.digest('SHA-256', data)\n // Base64url encode the hash\n return btoa(String.fromCharCode(...new Uint8Array(hash)))\n .replace(/\\+/g, '-')\n .replace(/\\//g, '_')\n .replace(/=/g, '')\n}\n\nfunction getEnv(key: string): string | undefined {\n if (typeof process !== 'undefined' && process.env) {\n return process.env[key]\n }\n return undefined\n}\n\nfunction parseCookie(cookieHeader: string, name: string): string | undefined {\n const match = cookieHeader.match(new RegExp(`(?:^|;\\\\s*)${name}=([^;]*)`))\n return match ? decodeURIComponent(match[1]) : undefined\n}\n\n// Public key cache: clientId -> { key, fetchedAt }\nconst publicKeyCache = new Map<string, { key: CryptoKey; fetchedAt: number }>()\nconst PUBLIC_KEY_CACHE_TTL = 60 * 60 * 1000 // 1 hour\n\nfunction createTokenCookies(tokens: TokenResponse): string[] {\n return [\n `SIMPLELOGIN_ACCESS_TOKEN=${tokens.access_token}; HttpOnly; Secure; SameSite=Lax; Path=/`,\n `SIMPLELOGIN_REFRESH_TOKEN=${tokens.refresh_token}; HttpOnly; Secure; SameSite=Lax; Path=/`,\n ]\n}\n\nfunction clearTokenCookies(): string[] {\n return [\n 'SIMPLELOGIN_ACCESS_TOKEN=; HttpOnly; Secure; SameSite=Lax; Path=/; Max-Age=0',\n 'SIMPLELOGIN_REFRESH_TOKEN=; HttpOnly; Secure; SameSite=Lax; Path=/; Max-Age=0',\n ]\n}\n\nexport class SimpleLogin {\n private clientId: string\n private clientSecret: string\n private redirectUri: string\n private origin: string\n private baseUrl: string\n\n constructor(config: SimpleLoginConfig = {}) {\n this.clientId = config.clientId ?? getEnv('SIMPLELOGIN_CLIENT_ID') ?? ''\n this.clientSecret = config.clientSecret ?? getEnv('SIMPLELOGIN_CLIENT_SECRET') ?? ''\n this.redirectUri = config.redirectUri ?? getEnv('SIMPLELOGIN_REDIRECT_URI') ?? ''\n this.origin = config.origin ?? getEnv('SIMPLELOGIN_ORIGIN') ?? ''\n this.baseUrl = BASE_URL\n\n if (!this.clientId) {\n throw new Error(\n 'clientId is required. Pass it in config or set SIMPLELOGIN_CLIENT_ID env var.',\n )\n }\n if (!this.clientSecret) {\n throw new Error(\n 'clientSecret is required. Pass it in config or set SIMPLELOGIN_CLIENT_SECRET env var.',\n )\n }\n if (!this.redirectUri) {\n throw new Error(\n 'redirectUri is required. Pass it in config or set SIMPLELOGIN_REDIRECT_URI env var.',\n )\n }\n if (!this.origin) {\n throw new Error(\n 'origin is required. Pass it in config or set SIMPLELOGIN_ORIGIN env var.',\n )\n }\n }\n\n /**\n * Returns a Response that redirects to the authorization URL with state and PKCE cookies set.\n * @param options - Optional scopes or custom state\n * @returns A 302 redirect Response ready to be returned from your route handler\n */\n async redirectToAuth(options: AuthorizationUrlOptions = {}): Promise<Response> {\n const { url, cookies } = await this.getAuthorizationUrl(options)\n const headers = new Headers()\n headers.set('Location', url)\n for (const cookie of cookies) {\n headers.append('Set-Cookie', cookie)\n }\n return new Response(null, { status: 302, headers })\n }\n\n /**\n * Generate the authorization URL to redirect users for sign-in\n * @returns The authorization URL, state, code verifier, and ready-to-use Set-Cookie header values\n */\n async getAuthorizationUrl(options: AuthorizationUrlOptions = {}): Promise<AuthorizationUrlResult> {\n const state = options.state ?? generateState()\n const codeVerifier = generateCodeVerifier()\n const codeChallenge = await generateCodeChallenge(codeVerifier)\n\n const params = new URLSearchParams({\n client_id: this.clientId,\n redirect_uri: this.redirectUri,\n response_type: 'code',\n state,\n code_challenge: codeChallenge,\n code_challenge_method: 'S256',\n })\n\n if (options.scopes?.length) {\n params.set('scope', options.scopes.join(' '))\n }\n\n const cookies = [\n `SIMPLELOGIN_STATE=${state}; HttpOnly; Secure; SameSite=Lax; Max-Age=600; Path=/`,\n `SIMPLELOGIN_CODE_VERIFIER=${codeVerifier}; HttpOnly; Secure; SameSite=Lax; Max-Age=600; Path=/`,\n ]\n\n return {\n url: `${this.baseUrl}/v1/auth/authorize?${params.toString()}`,\n state,\n codeVerifier,\n cookies,\n }\n }\n\n /**\n * Handle the OAuth callback: verify state, exchange code, fetch user info, and return a redirect Response.\n * @param request - The incoming Request object from the callback\n * @param redirectTo - URL to redirect to after successful authentication (default: '/')\n * @returns CallbackResult with redirect Response (cookies set) and user info to store\n * @throws AuthorizationError if state is missing or doesn't match\n */\n async handleCallback(request: Request, redirectTo: string = '/'): Promise<CallbackResult> {\n const url = new URL(request.url)\n const code = url.searchParams.get('code')\n const state = url.searchParams.get('state')\n\n if (!code) {\n throw new AuthorizationError('Missing authorization code in callback')\n }\n\n if (!state) {\n throw new AuthorizationError('Missing state parameter in callback')\n }\n\n const cookieHeader = request.headers.get('cookie') ?? ''\n const expectedState = parseCookie(cookieHeader, 'SIMPLELOGIN_STATE')\n const codeVerifier = parseCookie(cookieHeader, 'SIMPLELOGIN_CODE_VERIFIER')\n\n if (!expectedState) {\n throw new AuthorizationError('Missing SIMPLELOGIN_STATE cookie')\n }\n\n if (state !== expectedState) {\n throw new AuthorizationError('Invalid state parameter')\n }\n\n if (!codeVerifier) {\n throw new AuthorizationError('Missing SIMPLELOGIN_CODE_VERIFIER cookie')\n }\n\n // Exchange code for tokens (with PKCE code_verifier)\n const tokens = await this.exchangeCode(code, codeVerifier)\n\n // Fetch user info (only time we do this)\n const user = await this.getUserInfo(tokens.access_token)\n\n // Build redirect response with token cookies\n const cookies = createTokenCookies(tokens)\n const headers = new Headers()\n headers.set('Location', redirectTo)\n for (const cookie of cookies) {\n headers.append('Set-Cookie', cookie)\n }\n // Clear the state and code verifier cookies\n headers.append(\n 'Set-Cookie',\n 'SIMPLELOGIN_STATE=; HttpOnly; Secure; SameSite=Lax; Path=/; Max-Age=0',\n )\n headers.append(\n 'Set-Cookie',\n 'SIMPLELOGIN_CODE_VERIFIER=; HttpOnly; Secure; SameSite=Lax; Path=/; Max-Age=0',\n )\n\n const response = new Response(null, { status: 302, headers })\n\n return { response, user }\n }\n\n /**\n * Exchange an authorization code for access and refresh tokens\n * @param code - The authorization code from the callback\n * @param codeVerifier - The PKCE code verifier\n */\n async exchangeCode(code: string, codeVerifier: string): Promise<TokenResponse> {\n const response = await fetch(`${this.baseUrl}/v1/auth/token`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n },\n body: new URLSearchParams({\n grant_type: 'authorization_code',\n client_id: this.clientId,\n client_secret: this.clientSecret,\n redirect_uri: this.redirectUri,\n code,\n code_verifier: codeVerifier,\n }),\n })\n\n if (!response.ok) {\n const error = await response.json().catch(() => ({}))\n throw new TokenError(error.error_description || 'Failed to exchange authorization code')\n }\n\n return response.json()\n }\n\n /**\n * Refresh an access token using a refresh token\n */\n async refreshToken(refreshToken: string): Promise<TokenResponse> {\n const response = await fetch(`${this.baseUrl}/v1/auth/token`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n },\n body: new URLSearchParams({\n grant_type: 'refresh_token',\n client_id: this.clientId,\n client_secret: this.clientSecret,\n refresh_token: refreshToken,\n }),\n })\n\n if (!response.ok) {\n const error = await response.json().catch(() => ({}))\n throw new TokenError(error.error_description || 'Failed to refresh token')\n }\n\n return response.json()\n }\n\n /**\n * Get user information using an access token\n */\n async getUserInfo(accessToken: string): Promise<UserInfo> {\n const response = await fetch(`${this.baseUrl}/v1/auth/userinfo`, {\n headers: {\n Authorization: `Bearer ${accessToken}`,\n },\n })\n\n if (!response.ok) {\n throw new AuthorizationError('Failed to fetch user info')\n }\n\n return response.json()\n }\n\n /**\n * Fetch and cache the public key for JWT verification\n */\n private async getPublicKey(): Promise<CryptoKey> {\n const cached = publicKeyCache.get(this.clientId)\n if (cached && Date.now() - cached.fetchedAt < PUBLIC_KEY_CACHE_TTL) {\n return cached.key\n }\n\n const response = await fetch(`${this.baseUrl}/api/applications/${this.clientId}/public-key`)\n if (!response.ok) {\n throw new AuthorizationError('Failed to fetch public key')\n }\n\n const pem = await response.text()\n const key = await importSPKI(pem, 'RS256')\n\n publicKeyCache.set(this.clientId, { key, fetchedAt: Date.now() })\n return key\n }\n\n /**\n * Authenticate a request by verifying the access token from cookies.\n * No network calls except when refreshing expired tokens.\n * CSRF protection is automatic using the origin from config.\n * @param request - The incoming Request object\n * @returns AuthResult if authenticated, null if not authenticated\n */\n async authenticate(request: Request): Promise<AuthResult | null> {\n // CSRF protection for state-changing requests\n const method = request.method.toUpperCase()\n if (method !== 'GET' && method !== 'HEAD') {\n const requestOrigin = request.headers.get('origin') || request.headers.get('referer')\n if (!requestOrigin?.startsWith(this.origin)) {\n return null\n }\n }\n\n const cookieHeader = request.headers.get('cookie') ?? ''\n const accessToken = parseCookie(cookieHeader, 'SIMPLELOGIN_ACCESS_TOKEN')\n const refreshTokenValue = parseCookie(cookieHeader, 'SIMPLELOGIN_REFRESH_TOKEN')\n\n if (!accessToken) {\n return null\n }\n\n const publicKey = await this.getPublicKey()\n\n try {\n // Verify the token (local, no network call)\n await jwtVerify(accessToken, publicKey)\n\n // Decode claims from JWT (no network call)\n const claims = decodeJwt(accessToken) as AccessTokenClaims\n\n return { claims, accessToken, headers: new Headers() }\n } catch (error) {\n // Check if token is expired\n const isExpired =\n error instanceof Error &&\n 'code' in error &&\n (error as { code: string }).code === 'ERR_JWT_EXPIRED'\n\n if (!isExpired || !refreshTokenValue) {\n return null\n }\n\n // Token expired, try to refresh\n try {\n const tokens = await this.refreshToken(refreshTokenValue)\n const claims = decodeJwt(tokens.access_token) as AccessTokenClaims\n\n // Build headers with new cookies\n const headers = new Headers()\n for (const cookie of createTokenCookies(tokens)) {\n headers.append('Set-Cookie', cookie)\n }\n\n return { claims, accessToken: tokens.access_token, headers }\n } catch {\n return null\n }\n }\n }\n\n /**\n * Revoke a refresh token on the IdP\n */\n private async revokeToken(refreshToken: string): Promise<void> {\n await fetch(`${this.baseUrl}/v1/auth/revoke`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n body: new URLSearchParams({\n token: refreshToken,\n client_id: this.clientId,\n client_secret: this.clientSecret,\n }),\n })\n }\n\n /**\n * Logout: revoke refresh token on IdP, clear cookies, and redirect to auth.\n * @param request - The incoming Request object (to read refresh token from cookies)\n */\n async logout(request: Request): Promise<Response> {\n const cookieHeader = request.headers.get('cookie') ?? ''\n const refreshToken = parseCookie(cookieHeader, 'SIMPLELOGIN_REFRESH_TOKEN')\n\n // Revoke token on IdP (fire and forget - don't block logout on failure)\n if (refreshToken) {\n this.revokeToken(refreshToken).catch(() => {})\n }\n\n // Get auth redirect response\n const authResponse = await this.redirectToAuth()\n\n // Merge clear cookies with auth cookies\n const headers = new Headers(authResponse.headers)\n for (const cookie of clearTokenCookies()) {\n headers.append('Set-Cookie', cookie)\n }\n\n return new Response(null, { status: 302, headers })\n }\n}\n","export class SousaError extends Error {\n constructor(\n message: string,\n public code: string,\n public statusCode?: number\n ) {\n super(message);\n this.name = \"SousaError\";\n }\n}\n\nexport class AuthorizationError extends SousaError {\n constructor(message: string) {\n super(message, \"AUTHORIZATION_ERROR\", 401);\n this.name = \"AuthorizationError\";\n }\n}\n\nexport class TokenError extends SousaError {\n constructor(message: string) {\n super(message, \"TOKEN_ERROR\", 400);\n this.name = \"TokenError\";\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/index.ts","../src/client.ts","../src/errors.ts"],"sourcesContent":["export { SimpleLogin } from './client'\nexport { AuthorizationError, SousaError, TokenError } from './errors'\nexport type {\n AccessTokenClaims,\n AuthResult,\n AuthorizationUrlOptions,\n AuthorizationUrlResult,\n CallbackResult,\n SimpleLoginConfig,\n TokenResponse,\n UserInfo,\n} from './types'\n","import { decodeJwt, importSPKI, jwtVerify } from 'jose'\nimport { AuthorizationError, TokenError } from './errors'\nimport type {\n AccessTokenClaims,\n AuthResult,\n AuthorizationUrlOptions,\n AuthorizationUrlResult,\n CallbackResult,\n SimpleLoginConfig,\n TokenResponse,\n UserInfo,\n} from './types'\n\ndeclare const __SIMPLELOGIN_BASE_URL__: string\n\nconst BASE_URL = __SIMPLELOGIN_BASE_URL__\n\nfunction generateState(): string {\n const array = new Uint8Array(32)\n crypto.getRandomValues(array)\n return Array.from(array, (b) => b.toString(16).padStart(2, '0')).join('')\n}\n\nfunction generateCodeVerifier(): string {\n const array = new Uint8Array(32)\n crypto.getRandomValues(array)\n // Base64url encode (RFC 7636)\n return btoa(String.fromCharCode(...array))\n .replace(/\\+/g, '-')\n .replace(/\\//g, '_')\n .replace(/=/g, '')\n}\n\nasync function generateCodeChallenge(verifier: string): Promise<string> {\n const encoder = new TextEncoder()\n const data = encoder.encode(verifier)\n const hash = await crypto.subtle.digest('SHA-256', data)\n // Base64url encode the hash\n return btoa(String.fromCharCode(...new Uint8Array(hash)))\n .replace(/\\+/g, '-')\n .replace(/\\//g, '_')\n .replace(/=/g, '')\n}\n\nfunction getEnv(key: string): string | undefined {\n if (typeof process !== 'undefined' && process.env) {\n return process.env[key]\n }\n return undefined\n}\n\nfunction parseCookie(cookieHeader: string, name: string): string | undefined {\n const match = cookieHeader.match(new RegExp(`(?:^|;\\\\s*)${name}=([^;]*)`))\n return match ? decodeURIComponent(match[1]) : undefined\n}\n\n// Public key cache: clientId -> { key, fetchedAt }\nconst publicKeyCache = new Map<string, { key: CryptoKey; fetchedAt: number }>()\nconst PUBLIC_KEY_CACHE_TTL = 60 * 60 * 1000 // 1 hour\n\nfunction createTokenCookies(tokens: TokenResponse): string[] {\n return [\n `SIMPLELOGIN_ACCESS_TOKEN=${tokens.access_token}; HttpOnly; Secure; SameSite=Lax; Path=/`,\n `SIMPLELOGIN_REFRESH_TOKEN=${tokens.refresh_token}; HttpOnly; Secure; SameSite=Lax; Path=/`,\n ]\n}\n\nfunction clearTokenCookies(): string[] {\n return [\n 'SIMPLELOGIN_ACCESS_TOKEN=; HttpOnly; Secure; SameSite=Lax; Path=/; Max-Age=0',\n 'SIMPLELOGIN_REFRESH_TOKEN=; HttpOnly; Secure; SameSite=Lax; Path=/; Max-Age=0',\n ]\n}\n\nexport class SimpleLogin {\n private clientId: string\n private clientSecret: string\n private redirectUri: string\n private origin: string\n private baseUrl: string\n\n constructor(config: SimpleLoginConfig = {}) {\n this.clientId = config.clientId ?? getEnv('SIMPLELOGIN_CLIENT_ID') ?? ''\n this.clientSecret = config.clientSecret ?? getEnv('SIMPLELOGIN_CLIENT_SECRET') ?? ''\n this.redirectUri = config.redirectUri ?? getEnv('SIMPLELOGIN_REDIRECT_URI') ?? ''\n this.origin = config.origin ?? getEnv('SIMPLELOGIN_ORIGIN') ?? ''\n this.baseUrl = BASE_URL\n\n if (!this.clientId) {\n throw new Error(\n 'clientId is required. Pass it in config or set SIMPLELOGIN_CLIENT_ID env var.',\n )\n }\n if (!this.clientSecret) {\n throw new Error(\n 'clientSecret is required. Pass it in config or set SIMPLELOGIN_CLIENT_SECRET env var.',\n )\n }\n if (!this.redirectUri) {\n throw new Error(\n 'redirectUri is required. Pass it in config or set SIMPLELOGIN_REDIRECT_URI env var.',\n )\n }\n if (!this.origin) {\n throw new Error('origin is required. Pass it in config or set SIMPLELOGIN_ORIGIN env var.')\n }\n }\n\n /**\n * Returns a Response that redirects to the authorization URL with state and PKCE cookies set.\n * @param options - Optional scopes or custom state\n * @returns A 302 redirect Response ready to be returned from your route handler\n */\n async redirectToAuth(options: AuthorizationUrlOptions = {}): Promise<Response> {\n const { url, cookies } = await this.getAuthorizationUrl(options)\n const headers = new Headers()\n headers.set('Location', url)\n for (const cookie of cookies) {\n headers.append('Set-Cookie', cookie)\n }\n return new Response(null, { status: 302, headers })\n }\n\n /**\n * Generate the authorization URL to redirect users for sign-in\n * @returns The authorization URL, state, code verifier, and ready-to-use Set-Cookie header values\n */\n async getAuthorizationUrl(\n options: AuthorizationUrlOptions = {},\n ): Promise<AuthorizationUrlResult> {\n const state = options.state ?? generateState()\n const codeVerifier = generateCodeVerifier()\n const codeChallenge = await generateCodeChallenge(codeVerifier)\n\n const params = new URLSearchParams({\n client_id: this.clientId,\n redirect_uri: this.redirectUri,\n response_type: 'code',\n state,\n code_challenge: codeChallenge,\n code_challenge_method: 'S256',\n })\n\n if (options.scopes?.length) {\n params.set('scope', options.scopes.join(' '))\n }\n\n const cookies = [\n `SIMPLELOGIN_STATE=${state}; HttpOnly; Secure; SameSite=Lax; Max-Age=600; Path=/`,\n `SIMPLELOGIN_CODE_VERIFIER=${codeVerifier}; HttpOnly; Secure; SameSite=Lax; Max-Age=600; Path=/`,\n ]\n\n return {\n url: `${this.baseUrl}/v1/auth/authorize?${params.toString()}`,\n state,\n codeVerifier,\n cookies,\n }\n }\n\n /**\n * Handle the OAuth callback: verify state, exchange code, fetch user info, and return a redirect Response.\n * @param request - The incoming Request object from the callback\n * @param redirectTo - URL to redirect to after successful authentication (default: '/')\n * @returns CallbackResult with redirect Response (cookies set) and user info to store\n * @throws AuthorizationError if state is missing or doesn't match\n */\n async handleCallback(request: Request, redirectTo: string = '/'): Promise<CallbackResult> {\n const url = new URL(request.url)\n const code = url.searchParams.get('code')\n const state = url.searchParams.get('state')\n\n if (!code) {\n throw new AuthorizationError('Missing authorization code in callback')\n }\n\n if (!state) {\n throw new AuthorizationError('Missing state parameter in callback')\n }\n\n const cookieHeader = request.headers.get('cookie') ?? ''\n const expectedState = parseCookie(cookieHeader, 'SIMPLELOGIN_STATE')\n const codeVerifier = parseCookie(cookieHeader, 'SIMPLELOGIN_CODE_VERIFIER')\n\n if (!expectedState) {\n throw new AuthorizationError('Missing SIMPLELOGIN_STATE cookie')\n }\n\n if (state !== expectedState) {\n throw new AuthorizationError('Invalid state parameter')\n }\n\n if (!codeVerifier) {\n throw new AuthorizationError('Missing SIMPLELOGIN_CODE_VERIFIER cookie')\n }\n\n // Exchange code for tokens (with PKCE code_verifier)\n const tokens = await this.exchangeCode(code, codeVerifier)\n\n // Fetch user info (only time we do this)\n const user = await this.getUserInfo(tokens.access_token)\n\n // Build redirect response with token cookies\n const cookies = createTokenCookies(tokens)\n const headers = new Headers()\n headers.set('Location', redirectTo)\n for (const cookie of cookies) {\n headers.append('Set-Cookie', cookie)\n }\n // Clear the state and code verifier cookies\n headers.append(\n 'Set-Cookie',\n 'SIMPLELOGIN_STATE=; HttpOnly; Secure; SameSite=Lax; Path=/; Max-Age=0',\n )\n headers.append(\n 'Set-Cookie',\n 'SIMPLELOGIN_CODE_VERIFIER=; HttpOnly; Secure; SameSite=Lax; Path=/; Max-Age=0',\n )\n\n const response = new Response(null, { status: 302, headers })\n\n return { response, user }\n }\n\n /**\n * Exchange an authorization code for access and refresh tokens\n * @param code - The authorization code from the callback\n * @param codeVerifier - The PKCE code verifier\n */\n async exchangeCode(code: string, codeVerifier: string): Promise<TokenResponse> {\n const response = await fetch(`${this.baseUrl}/v1/auth/token`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n },\n body: new URLSearchParams({\n grant_type: 'authorization_code',\n client_id: this.clientId,\n client_secret: this.clientSecret,\n redirect_uri: this.redirectUri,\n code,\n code_verifier: codeVerifier,\n }),\n })\n\n if (!response.ok) {\n const error = await response.json().catch(() => ({}))\n throw new TokenError(error.error_description || 'Failed to exchange authorization code')\n }\n\n return response.json()\n }\n\n /**\n * Refresh an access token using a refresh token\n */\n async refreshToken(refreshToken: string): Promise<TokenResponse> {\n const response = await fetch(`${this.baseUrl}/v1/auth/token`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n },\n body: new URLSearchParams({\n grant_type: 'refresh_token',\n client_id: this.clientId,\n client_secret: this.clientSecret,\n refresh_token: refreshToken,\n }),\n })\n\n if (!response.ok) {\n const error = await response.json().catch(() => ({}))\n throw new TokenError(error.error_description || 'Failed to refresh token')\n }\n\n return response.json()\n }\n\n /**\n * Get user information using an access token\n */\n async getUserInfo(accessToken: string): Promise<UserInfo> {\n const response = await fetch(`${this.baseUrl}/v1/auth/userinfo`, {\n headers: {\n Authorization: `Bearer ${accessToken}`,\n },\n })\n\n if (!response.ok) {\n throw new AuthorizationError('Failed to fetch user info')\n }\n\n return response.json()\n }\n\n /**\n * Fetch and cache the public key for JWT verification\n */\n private async getPublicKey(): Promise<CryptoKey> {\n const cached = publicKeyCache.get(this.clientId)\n if (cached && Date.now() - cached.fetchedAt < PUBLIC_KEY_CACHE_TTL) {\n return cached.key\n }\n\n const response = await fetch(`${this.baseUrl}/api/applications/${this.clientId}/public-key`)\n if (!response.ok) {\n throw new AuthorizationError('Failed to fetch public key')\n }\n\n const data = await response.json()\n const pem = data['public_key']\n const key = await importSPKI(pem, 'RS256')\n\n publicKeyCache.set(this.clientId, { key, fetchedAt: Date.now() })\n return key\n }\n\n /**\n * Authenticate a request by verifying the access token from cookies.\n * No network calls except when refreshing expired tokens.\n * CSRF protection is automatic using the origin from config.\n * @param request - The incoming Request object\n * @returns AuthResult if authenticated, null if not authenticated\n */\n async authenticate(request: Request): Promise<AuthResult | null> {\n // CSRF protection for state-changing requests\n const method = request.method.toUpperCase()\n if (method !== 'GET' && method !== 'HEAD') {\n const requestOrigin = request.headers.get('origin') || request.headers.get('referer')\n if (!requestOrigin?.startsWith(this.origin)) {\n return null\n }\n }\n\n const cookieHeader = request.headers.get('cookie') ?? ''\n const accessToken = parseCookie(cookieHeader, 'SIMPLELOGIN_ACCESS_TOKEN')\n const refreshTokenValue = parseCookie(cookieHeader, 'SIMPLELOGIN_REFRESH_TOKEN')\n\n if (!accessToken) {\n return null\n }\n\n const publicKey = await this.getPublicKey()\n\n try {\n // Verify the token (local, no network call)\n await jwtVerify(accessToken, publicKey)\n\n // Decode claims from JWT (no network call)\n const claims = decodeJwt(accessToken) as AccessTokenClaims\n\n return { claims, accessToken, headers: new Headers() }\n } catch (error) {\n // Check if token is expired\n const isExpired =\n error instanceof Error &&\n 'code' in error &&\n (error as { code: string }).code === 'ERR_JWT_EXPIRED'\n\n if (!isExpired || !refreshTokenValue) {\n return null\n }\n\n // Token expired, try to refresh\n try {\n const tokens = await this.refreshToken(refreshTokenValue)\n const claims = decodeJwt(tokens.access_token) as AccessTokenClaims\n\n // Build headers with new cookies\n const headers = new Headers()\n for (const cookie of createTokenCookies(tokens)) {\n headers.append('Set-Cookie', cookie)\n }\n\n return { claims, accessToken: tokens.access_token, headers }\n } catch {\n return null\n }\n }\n }\n\n /**\n * Revoke a refresh token on the IdP\n */\n private async revokeToken(refreshToken: string): Promise<void> {\n await fetch(`${this.baseUrl}/v1/auth/revoke`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n body: new URLSearchParams({\n token: refreshToken,\n client_id: this.clientId,\n client_secret: this.clientSecret,\n }),\n })\n }\n\n /**\n * Logout: revoke refresh token on IdP, clear cookies, and redirect to auth.\n * @param request - The incoming Request object (to read refresh token from cookies)\n */\n async logout(request: Request): Promise<Response> {\n const cookieHeader = request.headers.get('cookie') ?? ''\n const refreshToken = parseCookie(cookieHeader, 'SIMPLELOGIN_REFRESH_TOKEN')\n\n // Revoke token on IdP (fire and forget - don't block logout on failure)\n if (refreshToken) {\n this.revokeToken(refreshToken).catch(() => {})\n }\n\n // Get auth redirect response\n const authResponse = await this.redirectToAuth()\n\n // Merge clear cookies with auth cookies\n const headers = new Headers(authResponse.headers)\n for (const cookie of clearTokenCookies()) {\n headers.append('Set-Cookie', cookie)\n }\n\n return new Response(null, { status: 302, headers })\n }\n}\n","export class SousaError extends Error {\n constructor(\n message: string,\n public code: string,\n public statusCode?: number\n ) {\n super(message);\n this.name = \"SousaError\";\n }\n}\n\nexport class AuthorizationError extends SousaError {\n constructor(message: string) {\n super(message, \"AUTHORIZATION_ERROR\", 401);\n this.name = \"AuthorizationError\";\n }\n}\n\nexport class TokenError extends SousaError {\n constructor(message: string) {\n super(message, \"TOKEN_ERROR\", 400);\n this.name = \"TokenError\";\n }\n}\n"],"mappings":"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","names":[]}
package/dist/index.js CHANGED
@@ -93,9 +93,7 @@ var SimpleLogin = class {
93
93
  );
94
94
  }
95
95
  if (!this.origin) {
96
- throw new Error(
97
- "origin is required. Pass it in config or set SIMPLELOGIN_ORIGIN env var."
98
- );
96
+ throw new Error("origin is required. Pass it in config or set SIMPLELOGIN_ORIGIN env var.");
99
97
  }
100
98
  }
101
99
  /**
@@ -264,7 +262,8 @@ var SimpleLogin = class {
264
262
  if (!response.ok) {
265
263
  throw new AuthorizationError("Failed to fetch public key");
266
264
  }
267
- const pem = await response.text();
265
+ const data = await response.json();
266
+ const pem = data["public_key"];
268
267
  const key = await importSPKI(pem, "RS256");
269
268
  publicKeyCache.set(this.clientId, { key, fetchedAt: Date.now() });
270
269
  return key;
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/client.ts","../src/errors.ts"],"sourcesContent":["import { decodeJwt, importSPKI, jwtVerify } from 'jose'\nimport { AuthorizationError, TokenError } from './errors'\nimport type {\n AccessTokenClaims,\n AuthResult,\n AuthorizationUrlOptions,\n AuthorizationUrlResult,\n CallbackResult,\n SimpleLoginConfig,\n TokenResponse,\n UserInfo,\n} from './types'\n\ndeclare const __SIMPLELOGIN_BASE_URL__: string\n\nconst BASE_URL = __SIMPLELOGIN_BASE_URL__\n\nfunction generateState(): string {\n const array = new Uint8Array(32)\n crypto.getRandomValues(array)\n return Array.from(array, (b) => b.toString(16).padStart(2, '0')).join('')\n}\n\nfunction generateCodeVerifier(): string {\n const array = new Uint8Array(32)\n crypto.getRandomValues(array)\n // Base64url encode (RFC 7636)\n return btoa(String.fromCharCode(...array))\n .replace(/\\+/g, '-')\n .replace(/\\//g, '_')\n .replace(/=/g, '')\n}\n\nasync function generateCodeChallenge(verifier: string): Promise<string> {\n const encoder = new TextEncoder()\n const data = encoder.encode(verifier)\n const hash = await crypto.subtle.digest('SHA-256', data)\n // Base64url encode the hash\n return btoa(String.fromCharCode(...new Uint8Array(hash)))\n .replace(/\\+/g, '-')\n .replace(/\\//g, '_')\n .replace(/=/g, '')\n}\n\nfunction getEnv(key: string): string | undefined {\n if (typeof process !== 'undefined' && process.env) {\n return process.env[key]\n }\n return undefined\n}\n\nfunction parseCookie(cookieHeader: string, name: string): string | undefined {\n const match = cookieHeader.match(new RegExp(`(?:^|;\\\\s*)${name}=([^;]*)`))\n return match ? decodeURIComponent(match[1]) : undefined\n}\n\n// Public key cache: clientId -> { key, fetchedAt }\nconst publicKeyCache = new Map<string, { key: CryptoKey; fetchedAt: number }>()\nconst PUBLIC_KEY_CACHE_TTL = 60 * 60 * 1000 // 1 hour\n\nfunction createTokenCookies(tokens: TokenResponse): string[] {\n return [\n `SIMPLELOGIN_ACCESS_TOKEN=${tokens.access_token}; HttpOnly; Secure; SameSite=Lax; Path=/`,\n `SIMPLELOGIN_REFRESH_TOKEN=${tokens.refresh_token}; HttpOnly; Secure; SameSite=Lax; Path=/`,\n ]\n}\n\nfunction clearTokenCookies(): string[] {\n return [\n 'SIMPLELOGIN_ACCESS_TOKEN=; HttpOnly; Secure; SameSite=Lax; Path=/; Max-Age=0',\n 'SIMPLELOGIN_REFRESH_TOKEN=; HttpOnly; Secure; SameSite=Lax; Path=/; Max-Age=0',\n ]\n}\n\nexport class SimpleLogin {\n private clientId: string\n private clientSecret: string\n private redirectUri: string\n private origin: string\n private baseUrl: string\n\n constructor(config: SimpleLoginConfig = {}) {\n this.clientId = config.clientId ?? getEnv('SIMPLELOGIN_CLIENT_ID') ?? ''\n this.clientSecret = config.clientSecret ?? getEnv('SIMPLELOGIN_CLIENT_SECRET') ?? ''\n this.redirectUri = config.redirectUri ?? getEnv('SIMPLELOGIN_REDIRECT_URI') ?? ''\n this.origin = config.origin ?? getEnv('SIMPLELOGIN_ORIGIN') ?? ''\n this.baseUrl = BASE_URL\n\n if (!this.clientId) {\n throw new Error(\n 'clientId is required. Pass it in config or set SIMPLELOGIN_CLIENT_ID env var.',\n )\n }\n if (!this.clientSecret) {\n throw new Error(\n 'clientSecret is required. Pass it in config or set SIMPLELOGIN_CLIENT_SECRET env var.',\n )\n }\n if (!this.redirectUri) {\n throw new Error(\n 'redirectUri is required. Pass it in config or set SIMPLELOGIN_REDIRECT_URI env var.',\n )\n }\n if (!this.origin) {\n throw new Error(\n 'origin is required. Pass it in config or set SIMPLELOGIN_ORIGIN env var.',\n )\n }\n }\n\n /**\n * Returns a Response that redirects to the authorization URL with state and PKCE cookies set.\n * @param options - Optional scopes or custom state\n * @returns A 302 redirect Response ready to be returned from your route handler\n */\n async redirectToAuth(options: AuthorizationUrlOptions = {}): Promise<Response> {\n const { url, cookies } = await this.getAuthorizationUrl(options)\n const headers = new Headers()\n headers.set('Location', url)\n for (const cookie of cookies) {\n headers.append('Set-Cookie', cookie)\n }\n return new Response(null, { status: 302, headers })\n }\n\n /**\n * Generate the authorization URL to redirect users for sign-in\n * @returns The authorization URL, state, code verifier, and ready-to-use Set-Cookie header values\n */\n async getAuthorizationUrl(options: AuthorizationUrlOptions = {}): Promise<AuthorizationUrlResult> {\n const state = options.state ?? generateState()\n const codeVerifier = generateCodeVerifier()\n const codeChallenge = await generateCodeChallenge(codeVerifier)\n\n const params = new URLSearchParams({\n client_id: this.clientId,\n redirect_uri: this.redirectUri,\n response_type: 'code',\n state,\n code_challenge: codeChallenge,\n code_challenge_method: 'S256',\n })\n\n if (options.scopes?.length) {\n params.set('scope', options.scopes.join(' '))\n }\n\n const cookies = [\n `SIMPLELOGIN_STATE=${state}; HttpOnly; Secure; SameSite=Lax; Max-Age=600; Path=/`,\n `SIMPLELOGIN_CODE_VERIFIER=${codeVerifier}; HttpOnly; Secure; SameSite=Lax; Max-Age=600; Path=/`,\n ]\n\n return {\n url: `${this.baseUrl}/v1/auth/authorize?${params.toString()}`,\n state,\n codeVerifier,\n cookies,\n }\n }\n\n /**\n * Handle the OAuth callback: verify state, exchange code, fetch user info, and return a redirect Response.\n * @param request - The incoming Request object from the callback\n * @param redirectTo - URL to redirect to after successful authentication (default: '/')\n * @returns CallbackResult with redirect Response (cookies set) and user info to store\n * @throws AuthorizationError if state is missing or doesn't match\n */\n async handleCallback(request: Request, redirectTo: string = '/'): Promise<CallbackResult> {\n const url = new URL(request.url)\n const code = url.searchParams.get('code')\n const state = url.searchParams.get('state')\n\n if (!code) {\n throw new AuthorizationError('Missing authorization code in callback')\n }\n\n if (!state) {\n throw new AuthorizationError('Missing state parameter in callback')\n }\n\n const cookieHeader = request.headers.get('cookie') ?? ''\n const expectedState = parseCookie(cookieHeader, 'SIMPLELOGIN_STATE')\n const codeVerifier = parseCookie(cookieHeader, 'SIMPLELOGIN_CODE_VERIFIER')\n\n if (!expectedState) {\n throw new AuthorizationError('Missing SIMPLELOGIN_STATE cookie')\n }\n\n if (state !== expectedState) {\n throw new AuthorizationError('Invalid state parameter')\n }\n\n if (!codeVerifier) {\n throw new AuthorizationError('Missing SIMPLELOGIN_CODE_VERIFIER cookie')\n }\n\n // Exchange code for tokens (with PKCE code_verifier)\n const tokens = await this.exchangeCode(code, codeVerifier)\n\n // Fetch user info (only time we do this)\n const user = await this.getUserInfo(tokens.access_token)\n\n // Build redirect response with token cookies\n const cookies = createTokenCookies(tokens)\n const headers = new Headers()\n headers.set('Location', redirectTo)\n for (const cookie of cookies) {\n headers.append('Set-Cookie', cookie)\n }\n // Clear the state and code verifier cookies\n headers.append(\n 'Set-Cookie',\n 'SIMPLELOGIN_STATE=; HttpOnly; Secure; SameSite=Lax; Path=/; Max-Age=0',\n )\n headers.append(\n 'Set-Cookie',\n 'SIMPLELOGIN_CODE_VERIFIER=; HttpOnly; Secure; SameSite=Lax; Path=/; Max-Age=0',\n )\n\n const response = new Response(null, { status: 302, headers })\n\n return { response, user }\n }\n\n /**\n * Exchange an authorization code for access and refresh tokens\n * @param code - The authorization code from the callback\n * @param codeVerifier - The PKCE code verifier\n */\n async exchangeCode(code: string, codeVerifier: string): Promise<TokenResponse> {\n const response = await fetch(`${this.baseUrl}/v1/auth/token`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n },\n body: new URLSearchParams({\n grant_type: 'authorization_code',\n client_id: this.clientId,\n client_secret: this.clientSecret,\n redirect_uri: this.redirectUri,\n code,\n code_verifier: codeVerifier,\n }),\n })\n\n if (!response.ok) {\n const error = await response.json().catch(() => ({}))\n throw new TokenError(error.error_description || 'Failed to exchange authorization code')\n }\n\n return response.json()\n }\n\n /**\n * Refresh an access token using a refresh token\n */\n async refreshToken(refreshToken: string): Promise<TokenResponse> {\n const response = await fetch(`${this.baseUrl}/v1/auth/token`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n },\n body: new URLSearchParams({\n grant_type: 'refresh_token',\n client_id: this.clientId,\n client_secret: this.clientSecret,\n refresh_token: refreshToken,\n }),\n })\n\n if (!response.ok) {\n const error = await response.json().catch(() => ({}))\n throw new TokenError(error.error_description || 'Failed to refresh token')\n }\n\n return response.json()\n }\n\n /**\n * Get user information using an access token\n */\n async getUserInfo(accessToken: string): Promise<UserInfo> {\n const response = await fetch(`${this.baseUrl}/v1/auth/userinfo`, {\n headers: {\n Authorization: `Bearer ${accessToken}`,\n },\n })\n\n if (!response.ok) {\n throw new AuthorizationError('Failed to fetch user info')\n }\n\n return response.json()\n }\n\n /**\n * Fetch and cache the public key for JWT verification\n */\n private async getPublicKey(): Promise<CryptoKey> {\n const cached = publicKeyCache.get(this.clientId)\n if (cached && Date.now() - cached.fetchedAt < PUBLIC_KEY_CACHE_TTL) {\n return cached.key\n }\n\n const response = await fetch(`${this.baseUrl}/api/applications/${this.clientId}/public-key`)\n if (!response.ok) {\n throw new AuthorizationError('Failed to fetch public key')\n }\n\n const pem = await response.text()\n const key = await importSPKI(pem, 'RS256')\n\n publicKeyCache.set(this.clientId, { key, fetchedAt: Date.now() })\n return key\n }\n\n /**\n * Authenticate a request by verifying the access token from cookies.\n * No network calls except when refreshing expired tokens.\n * CSRF protection is automatic using the origin from config.\n * @param request - The incoming Request object\n * @returns AuthResult if authenticated, null if not authenticated\n */\n async authenticate(request: Request): Promise<AuthResult | null> {\n // CSRF protection for state-changing requests\n const method = request.method.toUpperCase()\n if (method !== 'GET' && method !== 'HEAD') {\n const requestOrigin = request.headers.get('origin') || request.headers.get('referer')\n if (!requestOrigin?.startsWith(this.origin)) {\n return null\n }\n }\n\n const cookieHeader = request.headers.get('cookie') ?? ''\n const accessToken = parseCookie(cookieHeader, 'SIMPLELOGIN_ACCESS_TOKEN')\n const refreshTokenValue = parseCookie(cookieHeader, 'SIMPLELOGIN_REFRESH_TOKEN')\n\n if (!accessToken) {\n return null\n }\n\n const publicKey = await this.getPublicKey()\n\n try {\n // Verify the token (local, no network call)\n await jwtVerify(accessToken, publicKey)\n\n // Decode claims from JWT (no network call)\n const claims = decodeJwt(accessToken) as AccessTokenClaims\n\n return { claims, accessToken, headers: new Headers() }\n } catch (error) {\n // Check if token is expired\n const isExpired =\n error instanceof Error &&\n 'code' in error &&\n (error as { code: string }).code === 'ERR_JWT_EXPIRED'\n\n if (!isExpired || !refreshTokenValue) {\n return null\n }\n\n // Token expired, try to refresh\n try {\n const tokens = await this.refreshToken(refreshTokenValue)\n const claims = decodeJwt(tokens.access_token) as AccessTokenClaims\n\n // Build headers with new cookies\n const headers = new Headers()\n for (const cookie of createTokenCookies(tokens)) {\n headers.append('Set-Cookie', cookie)\n }\n\n return { claims, accessToken: tokens.access_token, headers }\n } catch {\n return null\n }\n }\n }\n\n /**\n * Revoke a refresh token on the IdP\n */\n private async revokeToken(refreshToken: string): Promise<void> {\n await fetch(`${this.baseUrl}/v1/auth/revoke`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n body: new URLSearchParams({\n token: refreshToken,\n client_id: this.clientId,\n client_secret: this.clientSecret,\n }),\n })\n }\n\n /**\n * Logout: revoke refresh token on IdP, clear cookies, and redirect to auth.\n * @param request - The incoming Request object (to read refresh token from cookies)\n */\n async logout(request: Request): Promise<Response> {\n const cookieHeader = request.headers.get('cookie') ?? ''\n const refreshToken = parseCookie(cookieHeader, 'SIMPLELOGIN_REFRESH_TOKEN')\n\n // Revoke token on IdP (fire and forget - don't block logout on failure)\n if (refreshToken) {\n this.revokeToken(refreshToken).catch(() => {})\n }\n\n // Get auth redirect response\n const authResponse = await this.redirectToAuth()\n\n // Merge clear cookies with auth cookies\n const headers = new Headers(authResponse.headers)\n for (const cookie of clearTokenCookies()) {\n headers.append('Set-Cookie', cookie)\n }\n\n return new Response(null, { status: 302, headers })\n }\n}\n","export class SousaError extends Error {\n constructor(\n message: string,\n public code: string,\n public statusCode?: number\n ) {\n super(message);\n this.name = \"SousaError\";\n }\n}\n\nexport class AuthorizationError extends SousaError {\n constructor(message: string) {\n super(message, \"AUTHORIZATION_ERROR\", 401);\n this.name = \"AuthorizationError\";\n }\n}\n\nexport class TokenError extends SousaError {\n constructor(message: string) {\n super(message, \"TOKEN_ERROR\", 400);\n this.name = \"TokenError\";\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/client.ts","../src/errors.ts"],"sourcesContent":["import { decodeJwt, importSPKI, jwtVerify } from 'jose'\nimport { AuthorizationError, TokenError } from './errors'\nimport type {\n AccessTokenClaims,\n AuthResult,\n AuthorizationUrlOptions,\n AuthorizationUrlResult,\n CallbackResult,\n SimpleLoginConfig,\n TokenResponse,\n UserInfo,\n} from './types'\n\ndeclare const __SIMPLELOGIN_BASE_URL__: string\n\nconst BASE_URL = __SIMPLELOGIN_BASE_URL__\n\nfunction generateState(): string {\n const array = new Uint8Array(32)\n crypto.getRandomValues(array)\n return Array.from(array, (b) => b.toString(16).padStart(2, '0')).join('')\n}\n\nfunction generateCodeVerifier(): string {\n const array = new Uint8Array(32)\n crypto.getRandomValues(array)\n // Base64url encode (RFC 7636)\n return btoa(String.fromCharCode(...array))\n .replace(/\\+/g, '-')\n .replace(/\\//g, '_')\n .replace(/=/g, '')\n}\n\nasync function generateCodeChallenge(verifier: string): Promise<string> {\n const encoder = new TextEncoder()\n const data = encoder.encode(verifier)\n const hash = await crypto.subtle.digest('SHA-256', data)\n // Base64url encode the hash\n return btoa(String.fromCharCode(...new Uint8Array(hash)))\n .replace(/\\+/g, '-')\n .replace(/\\//g, '_')\n .replace(/=/g, '')\n}\n\nfunction getEnv(key: string): string | undefined {\n if (typeof process !== 'undefined' && process.env) {\n return process.env[key]\n }\n return undefined\n}\n\nfunction parseCookie(cookieHeader: string, name: string): string | undefined {\n const match = cookieHeader.match(new RegExp(`(?:^|;\\\\s*)${name}=([^;]*)`))\n return match ? decodeURIComponent(match[1]) : undefined\n}\n\n// Public key cache: clientId -> { key, fetchedAt }\nconst publicKeyCache = new Map<string, { key: CryptoKey; fetchedAt: number }>()\nconst PUBLIC_KEY_CACHE_TTL = 60 * 60 * 1000 // 1 hour\n\nfunction createTokenCookies(tokens: TokenResponse): string[] {\n return [\n `SIMPLELOGIN_ACCESS_TOKEN=${tokens.access_token}; HttpOnly; Secure; SameSite=Lax; Path=/`,\n `SIMPLELOGIN_REFRESH_TOKEN=${tokens.refresh_token}; HttpOnly; Secure; SameSite=Lax; Path=/`,\n ]\n}\n\nfunction clearTokenCookies(): string[] {\n return [\n 'SIMPLELOGIN_ACCESS_TOKEN=; HttpOnly; Secure; SameSite=Lax; Path=/; Max-Age=0',\n 'SIMPLELOGIN_REFRESH_TOKEN=; HttpOnly; Secure; SameSite=Lax; Path=/; Max-Age=0',\n ]\n}\n\nexport class SimpleLogin {\n private clientId: string\n private clientSecret: string\n private redirectUri: string\n private origin: string\n private baseUrl: string\n\n constructor(config: SimpleLoginConfig = {}) {\n this.clientId = config.clientId ?? getEnv('SIMPLELOGIN_CLIENT_ID') ?? ''\n this.clientSecret = config.clientSecret ?? getEnv('SIMPLELOGIN_CLIENT_SECRET') ?? ''\n this.redirectUri = config.redirectUri ?? getEnv('SIMPLELOGIN_REDIRECT_URI') ?? ''\n this.origin = config.origin ?? getEnv('SIMPLELOGIN_ORIGIN') ?? ''\n this.baseUrl = BASE_URL\n\n if (!this.clientId) {\n throw new Error(\n 'clientId is required. Pass it in config or set SIMPLELOGIN_CLIENT_ID env var.',\n )\n }\n if (!this.clientSecret) {\n throw new Error(\n 'clientSecret is required. Pass it in config or set SIMPLELOGIN_CLIENT_SECRET env var.',\n )\n }\n if (!this.redirectUri) {\n throw new Error(\n 'redirectUri is required. Pass it in config or set SIMPLELOGIN_REDIRECT_URI env var.',\n )\n }\n if (!this.origin) {\n throw new Error('origin is required. Pass it in config or set SIMPLELOGIN_ORIGIN env var.')\n }\n }\n\n /**\n * Returns a Response that redirects to the authorization URL with state and PKCE cookies set.\n * @param options - Optional scopes or custom state\n * @returns A 302 redirect Response ready to be returned from your route handler\n */\n async redirectToAuth(options: AuthorizationUrlOptions = {}): Promise<Response> {\n const { url, cookies } = await this.getAuthorizationUrl(options)\n const headers = new Headers()\n headers.set('Location', url)\n for (const cookie of cookies) {\n headers.append('Set-Cookie', cookie)\n }\n return new Response(null, { status: 302, headers })\n }\n\n /**\n * Generate the authorization URL to redirect users for sign-in\n * @returns The authorization URL, state, code verifier, and ready-to-use Set-Cookie header values\n */\n async getAuthorizationUrl(\n options: AuthorizationUrlOptions = {},\n ): Promise<AuthorizationUrlResult> {\n const state = options.state ?? generateState()\n const codeVerifier = generateCodeVerifier()\n const codeChallenge = await generateCodeChallenge(codeVerifier)\n\n const params = new URLSearchParams({\n client_id: this.clientId,\n redirect_uri: this.redirectUri,\n response_type: 'code',\n state,\n code_challenge: codeChallenge,\n code_challenge_method: 'S256',\n })\n\n if (options.scopes?.length) {\n params.set('scope', options.scopes.join(' '))\n }\n\n const cookies = [\n `SIMPLELOGIN_STATE=${state}; HttpOnly; Secure; SameSite=Lax; Max-Age=600; Path=/`,\n `SIMPLELOGIN_CODE_VERIFIER=${codeVerifier}; HttpOnly; Secure; SameSite=Lax; Max-Age=600; Path=/`,\n ]\n\n return {\n url: `${this.baseUrl}/v1/auth/authorize?${params.toString()}`,\n state,\n codeVerifier,\n cookies,\n }\n }\n\n /**\n * Handle the OAuth callback: verify state, exchange code, fetch user info, and return a redirect Response.\n * @param request - The incoming Request object from the callback\n * @param redirectTo - URL to redirect to after successful authentication (default: '/')\n * @returns CallbackResult with redirect Response (cookies set) and user info to store\n * @throws AuthorizationError if state is missing or doesn't match\n */\n async handleCallback(request: Request, redirectTo: string = '/'): Promise<CallbackResult> {\n const url = new URL(request.url)\n const code = url.searchParams.get('code')\n const state = url.searchParams.get('state')\n\n if (!code) {\n throw new AuthorizationError('Missing authorization code in callback')\n }\n\n if (!state) {\n throw new AuthorizationError('Missing state parameter in callback')\n }\n\n const cookieHeader = request.headers.get('cookie') ?? ''\n const expectedState = parseCookie(cookieHeader, 'SIMPLELOGIN_STATE')\n const codeVerifier = parseCookie(cookieHeader, 'SIMPLELOGIN_CODE_VERIFIER')\n\n if (!expectedState) {\n throw new AuthorizationError('Missing SIMPLELOGIN_STATE cookie')\n }\n\n if (state !== expectedState) {\n throw new AuthorizationError('Invalid state parameter')\n }\n\n if (!codeVerifier) {\n throw new AuthorizationError('Missing SIMPLELOGIN_CODE_VERIFIER cookie')\n }\n\n // Exchange code for tokens (with PKCE code_verifier)\n const tokens = await this.exchangeCode(code, codeVerifier)\n\n // Fetch user info (only time we do this)\n const user = await this.getUserInfo(tokens.access_token)\n\n // Build redirect response with token cookies\n const cookies = createTokenCookies(tokens)\n const headers = new Headers()\n headers.set('Location', redirectTo)\n for (const cookie of cookies) {\n headers.append('Set-Cookie', cookie)\n }\n // Clear the state and code verifier cookies\n headers.append(\n 'Set-Cookie',\n 'SIMPLELOGIN_STATE=; HttpOnly; Secure; SameSite=Lax; Path=/; Max-Age=0',\n )\n headers.append(\n 'Set-Cookie',\n 'SIMPLELOGIN_CODE_VERIFIER=; HttpOnly; Secure; SameSite=Lax; Path=/; Max-Age=0',\n )\n\n const response = new Response(null, { status: 302, headers })\n\n return { response, user }\n }\n\n /**\n * Exchange an authorization code for access and refresh tokens\n * @param code - The authorization code from the callback\n * @param codeVerifier - The PKCE code verifier\n */\n async exchangeCode(code: string, codeVerifier: string): Promise<TokenResponse> {\n const response = await fetch(`${this.baseUrl}/v1/auth/token`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n },\n body: new URLSearchParams({\n grant_type: 'authorization_code',\n client_id: this.clientId,\n client_secret: this.clientSecret,\n redirect_uri: this.redirectUri,\n code,\n code_verifier: codeVerifier,\n }),\n })\n\n if (!response.ok) {\n const error = await response.json().catch(() => ({}))\n throw new TokenError(error.error_description || 'Failed to exchange authorization code')\n }\n\n return response.json()\n }\n\n /**\n * Refresh an access token using a refresh token\n */\n async refreshToken(refreshToken: string): Promise<TokenResponse> {\n const response = await fetch(`${this.baseUrl}/v1/auth/token`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n },\n body: new URLSearchParams({\n grant_type: 'refresh_token',\n client_id: this.clientId,\n client_secret: this.clientSecret,\n refresh_token: refreshToken,\n }),\n })\n\n if (!response.ok) {\n const error = await response.json().catch(() => ({}))\n throw new TokenError(error.error_description || 'Failed to refresh token')\n }\n\n return response.json()\n }\n\n /**\n * Get user information using an access token\n */\n async getUserInfo(accessToken: string): Promise<UserInfo> {\n const response = await fetch(`${this.baseUrl}/v1/auth/userinfo`, {\n headers: {\n Authorization: `Bearer ${accessToken}`,\n },\n })\n\n if (!response.ok) {\n throw new AuthorizationError('Failed to fetch user info')\n }\n\n return response.json()\n }\n\n /**\n * Fetch and cache the public key for JWT verification\n */\n private async getPublicKey(): Promise<CryptoKey> {\n const cached = publicKeyCache.get(this.clientId)\n if (cached && Date.now() - cached.fetchedAt < PUBLIC_KEY_CACHE_TTL) {\n return cached.key\n }\n\n const response = await fetch(`${this.baseUrl}/api/applications/${this.clientId}/public-key`)\n if (!response.ok) {\n throw new AuthorizationError('Failed to fetch public key')\n }\n\n const data = await response.json()\n const pem = data['public_key']\n const key = await importSPKI(pem, 'RS256')\n\n publicKeyCache.set(this.clientId, { key, fetchedAt: Date.now() })\n return key\n }\n\n /**\n * Authenticate a request by verifying the access token from cookies.\n * No network calls except when refreshing expired tokens.\n * CSRF protection is automatic using the origin from config.\n * @param request - The incoming Request object\n * @returns AuthResult if authenticated, null if not authenticated\n */\n async authenticate(request: Request): Promise<AuthResult | null> {\n // CSRF protection for state-changing requests\n const method = request.method.toUpperCase()\n if (method !== 'GET' && method !== 'HEAD') {\n const requestOrigin = request.headers.get('origin') || request.headers.get('referer')\n if (!requestOrigin?.startsWith(this.origin)) {\n return null\n }\n }\n\n const cookieHeader = request.headers.get('cookie') ?? ''\n const accessToken = parseCookie(cookieHeader, 'SIMPLELOGIN_ACCESS_TOKEN')\n const refreshTokenValue = parseCookie(cookieHeader, 'SIMPLELOGIN_REFRESH_TOKEN')\n\n if (!accessToken) {\n return null\n }\n\n const publicKey = await this.getPublicKey()\n\n try {\n // Verify the token (local, no network call)\n await jwtVerify(accessToken, publicKey)\n\n // Decode claims from JWT (no network call)\n const claims = decodeJwt(accessToken) as AccessTokenClaims\n\n return { claims, accessToken, headers: new Headers() }\n } catch (error) {\n // Check if token is expired\n const isExpired =\n error instanceof Error &&\n 'code' in error &&\n (error as { code: string }).code === 'ERR_JWT_EXPIRED'\n\n if (!isExpired || !refreshTokenValue) {\n return null\n }\n\n // Token expired, try to refresh\n try {\n const tokens = await this.refreshToken(refreshTokenValue)\n const claims = decodeJwt(tokens.access_token) as AccessTokenClaims\n\n // Build headers with new cookies\n const headers = new Headers()\n for (const cookie of createTokenCookies(tokens)) {\n headers.append('Set-Cookie', cookie)\n }\n\n return { claims, accessToken: tokens.access_token, headers }\n } catch {\n return null\n }\n }\n }\n\n /**\n * Revoke a refresh token on the IdP\n */\n private async revokeToken(refreshToken: string): Promise<void> {\n await fetch(`${this.baseUrl}/v1/auth/revoke`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n body: new URLSearchParams({\n token: refreshToken,\n client_id: this.clientId,\n client_secret: this.clientSecret,\n }),\n })\n }\n\n /**\n * Logout: revoke refresh token on IdP, clear cookies, and redirect to auth.\n * @param request - The incoming Request object (to read refresh token from cookies)\n */\n async logout(request: Request): Promise<Response> {\n const cookieHeader = request.headers.get('cookie') ?? ''\n const refreshToken = parseCookie(cookieHeader, 'SIMPLELOGIN_REFRESH_TOKEN')\n\n // Revoke token on IdP (fire and forget - don't block logout on failure)\n if (refreshToken) {\n this.revokeToken(refreshToken).catch(() => {})\n }\n\n // Get auth redirect response\n const authResponse = await this.redirectToAuth()\n\n // Merge clear cookies with auth cookies\n const headers = new Headers(authResponse.headers)\n for (const cookie of clearTokenCookies()) {\n headers.append('Set-Cookie', cookie)\n }\n\n return new Response(null, { status: 302, headers })\n }\n}\n","export class SousaError extends Error {\n constructor(\n message: string,\n public code: string,\n public statusCode?: number\n ) {\n super(message);\n this.name = \"SousaError\";\n }\n}\n\nexport class AuthorizationError extends SousaError {\n constructor(message: string) {\n super(message, \"AUTHORIZATION_ERROR\", 401);\n this.name = \"AuthorizationError\";\n }\n}\n\nexport class TokenError extends SousaError {\n constructor(message: string) {\n super(message, \"TOKEN_ERROR\", 400);\n this.name = \"TokenError\";\n }\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@simple-login/sdk",
3
- "version": "1.3.0",
3
+ "version": "1.4.0",
4
4
  "description": "Official SDK for Simple Login",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",