@happyvertical/auth 0.103.4 → 0.103.5
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/AGENT.md +1 -1
- package/README.md +61 -0
- package/dist/chunks/{decode_jwt-DyHK2A0i.js → decode_jwt-ob-gfrt8.js} +2 -2
- package/dist/chunks/{decode_jwt-DyHK2A0i.js.map → decode_jwt-ob-gfrt8.js.map} +1 -1
- package/dist/chunks/{google-D8L8_cDF.js → google-CyESKfaj.js} +4 -3
- package/dist/chunks/{google-D8L8_cDF.js.map → google-CyESKfaj.js.map} +1 -1
- package/dist/chunks/{kanidm-YfZNisdt.js → kanidm-XUwRGxXX.js} +4 -3
- package/dist/chunks/{kanidm-YfZNisdt.js.map → kanidm-XUwRGxXX.js.map} +1 -1
- package/dist/chunks/{keycloak-hR2UOZ1n.js → keycloak-Bfk3iJUJ.js} +4 -3
- package/dist/chunks/{keycloak-hR2UOZ1n.js.map → keycloak-Bfk3iJUJ.js.map} +1 -1
- package/dist/chunks/remote-D3qRdbiU.js +274 -0
- package/dist/chunks/remote-D3qRdbiU.js.map +1 -0
- package/dist/chunks/{remote-C3O0f74l.js → verify-CGYWoFss.js} +80 -274
- package/dist/chunks/verify-CGYWoFss.js.map +1 -0
- package/dist/index.js +3 -3
- package/dist/local.js +2 -1
- package/dist/local.js.map +1 -1
- package/dist/server/index.d.ts +90 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +761 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/memory.d.ts +34 -0
- package/dist/server/memory.d.ts.map +1 -0
- package/dist/server/types.d.ts +182 -0
- package/dist/server/types.d.ts.map +1 -0
- package/metadata.json +2 -1
- package/package.json +6 -2
- package/dist/chunks/remote-C3O0f74l.js.map +0 -1
package/AGENT.md
CHANGED
|
@@ -25,7 +25,7 @@ pnpm --filter @happyvertical/auth clean
|
|
|
25
25
|
- If failures span multiple packages or Turborepo ordering looks wrong, run `pnpm build` and `pnpm typecheck` from the repo root before retrying package-scoped commands.
|
|
26
26
|
|
|
27
27
|
## Ecosystem Relationships
|
|
28
|
-
- Provides: Unified authentication for Keycloak, Cognito, Nostr, and local ChatGPT sessions with OAuth2/OIDC and public key identity
|
|
28
|
+
- Provides: Unified authentication for Keycloak, Cognito, Nostr, and local ChatGPT sessions with OAuth2/OIDC and public key identity, Framework-independent OAuth authorization server via @happyvertical/auth/server: S256 PKCE, rotating refresh tokens, durable storage and live consent adapters, discovery, JWKS, revocation and opt-in client registration
|
|
29
29
|
- Implements: none
|
|
30
30
|
- Requires: @happyvertical/utils, @aws-sdk/client-cognito-identity-provider, jose, nostr-tools
|
|
31
31
|
- Stability: stable (Primary package surface is described as implemented and production-oriented.)
|
package/README.md
CHANGED
|
@@ -68,6 +68,65 @@ if (await auth.hasRole(token, 'admin')) {
|
|
|
68
68
|
|
|
69
69
|
See [AGENT.md](./AGENT.md) for complete API documentation.
|
|
70
70
|
|
|
71
|
+
## Authorization server
|
|
72
|
+
|
|
73
|
+
`@happyvertical/auth/server` provides a framework-independent OAuth 2.0
|
|
74
|
+
authorization-code issuer. It intentionally does not authenticate browser
|
|
75
|
+
sessions or render consent: an application validates its current session and
|
|
76
|
+
permissions, then calls `parseAuthorizationRequest()` and `approve()` with an
|
|
77
|
+
application-owned `OAuthConsentContext`.
|
|
78
|
+
|
|
79
|
+
```typescript
|
|
80
|
+
import { createAuthorizationServer } from '@happyvertical/auth/server';
|
|
81
|
+
|
|
82
|
+
const request = await server.parseAuthorizationRequest(url.searchParams);
|
|
83
|
+
const approved = await server.approve(request, {
|
|
84
|
+
subject: session.userId,
|
|
85
|
+
tenantId: session.activeTenantId,
|
|
86
|
+
});
|
|
87
|
+
return Response.redirect(approved.redirectUri, 302);
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Supply a durable `OAuthAuthorizationStorage` implementation. Its
|
|
91
|
+
`consumeAuthorizationCode` and `rotateRefreshGrant` operations must be atomic
|
|
92
|
+
database transactions (or compare-and-swap operations); the exported in-memory
|
|
93
|
+
store is for tests only. The server enforces S256 PKCE, exact redirect URI and
|
|
94
|
+
resource binding, JWT signing/JWKS, refresh-token rotation with family replay
|
|
95
|
+
revocation, and live authorization checks at both code exchange and refresh
|
|
96
|
+
through `OAuthIdentityProvider.revalidateConsent` (the legacy `refreshConsent`
|
|
97
|
+
hook is a fallback for both). Return the unchanged subject and tenant, optional
|
|
98
|
+
replacement application claims, and optional scopes that are a subset of the
|
|
99
|
+
grant. Return null to deny; changing identity, tenant, or expanding scope fails
|
|
100
|
+
closed. Omitting claims removes previous application claims. JWT protocol claims
|
|
101
|
+
are reserved. Consent supplied to `approve` may also narrow the requested scopes.
|
|
102
|
+
|
|
103
|
+
Stores must implement `narrowRefreshGrant` when refresh tokens are issued (the constructor rejects stores without it): atomically
|
|
104
|
+
validate the replacement token hash and active grant, then persist only a subset
|
|
105
|
+
of its current scopes. Throw on any failure. The server revokes the family on
|
|
106
|
+
failed live checks or narrowing persistence; it never returns the replacement.
|
|
107
|
+
Storage failures must reject; never return success before durable commit. A
|
|
108
|
+
connection failure after commit can leave an unreachable hash-only grant, but
|
|
109
|
+
must not expose any token. Concrete SQL adapters and dialect tests belong to the
|
|
110
|
+
host application.
|
|
111
|
+
|
|
112
|
+
Path issuers such as `https://host/oauth` advertise endpoints under `/oauth` and
|
|
113
|
+
RFC 8414 discovery at `/.well-known/oauth-authorization-server/oauth`. Public
|
|
114
|
+
DCR accepts only code responses, authorization-code/refresh grants, allowed
|
|
115
|
+
scopes, and HTTPS redirects. HTTP loopback redirects require explicit
|
|
116
|
+
`allowLoopbackRedirects: true` for native/development clients. Resource servers must pass their own audience to `verifyAccessToken(token, audience)`. Access-token
|
|
117
|
+
verification requires `typ: at+jwt`, expiry, issued-at, subject, audience, token
|
|
118
|
+
ID, client ID and scope with valid claim types. Registration bodies are limited to 16 KiB while streaming, including when
|
|
119
|
+
Content-Length is absent or understated. Metadata permits at most 10 redirect
|
|
120
|
+
URIs of 2,048 characters each, a 256-character client name, a 2,048-character
|
|
121
|
+
scope string, two grant types and one response type. Direct `register()` calls
|
|
122
|
+
apply the same field bounds. Oversized bodies return 413 before persistence;
|
|
123
|
+
invalid metadata returns 400. Unknown extension metadata is ignored within the
|
|
124
|
+
body limit. Revocation returns success for unknown or invalid tokens, but
|
|
125
|
+
storage outages return a generic 500 server_error so callers can retry after
|
|
126
|
+
recovery. CIMD is not implemented. The signing API supports one active key;
|
|
127
|
+
overlapping signing-key rotation is not implemented. Hosts must supply a public
|
|
128
|
+
JWK matching the private signing key and keep private material out of JWKS.
|
|
129
|
+
|
|
71
130
|
## Local Sign in with ChatGPT
|
|
72
131
|
|
|
73
132
|
Node-only local applications can import `@happyvertical/auth/local` to manage
|
|
@@ -85,3 +144,5 @@ inference request or use browser storage.
|
|
|
85
144
|
## License
|
|
86
145
|
|
|
87
146
|
MIT
|
|
147
|
+
|
|
148
|
+
Refresh storage adapters must implement `narrowRefreshGrant` to atomically persist both the narrowed scopes and the current `claims` on the exact replacement token before it is returned. An explicit `undefined` claims value clears previous application claims. Token and revocation form bodies share the 16 KiB streaming limit with registration. Refresh requests may request a subset of the original grant scope; expansions are rejected.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { C as decode, E as decoder, S as JWTInvalid, l as isObject } from "./verify-CGYWoFss.js";
|
|
2
2
|
//#region ../../node_modules/.pnpm/jose@6.2.2/node_modules/jose/dist/webapi/util/decode_jwt.js
|
|
3
3
|
function decodeJwt(jwt) {
|
|
4
4
|
if (typeof jwt !== "string") throw new JWTInvalid("JWTs must use Compact JWS serialization, JWT must be a string");
|
|
@@ -24,4 +24,4 @@ function decodeJwt(jwt) {
|
|
|
24
24
|
//#endregion
|
|
25
25
|
export { decodeJwt as t };
|
|
26
26
|
|
|
27
|
-
//# sourceMappingURL=decode_jwt-
|
|
27
|
+
//# sourceMappingURL=decode_jwt-ob-gfrt8.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"decode_jwt-
|
|
1
|
+
{"version":3,"file":"decode_jwt-ob-gfrt8.js","names":["b64u"],"sources":["../../../../node_modules/.pnpm/jose@6.2.2/node_modules/jose/dist/webapi/util/decode_jwt.js"],"sourcesContent":["import { decode as b64u } from './base64url.js';\nimport { decoder } from '../lib/buffer_utils.js';\nimport { isObject } from '../lib/type_checks.js';\nimport { JWTInvalid } from './errors.js';\nexport function decodeJwt(jwt) {\n if (typeof jwt !== 'string')\n throw new JWTInvalid('JWTs must use Compact JWS serialization, JWT must be a string');\n const { 1: payload, length } = jwt.split('.');\n if (length === 5)\n throw new JWTInvalid('Only JWTs using Compact JWS serialization can be decoded');\n if (length !== 3)\n throw new JWTInvalid('Invalid JWT');\n if (!payload)\n throw new JWTInvalid('JWTs must contain a payload');\n let decoded;\n try {\n decoded = b64u(payload);\n }\n catch {\n throw new JWTInvalid('Failed to base64url decode the payload');\n }\n let result;\n try {\n result = JSON.parse(decoder.decode(decoded));\n }\n catch {\n throw new JWTInvalid('Failed to parse the decoded payload as JSON');\n }\n if (!isObject(result))\n throw new JWTInvalid('Invalid JWT Claims Set');\n return result;\n}\n"],"x_google_ignoreList":[0],"mappings":";;AAIA,SAAgB,UAAU,KAAK;CAC3B,IAAI,OAAO,QAAQ,UACf,MAAM,IAAI,WAAW,+DAA+D;CACxF,MAAM,EAAE,GAAG,SAAS,WAAW,IAAI,MAAM,GAAG;CAC5C,IAAI,WAAW,GACX,MAAM,IAAI,WAAW,0DAA0D;CACnF,IAAI,WAAW,GACX,MAAM,IAAI,WAAW,aAAa;CACtC,IAAI,CAAC,SACD,MAAM,IAAI,WAAW,6BAA6B;CACtD,IAAI;CACJ,IAAI;EACA,UAAUA,OAAK,OAAO;CAC1B,QACM;EACF,MAAM,IAAI,WAAW,wCAAwC;CACjE;CACA,IAAI;CACJ,IAAI;EACA,SAAS,KAAK,MAAM,QAAQ,OAAO,OAAO,CAAC;CAC/C,QACM;EACF,MAAM,IAAI,WAAW,6CAA6C;CACtE;CACA,IAAI,CAAC,SAAS,MAAM,GAChB,MAAM,IAAI,WAAW,wBAAwB;CACjD,OAAO;AACX"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { S as ProviderError, T as TokenExpiredError, a as ConfigurationError, b as NetworkError, c as InvalidClientError, p as InvalidNonceError, t as AccessDeniedError, u as InvalidGrantError, v as InvalidTokenError, x as NotImplementedError } from "./errors-RgVH84_1.js";
|
|
2
|
-
import {
|
|
3
|
-
import { t as
|
|
2
|
+
import { b as JWTClaimValidationFailed, t as jwtVerify, x as JWTExpired, y as JWSSignatureVerificationFailed } from "./verify-CGYWoFss.js";
|
|
3
|
+
import { t as createRemoteJWKSet } from "./remote-D3qRdbiU.js";
|
|
4
|
+
import { t as decodeJwt } from "./decode_jwt-ob-gfrt8.js";
|
|
4
5
|
//#region src/shared/providers/google.ts
|
|
5
6
|
/**
|
|
6
7
|
* Google Provider - OAuth2/OIDC Authentication
|
|
@@ -372,4 +373,4 @@ var GoogleProvider = class GoogleProvider {
|
|
|
372
373
|
//#endregion
|
|
373
374
|
export { GoogleProvider };
|
|
374
375
|
|
|
375
|
-
//# sourceMappingURL=google-
|
|
376
|
+
//# sourceMappingURL=google-CyESKfaj.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"google-D8L8_cDF.js","names":[],"sources":["../../src/shared/providers/google.ts"],"sourcesContent":["/**\n * Google Provider - OAuth2/OIDC Authentication\n *\n * Implements OAuth2/OIDC authentication with Google including:\n * - OIDC Discovery\n * - Authorization Code Flow with PKCE\n * - Token validation using JWKS (RS256)\n * - ID token claims extraction\n */\n\nimport * as jose from 'jose';\n\nimport {\n AccessDeniedError,\n ConfigurationError,\n InvalidClientError,\n InvalidGrantError,\n InvalidNonceError,\n InvalidTokenError,\n NetworkError,\n NotImplementedError,\n ProviderError,\n TokenExpiredError,\n} from '../errors.js';\nimport type {\n AuthCapabilities,\n AuthCredentials,\n AuthInterface,\n AuthorizationOptions,\n AuthorizationResult,\n AuthResult,\n CodeExchangeParams,\n CreateUserRequest,\n GoogleOptions,\n LogoutOptions,\n OIDCDiscoveryDocument,\n Session,\n TokenClaims,\n TokenIntrospection,\n TokenPayload,\n TokenValidationOptions,\n UserListResult,\n UserProfile,\n UserQuery,\n} from '../types.js';\n\n/**\n * Generate a random string for state/nonce/PKCE.\n */\nfunction generateRandomString(length = 32): string {\n const array = new Uint8Array(length);\n crypto.getRandomValues(array);\n return Array.from(array, (byte) => byte.toString(16).padStart(2, '0')).join(\n '',\n );\n}\n\n/**\n * Generate PKCE code verifier and challenge.\n */\nasync function generatePKCE(): Promise<{\n verifier: string;\n challenge: string;\n}> {\n const verifier = generateRandomString(32);\n const encoder = new TextEncoder();\n const data = encoder.encode(verifier);\n const hash = await crypto.subtle.digest('SHA-256', data);\n const challenge = btoa(String.fromCharCode(...new Uint8Array(hash)))\n .replace(/\\+/g, '-')\n .replace(/\\//g, '_')\n .replace(/=+$/, '');\n return { verifier, challenge };\n}\n\n/**\n * Google authentication provider.\n *\n * Implements OAuth2/OIDC authentication with Google.\n * Uses standard OIDC discovery and PKCE for security.\n */\nexport class GoogleProvider implements AuthInterface {\n private options: Required<Pick<GoogleOptions, 'clientId'>> & GoogleOptions;\n private discoveryDocument: OIDCDiscoveryDocument | null = null;\n private jwks: jose.JWTVerifyGetKey | null = null;\n\n private static readonly DISCOVERY_URL =\n 'https://accounts.google.com/.well-known/openid-configuration';\n\n constructor(options: GoogleOptions) {\n if (!options.clientId) {\n throw new ConfigurationError('clientId is required', 'google');\n }\n if (!options.clientSecret) {\n throw new ConfigurationError('clientSecret is required', 'google');\n }\n\n this.options = {\n usePKCE: true,\n scopes: ['openid', 'profile', 'email'],\n timeout: 30000,\n maxRetries: 3,\n ...options,\n };\n }\n\n // ---------------------------------------------------------------------------\n // INTERNAL HELPERS\n // ---------------------------------------------------------------------------\n\n /**\n * Make an HTTP request with error handling.\n */\n private async request<T>(\n url: string,\n options: RequestInit = {},\n token?: string,\n ): Promise<T> {\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n ...this.options.headers,\n ...(options.headers as Record<string, string>),\n };\n\n if (token) {\n headers.Authorization = `Bearer ${token}`;\n }\n\n try {\n const response = await fetch(url, {\n ...options,\n headers,\n signal: AbortSignal.timeout(this.options.timeout || 30000),\n });\n\n if (!response.ok) {\n const errorBody = await response.text().catch(() => '');\n let errorData: Record<string, unknown> = {};\n try {\n errorData = JSON.parse(errorBody);\n } catch {\n // Not JSON\n }\n\n this.handleHttpError(response.status, errorData, errorBody);\n }\n\n const text = await response.text();\n if (!text) return {} as T;\n return JSON.parse(text) as T;\n } catch (error) {\n if (error instanceof Error && error.name === 'TimeoutError') {\n throw new NetworkError('Request timed out', 'google', error);\n }\n if (\n error instanceof AccessDeniedError ||\n error instanceof InvalidGrantError ||\n error instanceof InvalidClientError ||\n error instanceof ProviderError\n ) {\n throw error;\n }\n throw new NetworkError(\n `Network error: ${error instanceof Error ? error.message : 'Unknown error'}`,\n 'google',\n error instanceof Error ? error : undefined,\n );\n }\n }\n\n /**\n * Handle HTTP error responses.\n */\n private handleHttpError(\n status: number,\n data: Record<string, unknown>,\n rawBody: string,\n ): never {\n const error = data.error as string | undefined;\n const errorDescription = data.error_description || data.message || rawBody;\n\n switch (status) {\n case 400:\n if (error === 'invalid_grant') {\n throw new InvalidGrantError(errorDescription as string, 'google');\n }\n if (error === 'invalid_client') {\n throw new InvalidClientError('google');\n }\n throw new ProviderError(`Bad request: ${errorDescription}`, 'google');\n\n case 401:\n throw new InvalidClientError('google');\n\n case 403:\n throw new AccessDeniedError(errorDescription as string, 'google');\n\n default:\n throw new ProviderError(\n `Google error (${status}): ${errorDescription}`,\n 'google',\n );\n }\n }\n\n /**\n * Fetch and cache the OIDC discovery document.\n */\n private async fetchDiscoveryDocument(): Promise<OIDCDiscoveryDocument> {\n if (this.discoveryDocument) {\n return this.discoveryDocument;\n }\n\n this.discoveryDocument = await this.request<OIDCDiscoveryDocument>(\n GoogleProvider.DISCOVERY_URL,\n );\n return this.discoveryDocument;\n }\n\n /**\n * Get JWKS for token validation.\n */\n private async getJWKS(): Promise<jose.JWTVerifyGetKey> {\n if (this.jwks) {\n return this.jwks;\n }\n\n const discovery = await this.fetchDiscoveryDocument();\n this.jwks = jose.createRemoteJWKSet(new URL(discovery.jwks_uri));\n return this.jwks;\n }\n\n // ---------------------------------------------------------------------------\n // AUTHENTICATION FLOWS\n // ---------------------------------------------------------------------------\n\n async getAuthorizationUrl(\n options?: AuthorizationOptions,\n ): Promise<AuthorizationResult> {\n const discovery = await this.fetchDiscoveryDocument();\n\n const state = options?.state || generateRandomString();\n const nonce = options?.nonce || generateRandomString();\n const scopes = options?.scopes ||\n this.options.scopes || ['openid', 'profile', 'email'];\n const redirectUri = options?.redirectUri || this.options.redirectUri;\n\n if (!redirectUri) {\n throw new ConfigurationError('redirectUri is required', 'google');\n }\n\n const params = new URLSearchParams({\n client_id: this.options.clientId,\n redirect_uri: redirectUri,\n response_type: 'code',\n scope: scopes.join(' '),\n state,\n nonce,\n access_type: 'offline', // Request refresh token\n });\n\n if (options?.prompt) {\n params.set('prompt', options.prompt);\n }\n\n if (options?.loginHint) {\n params.set('login_hint', options.loginHint);\n }\n\n // Add extra params\n if (options?.extraParams) {\n for (const [key, value] of Object.entries(options.extraParams)) {\n params.set(key, value);\n }\n }\n\n // PKCE\n let codeVerifier: string | undefined;\n if (this.options.usePKCE !== false) {\n const pkce = await generatePKCE();\n codeVerifier = pkce.verifier;\n params.set('code_challenge', pkce.challenge);\n params.set('code_challenge_method', 'S256');\n }\n\n const url = `${discovery.authorization_endpoint}?${params.toString()}`;\n\n return {\n url,\n state,\n nonce,\n codeVerifier,\n };\n }\n\n async exchangeCode(params: CodeExchangeParams): Promise<AuthResult> {\n const discovery = await this.fetchDiscoveryDocument();\n\n const redirectUri = params.redirectUri || this.options.redirectUri;\n if (!redirectUri) {\n throw new ConfigurationError('redirectUri is required', 'google');\n }\n\n const body = new URLSearchParams({\n grant_type: 'authorization_code',\n client_id: this.options.clientId,\n client_secret: this.options.clientSecret!,\n code: params.code,\n redirect_uri: redirectUri,\n });\n\n // Code verifier for PKCE\n if (params.codeVerifier) {\n body.set('code_verifier', params.codeVerifier);\n }\n\n const response = await this.request<{\n access_token: string;\n refresh_token?: string;\n id_token?: string;\n expires_in: number;\n token_type: string;\n scope?: string;\n }>(discovery.token_endpoint, {\n method: 'POST',\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n body: body.toString(),\n });\n\n // Decode ID token to get user ID\n let userId = '';\n if (response.id_token) {\n const payload = jose.decodeJwt(response.id_token);\n userId = payload.sub || '';\n }\n\n return {\n accessToken: response.access_token,\n tokenType: response.token_type || 'Bearer',\n expiresIn: response.expires_in,\n refreshToken: response.refresh_token,\n idToken: response.id_token,\n scope: response.scope,\n userId,\n };\n }\n\n async authenticate(_credentials: AuthCredentials): Promise<AuthResult> {\n throw new NotImplementedError('authenticate', 'google', {\n reason:\n 'Google only supports authorization code flow. Use getAuthorizationUrl() and exchangeCode() instead.',\n });\n }\n\n async refresh(refreshToken: string): Promise<AuthResult> {\n const discovery = await this.fetchDiscoveryDocument();\n\n const body = new URLSearchParams({\n grant_type: 'refresh_token',\n client_id: this.options.clientId,\n client_secret: this.options.clientSecret!,\n refresh_token: refreshToken,\n });\n\n const response = await this.request<{\n access_token: string;\n refresh_token?: string;\n id_token?: string;\n expires_in: number;\n token_type: string;\n scope?: string;\n }>(discovery.token_endpoint, {\n method: 'POST',\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n body: body.toString(),\n });\n\n // Decode to get user ID\n let userId = '';\n if (response.id_token) {\n const payload = jose.decodeJwt(response.id_token);\n userId = payload.sub || '';\n }\n\n return {\n accessToken: response.access_token,\n tokenType: response.token_type || 'Bearer',\n expiresIn: response.expires_in,\n refreshToken: response.refresh_token || refreshToken,\n idToken: response.id_token,\n scope: response.scope,\n userId,\n };\n }\n\n async logout(options?: LogoutOptions): Promise<void> {\n // Google uses token revocation endpoint\n if (options?.token) {\n try {\n await fetch(\n `https://oauth2.googleapis.com/revoke?token=${options.token}`,\n { method: 'POST' },\n );\n } catch {\n // Ignore revocation errors\n }\n }\n\n if (options?.refreshToken) {\n try {\n await fetch(\n `https://oauth2.googleapis.com/revoke?token=${options.refreshToken}`,\n { method: 'POST' },\n );\n } catch {\n // Ignore revocation errors\n }\n }\n }\n\n // ---------------------------------------------------------------------------\n // TOKEN OPERATIONS\n // ---------------------------------------------------------------------------\n\n async validateToken(\n token: string,\n options?: TokenValidationOptions,\n ): Promise<TokenClaims | null> {\n try {\n const jwks = await this.getJWKS();\n const discovery = await this.fetchDiscoveryDocument();\n\n const verifyOptions: jose.JWTVerifyOptions = {\n issuer: options?.issuer || discovery.issuer,\n clockTolerance: options?.clockTolerance || 0,\n };\n\n // Google tokens can have 'accounts.google.com' or 'https://accounts.google.com' as issuer\n if (!options?.issuer) {\n verifyOptions.issuer = [\n 'https://accounts.google.com',\n 'accounts.google.com',\n ];\n }\n\n if (options?.audience) {\n verifyOptions.audience = options.audience;\n }\n\n const { payload } = await jose.jwtVerify(token, jwks, verifyOptions);\n\n // Check nonce if provided\n if (options?.nonce && payload.nonce !== options.nonce) {\n throw new InvalidNonceError('google');\n }\n\n return {\n sub: payload.sub || '',\n iss: payload.iss || '',\n aud: payload.aud || '',\n exp: payload.exp || 0,\n iat: payload.iat || 0,\n nbf: payload.nbf,\n azp: payload.azp as string | undefined,\n email: payload.email as string | undefined,\n email_verified: payload.email_verified as boolean | undefined,\n name: payload.name as string | undefined,\n picture: payload.picture as string | undefined,\n given_name: payload.given_name as string | undefined,\n family_name: payload.family_name as string | undefined,\n locale: payload.locale as string | undefined,\n ...payload,\n };\n } catch (error) {\n if (error instanceof jose.errors.JWTExpired) {\n throw new TokenExpiredError('google');\n }\n if (error instanceof jose.errors.JWTClaimValidationFailed) {\n return null;\n }\n if (error instanceof jose.errors.JWSSignatureVerificationFailed) {\n throw new InvalidTokenError('Invalid token signature', 'google');\n }\n if (error instanceof InvalidNonceError) {\n throw error;\n }\n return null;\n }\n }\n\n decodeToken(token: string): TokenPayload {\n try {\n const parts = token.split('.');\n if (parts.length !== 3) {\n throw new InvalidTokenError('Invalid JWT format', 'google');\n }\n\n const header = JSON.parse(atob(parts[0]));\n const payload = jose.decodeJwt(token);\n\n return {\n header: {\n alg: header.alg,\n typ: header.typ,\n kid: header.kid,\n },\n payload: payload as unknown as TokenClaims,\n signature: parts[2],\n };\n } catch {\n throw new InvalidTokenError('Failed to decode token', 'google');\n }\n }\n\n async introspectToken(token: string): Promise<TokenIntrospection> {\n // Google doesn't have a public introspection endpoint\n // Fall back to local validation\n const claims = await this.validateToken(token);\n return {\n active: claims !== null,\n claims: claims || undefined,\n };\n }\n\n // ---------------------------------------------------------------------------\n // USER OPERATIONS\n // ---------------------------------------------------------------------------\n\n async getProfile(tokenOrSession: string): Promise<UserProfile> {\n const response = await this.request<{\n sub: string;\n email?: string;\n email_verified?: boolean;\n name?: string;\n given_name?: string;\n family_name?: string;\n picture?: string;\n locale?: string;\n }>(\n 'https://www.googleapis.com/oauth2/v3/userinfo',\n { method: 'GET' },\n tokenOrSession,\n );\n\n return {\n id: response.sub,\n email: response.email,\n emailVerified: response.email_verified,\n firstName: response.given_name,\n lastName: response.family_name,\n displayName: response.name,\n picture: response.picture,\n };\n }\n\n async updateProfile(\n _tokenOrSession: string,\n _profile: Partial<UserProfile>,\n ): Promise<UserProfile> {\n throw new NotImplementedError('updateProfile', 'google', {\n reason: 'Google does not support profile updates via OAuth2',\n });\n }\n\n async getUser(_userId: string, _adminToken?: string): Promise<UserProfile> {\n throw new NotImplementedError('getUser', 'google', {\n reason: 'Google does not expose admin user management',\n });\n }\n\n async createUser(\n _user: CreateUserRequest,\n _adminToken: string,\n ): Promise<UserProfile> {\n throw new NotImplementedError('createUser', 'google', {\n reason: 'Google does not expose user creation',\n });\n }\n\n async updateUser(\n _userId: string,\n _updates: Partial<CreateUserRequest>,\n _adminToken: string,\n ): Promise<UserProfile> {\n throw new NotImplementedError('updateUser', 'google', {\n reason: 'Google does not expose user management',\n });\n }\n\n async deleteUser(_userId: string, _adminToken: string): Promise<void> {\n throw new NotImplementedError('deleteUser', 'google', {\n reason: 'Google does not expose user management',\n });\n }\n\n async listUsers(\n _query: UserQuery,\n _adminToken?: string,\n ): Promise<UserListResult> {\n throw new NotImplementedError('listUsers', 'google', {\n reason: 'Google does not expose user listing',\n });\n }\n\n async requestPasswordReset(_email: string): Promise<void> {\n throw new NotImplementedError('requestPasswordReset', 'google', {\n reason: 'Password management is handled by Google',\n });\n }\n\n async resetPassword(_token: string, _newPassword: string): Promise<void> {\n throw new NotImplementedError('resetPassword', 'google', {\n reason: 'Password management is handled by Google',\n });\n }\n\n // ---------------------------------------------------------------------------\n // SESSION OPERATIONS\n // ---------------------------------------------------------------------------\n\n async listSessions(\n _userId: string,\n _adminToken?: string,\n ): Promise<Session[]> {\n throw new NotImplementedError('listSessions', 'google', {\n reason: 'Session management is handled by Google',\n });\n }\n\n async revokeSession(_sessionId: string, _adminToken?: string): Promise<void> {\n throw new NotImplementedError('revokeSession', 'google', {\n reason: 'Session management is handled by Google',\n });\n }\n\n async revokeAllSessions(\n _userId: string,\n _adminToken?: string,\n ): Promise<void> {\n throw new NotImplementedError('revokeAllSessions', 'google', {\n reason: 'Session management is handled by Google',\n });\n }\n\n // ---------------------------------------------------------------------------\n // AUTHORIZATION\n // ---------------------------------------------------------------------------\n\n async hasRole(_tokenOrUserId: string, _role: string): Promise<boolean> {\n // Google doesn't have a concept of roles in OAuth\n return false;\n }\n\n async hasPermission(\n _tokenOrUserId: string,\n _permission: string,\n _resource?: string,\n ): Promise<boolean> {\n return false;\n }\n\n async getRoles(\n _tokenOrUserId: string,\n _adminToken?: string,\n ): Promise<string[]> {\n return [];\n }\n\n async assignRole(\n _userId: string,\n _role: string,\n _adminToken: string,\n ): Promise<void> {\n throw new NotImplementedError('assignRole', 'google', {\n reason: 'Google does not support role management',\n });\n }\n\n async removeRole(\n _userId: string,\n _role: string,\n _adminToken: string,\n ): Promise<void> {\n throw new NotImplementedError('removeRole', 'google', {\n reason: 'Google does not support role management',\n });\n }\n\n // ---------------------------------------------------------------------------\n // PROVIDER INFORMATION\n // ---------------------------------------------------------------------------\n\n async getCapabilities(): Promise<AuthCapabilities> {\n return {\n authorizationCode: true,\n passwordGrant: false,\n clientCredentials: false,\n tokenRefresh: true,\n oidc: true,\n userManagement: false,\n sessionManagement: false,\n rbac: false,\n passwordReset: false,\n mfa: true, // Google supports 2FA\n socialLogin: true,\n federation: false,\n decentralized: false,\n };\n }\n\n async getDiscoveryDocument(): Promise<OIDCDiscoveryDocument | null> {\n return this.fetchDiscoveryDocument();\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"google-CyESKfaj.js","names":[],"sources":["../../src/shared/providers/google.ts"],"sourcesContent":["/**\n * Google Provider - OAuth2/OIDC Authentication\n *\n * Implements OAuth2/OIDC authentication with Google including:\n * - OIDC Discovery\n * - Authorization Code Flow with PKCE\n * - Token validation using JWKS (RS256)\n * - ID token claims extraction\n */\n\nimport * as jose from 'jose';\n\nimport {\n AccessDeniedError,\n ConfigurationError,\n InvalidClientError,\n InvalidGrantError,\n InvalidNonceError,\n InvalidTokenError,\n NetworkError,\n NotImplementedError,\n ProviderError,\n TokenExpiredError,\n} from '../errors.js';\nimport type {\n AuthCapabilities,\n AuthCredentials,\n AuthInterface,\n AuthorizationOptions,\n AuthorizationResult,\n AuthResult,\n CodeExchangeParams,\n CreateUserRequest,\n GoogleOptions,\n LogoutOptions,\n OIDCDiscoveryDocument,\n Session,\n TokenClaims,\n TokenIntrospection,\n TokenPayload,\n TokenValidationOptions,\n UserListResult,\n UserProfile,\n UserQuery,\n} from '../types.js';\n\n/**\n * Generate a random string for state/nonce/PKCE.\n */\nfunction generateRandomString(length = 32): string {\n const array = new Uint8Array(length);\n crypto.getRandomValues(array);\n return Array.from(array, (byte) => byte.toString(16).padStart(2, '0')).join(\n '',\n );\n}\n\n/**\n * Generate PKCE code verifier and challenge.\n */\nasync function generatePKCE(): Promise<{\n verifier: string;\n challenge: string;\n}> {\n const verifier = generateRandomString(32);\n const encoder = new TextEncoder();\n const data = encoder.encode(verifier);\n const hash = await crypto.subtle.digest('SHA-256', data);\n const challenge = btoa(String.fromCharCode(...new Uint8Array(hash)))\n .replace(/\\+/g, '-')\n .replace(/\\//g, '_')\n .replace(/=+$/, '');\n return { verifier, challenge };\n}\n\n/**\n * Google authentication provider.\n *\n * Implements OAuth2/OIDC authentication with Google.\n * Uses standard OIDC discovery and PKCE for security.\n */\nexport class GoogleProvider implements AuthInterface {\n private options: Required<Pick<GoogleOptions, 'clientId'>> & GoogleOptions;\n private discoveryDocument: OIDCDiscoveryDocument | null = null;\n private jwks: jose.JWTVerifyGetKey | null = null;\n\n private static readonly DISCOVERY_URL =\n 'https://accounts.google.com/.well-known/openid-configuration';\n\n constructor(options: GoogleOptions) {\n if (!options.clientId) {\n throw new ConfigurationError('clientId is required', 'google');\n }\n if (!options.clientSecret) {\n throw new ConfigurationError('clientSecret is required', 'google');\n }\n\n this.options = {\n usePKCE: true,\n scopes: ['openid', 'profile', 'email'],\n timeout: 30000,\n maxRetries: 3,\n ...options,\n };\n }\n\n // ---------------------------------------------------------------------------\n // INTERNAL HELPERS\n // ---------------------------------------------------------------------------\n\n /**\n * Make an HTTP request with error handling.\n */\n private async request<T>(\n url: string,\n options: RequestInit = {},\n token?: string,\n ): Promise<T> {\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n ...this.options.headers,\n ...(options.headers as Record<string, string>),\n };\n\n if (token) {\n headers.Authorization = `Bearer ${token}`;\n }\n\n try {\n const response = await fetch(url, {\n ...options,\n headers,\n signal: AbortSignal.timeout(this.options.timeout || 30000),\n });\n\n if (!response.ok) {\n const errorBody = await response.text().catch(() => '');\n let errorData: Record<string, unknown> = {};\n try {\n errorData = JSON.parse(errorBody);\n } catch {\n // Not JSON\n }\n\n this.handleHttpError(response.status, errorData, errorBody);\n }\n\n const text = await response.text();\n if (!text) return {} as T;\n return JSON.parse(text) as T;\n } catch (error) {\n if (error instanceof Error && error.name === 'TimeoutError') {\n throw new NetworkError('Request timed out', 'google', error);\n }\n if (\n error instanceof AccessDeniedError ||\n error instanceof InvalidGrantError ||\n error instanceof InvalidClientError ||\n error instanceof ProviderError\n ) {\n throw error;\n }\n throw new NetworkError(\n `Network error: ${error instanceof Error ? error.message : 'Unknown error'}`,\n 'google',\n error instanceof Error ? error : undefined,\n );\n }\n }\n\n /**\n * Handle HTTP error responses.\n */\n private handleHttpError(\n status: number,\n data: Record<string, unknown>,\n rawBody: string,\n ): never {\n const error = data.error as string | undefined;\n const errorDescription = data.error_description || data.message || rawBody;\n\n switch (status) {\n case 400:\n if (error === 'invalid_grant') {\n throw new InvalidGrantError(errorDescription as string, 'google');\n }\n if (error === 'invalid_client') {\n throw new InvalidClientError('google');\n }\n throw new ProviderError(`Bad request: ${errorDescription}`, 'google');\n\n case 401:\n throw new InvalidClientError('google');\n\n case 403:\n throw new AccessDeniedError(errorDescription as string, 'google');\n\n default:\n throw new ProviderError(\n `Google error (${status}): ${errorDescription}`,\n 'google',\n );\n }\n }\n\n /**\n * Fetch and cache the OIDC discovery document.\n */\n private async fetchDiscoveryDocument(): Promise<OIDCDiscoveryDocument> {\n if (this.discoveryDocument) {\n return this.discoveryDocument;\n }\n\n this.discoveryDocument = await this.request<OIDCDiscoveryDocument>(\n GoogleProvider.DISCOVERY_URL,\n );\n return this.discoveryDocument;\n }\n\n /**\n * Get JWKS for token validation.\n */\n private async getJWKS(): Promise<jose.JWTVerifyGetKey> {\n if (this.jwks) {\n return this.jwks;\n }\n\n const discovery = await this.fetchDiscoveryDocument();\n this.jwks = jose.createRemoteJWKSet(new URL(discovery.jwks_uri));\n return this.jwks;\n }\n\n // ---------------------------------------------------------------------------\n // AUTHENTICATION FLOWS\n // ---------------------------------------------------------------------------\n\n async getAuthorizationUrl(\n options?: AuthorizationOptions,\n ): Promise<AuthorizationResult> {\n const discovery = await this.fetchDiscoveryDocument();\n\n const state = options?.state || generateRandomString();\n const nonce = options?.nonce || generateRandomString();\n const scopes = options?.scopes ||\n this.options.scopes || ['openid', 'profile', 'email'];\n const redirectUri = options?.redirectUri || this.options.redirectUri;\n\n if (!redirectUri) {\n throw new ConfigurationError('redirectUri is required', 'google');\n }\n\n const params = new URLSearchParams({\n client_id: this.options.clientId,\n redirect_uri: redirectUri,\n response_type: 'code',\n scope: scopes.join(' '),\n state,\n nonce,\n access_type: 'offline', // Request refresh token\n });\n\n if (options?.prompt) {\n params.set('prompt', options.prompt);\n }\n\n if (options?.loginHint) {\n params.set('login_hint', options.loginHint);\n }\n\n // Add extra params\n if (options?.extraParams) {\n for (const [key, value] of Object.entries(options.extraParams)) {\n params.set(key, value);\n }\n }\n\n // PKCE\n let codeVerifier: string | undefined;\n if (this.options.usePKCE !== false) {\n const pkce = await generatePKCE();\n codeVerifier = pkce.verifier;\n params.set('code_challenge', pkce.challenge);\n params.set('code_challenge_method', 'S256');\n }\n\n const url = `${discovery.authorization_endpoint}?${params.toString()}`;\n\n return {\n url,\n state,\n nonce,\n codeVerifier,\n };\n }\n\n async exchangeCode(params: CodeExchangeParams): Promise<AuthResult> {\n const discovery = await this.fetchDiscoveryDocument();\n\n const redirectUri = params.redirectUri || this.options.redirectUri;\n if (!redirectUri) {\n throw new ConfigurationError('redirectUri is required', 'google');\n }\n\n const body = new URLSearchParams({\n grant_type: 'authorization_code',\n client_id: this.options.clientId,\n client_secret: this.options.clientSecret!,\n code: params.code,\n redirect_uri: redirectUri,\n });\n\n // Code verifier for PKCE\n if (params.codeVerifier) {\n body.set('code_verifier', params.codeVerifier);\n }\n\n const response = await this.request<{\n access_token: string;\n refresh_token?: string;\n id_token?: string;\n expires_in: number;\n token_type: string;\n scope?: string;\n }>(discovery.token_endpoint, {\n method: 'POST',\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n body: body.toString(),\n });\n\n // Decode ID token to get user ID\n let userId = '';\n if (response.id_token) {\n const payload = jose.decodeJwt(response.id_token);\n userId = payload.sub || '';\n }\n\n return {\n accessToken: response.access_token,\n tokenType: response.token_type || 'Bearer',\n expiresIn: response.expires_in,\n refreshToken: response.refresh_token,\n idToken: response.id_token,\n scope: response.scope,\n userId,\n };\n }\n\n async authenticate(_credentials: AuthCredentials): Promise<AuthResult> {\n throw new NotImplementedError('authenticate', 'google', {\n reason:\n 'Google only supports authorization code flow. Use getAuthorizationUrl() and exchangeCode() instead.',\n });\n }\n\n async refresh(refreshToken: string): Promise<AuthResult> {\n const discovery = await this.fetchDiscoveryDocument();\n\n const body = new URLSearchParams({\n grant_type: 'refresh_token',\n client_id: this.options.clientId,\n client_secret: this.options.clientSecret!,\n refresh_token: refreshToken,\n });\n\n const response = await this.request<{\n access_token: string;\n refresh_token?: string;\n id_token?: string;\n expires_in: number;\n token_type: string;\n scope?: string;\n }>(discovery.token_endpoint, {\n method: 'POST',\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n body: body.toString(),\n });\n\n // Decode to get user ID\n let userId = '';\n if (response.id_token) {\n const payload = jose.decodeJwt(response.id_token);\n userId = payload.sub || '';\n }\n\n return {\n accessToken: response.access_token,\n tokenType: response.token_type || 'Bearer',\n expiresIn: response.expires_in,\n refreshToken: response.refresh_token || refreshToken,\n idToken: response.id_token,\n scope: response.scope,\n userId,\n };\n }\n\n async logout(options?: LogoutOptions): Promise<void> {\n // Google uses token revocation endpoint\n if (options?.token) {\n try {\n await fetch(\n `https://oauth2.googleapis.com/revoke?token=${options.token}`,\n { method: 'POST' },\n );\n } catch {\n // Ignore revocation errors\n }\n }\n\n if (options?.refreshToken) {\n try {\n await fetch(\n `https://oauth2.googleapis.com/revoke?token=${options.refreshToken}`,\n { method: 'POST' },\n );\n } catch {\n // Ignore revocation errors\n }\n }\n }\n\n // ---------------------------------------------------------------------------\n // TOKEN OPERATIONS\n // ---------------------------------------------------------------------------\n\n async validateToken(\n token: string,\n options?: TokenValidationOptions,\n ): Promise<TokenClaims | null> {\n try {\n const jwks = await this.getJWKS();\n const discovery = await this.fetchDiscoveryDocument();\n\n const verifyOptions: jose.JWTVerifyOptions = {\n issuer: options?.issuer || discovery.issuer,\n clockTolerance: options?.clockTolerance || 0,\n };\n\n // Google tokens can have 'accounts.google.com' or 'https://accounts.google.com' as issuer\n if (!options?.issuer) {\n verifyOptions.issuer = [\n 'https://accounts.google.com',\n 'accounts.google.com',\n ];\n }\n\n if (options?.audience) {\n verifyOptions.audience = options.audience;\n }\n\n const { payload } = await jose.jwtVerify(token, jwks, verifyOptions);\n\n // Check nonce if provided\n if (options?.nonce && payload.nonce !== options.nonce) {\n throw new InvalidNonceError('google');\n }\n\n return {\n sub: payload.sub || '',\n iss: payload.iss || '',\n aud: payload.aud || '',\n exp: payload.exp || 0,\n iat: payload.iat || 0,\n nbf: payload.nbf,\n azp: payload.azp as string | undefined,\n email: payload.email as string | undefined,\n email_verified: payload.email_verified as boolean | undefined,\n name: payload.name as string | undefined,\n picture: payload.picture as string | undefined,\n given_name: payload.given_name as string | undefined,\n family_name: payload.family_name as string | undefined,\n locale: payload.locale as string | undefined,\n ...payload,\n };\n } catch (error) {\n if (error instanceof jose.errors.JWTExpired) {\n throw new TokenExpiredError('google');\n }\n if (error instanceof jose.errors.JWTClaimValidationFailed) {\n return null;\n }\n if (error instanceof jose.errors.JWSSignatureVerificationFailed) {\n throw new InvalidTokenError('Invalid token signature', 'google');\n }\n if (error instanceof InvalidNonceError) {\n throw error;\n }\n return null;\n }\n }\n\n decodeToken(token: string): TokenPayload {\n try {\n const parts = token.split('.');\n if (parts.length !== 3) {\n throw new InvalidTokenError('Invalid JWT format', 'google');\n }\n\n const header = JSON.parse(atob(parts[0]));\n const payload = jose.decodeJwt(token);\n\n return {\n header: {\n alg: header.alg,\n typ: header.typ,\n kid: header.kid,\n },\n payload: payload as unknown as TokenClaims,\n signature: parts[2],\n };\n } catch {\n throw new InvalidTokenError('Failed to decode token', 'google');\n }\n }\n\n async introspectToken(token: string): Promise<TokenIntrospection> {\n // Google doesn't have a public introspection endpoint\n // Fall back to local validation\n const claims = await this.validateToken(token);\n return {\n active: claims !== null,\n claims: claims || undefined,\n };\n }\n\n // ---------------------------------------------------------------------------\n // USER OPERATIONS\n // ---------------------------------------------------------------------------\n\n async getProfile(tokenOrSession: string): Promise<UserProfile> {\n const response = await this.request<{\n sub: string;\n email?: string;\n email_verified?: boolean;\n name?: string;\n given_name?: string;\n family_name?: string;\n picture?: string;\n locale?: string;\n }>(\n 'https://www.googleapis.com/oauth2/v3/userinfo',\n { method: 'GET' },\n tokenOrSession,\n );\n\n return {\n id: response.sub,\n email: response.email,\n emailVerified: response.email_verified,\n firstName: response.given_name,\n lastName: response.family_name,\n displayName: response.name,\n picture: response.picture,\n };\n }\n\n async updateProfile(\n _tokenOrSession: string,\n _profile: Partial<UserProfile>,\n ): Promise<UserProfile> {\n throw new NotImplementedError('updateProfile', 'google', {\n reason: 'Google does not support profile updates via OAuth2',\n });\n }\n\n async getUser(_userId: string, _adminToken?: string): Promise<UserProfile> {\n throw new NotImplementedError('getUser', 'google', {\n reason: 'Google does not expose admin user management',\n });\n }\n\n async createUser(\n _user: CreateUserRequest,\n _adminToken: string,\n ): Promise<UserProfile> {\n throw new NotImplementedError('createUser', 'google', {\n reason: 'Google does not expose user creation',\n });\n }\n\n async updateUser(\n _userId: string,\n _updates: Partial<CreateUserRequest>,\n _adminToken: string,\n ): Promise<UserProfile> {\n throw new NotImplementedError('updateUser', 'google', {\n reason: 'Google does not expose user management',\n });\n }\n\n async deleteUser(_userId: string, _adminToken: string): Promise<void> {\n throw new NotImplementedError('deleteUser', 'google', {\n reason: 'Google does not expose user management',\n });\n }\n\n async listUsers(\n _query: UserQuery,\n _adminToken?: string,\n ): Promise<UserListResult> {\n throw new NotImplementedError('listUsers', 'google', {\n reason: 'Google does not expose user listing',\n });\n }\n\n async requestPasswordReset(_email: string): Promise<void> {\n throw new NotImplementedError('requestPasswordReset', 'google', {\n reason: 'Password management is handled by Google',\n });\n }\n\n async resetPassword(_token: string, _newPassword: string): Promise<void> {\n throw new NotImplementedError('resetPassword', 'google', {\n reason: 'Password management is handled by Google',\n });\n }\n\n // ---------------------------------------------------------------------------\n // SESSION OPERATIONS\n // ---------------------------------------------------------------------------\n\n async listSessions(\n _userId: string,\n _adminToken?: string,\n ): Promise<Session[]> {\n throw new NotImplementedError('listSessions', 'google', {\n reason: 'Session management is handled by Google',\n });\n }\n\n async revokeSession(_sessionId: string, _adminToken?: string): Promise<void> {\n throw new NotImplementedError('revokeSession', 'google', {\n reason: 'Session management is handled by Google',\n });\n }\n\n async revokeAllSessions(\n _userId: string,\n _adminToken?: string,\n ): Promise<void> {\n throw new NotImplementedError('revokeAllSessions', 'google', {\n reason: 'Session management is handled by Google',\n });\n }\n\n // ---------------------------------------------------------------------------\n // AUTHORIZATION\n // ---------------------------------------------------------------------------\n\n async hasRole(_tokenOrUserId: string, _role: string): Promise<boolean> {\n // Google doesn't have a concept of roles in OAuth\n return false;\n }\n\n async hasPermission(\n _tokenOrUserId: string,\n _permission: string,\n _resource?: string,\n ): Promise<boolean> {\n return false;\n }\n\n async getRoles(\n _tokenOrUserId: string,\n _adminToken?: string,\n ): Promise<string[]> {\n return [];\n }\n\n async assignRole(\n _userId: string,\n _role: string,\n _adminToken: string,\n ): Promise<void> {\n throw new NotImplementedError('assignRole', 'google', {\n reason: 'Google does not support role management',\n });\n }\n\n async removeRole(\n _userId: string,\n _role: string,\n _adminToken: string,\n ): Promise<void> {\n throw new NotImplementedError('removeRole', 'google', {\n reason: 'Google does not support role management',\n });\n }\n\n // ---------------------------------------------------------------------------\n // PROVIDER INFORMATION\n // ---------------------------------------------------------------------------\n\n async getCapabilities(): Promise<AuthCapabilities> {\n return {\n authorizationCode: true,\n passwordGrant: false,\n clientCredentials: false,\n tokenRefresh: true,\n oidc: true,\n userManagement: false,\n sessionManagement: false,\n rbac: false,\n passwordReset: false,\n mfa: true, // Google supports 2FA\n socialLogin: true,\n federation: false,\n decentralized: false,\n };\n }\n\n async getDiscoveryDocument(): Promise<OIDCDiscoveryDocument | null> {\n return this.fetchDiscoveryDocument();\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { E as UserAlreadyExistsError, O as UserNotFoundError, S as ProviderError, T as TokenExpiredError, a as ConfigurationError, b as NetworkError, c as InvalidClientError, l as InvalidCredentialsError, p as InvalidNonceError, t as AccessDeniedError, u as InvalidGrantError, v as InvalidTokenError, x as NotImplementedError } from "./errors-RgVH84_1.js";
|
|
2
|
-
import {
|
|
3
|
-
import { t as
|
|
2
|
+
import { b as JWTClaimValidationFailed, t as jwtVerify, x as JWTExpired, y as JWSSignatureVerificationFailed } from "./verify-CGYWoFss.js";
|
|
3
|
+
import { t as createRemoteJWKSet } from "./remote-D3qRdbiU.js";
|
|
4
|
+
import { t as decodeJwt } from "./decode_jwt-ob-gfrt8.js";
|
|
4
5
|
//#region src/shared/providers/kanidm.ts
|
|
5
6
|
/**
|
|
6
7
|
* Kanidm Provider - OAuth2/OIDC Authentication
|
|
@@ -565,4 +566,4 @@ var KanidmProvider = class {
|
|
|
565
566
|
//#endregion
|
|
566
567
|
export { KanidmProvider };
|
|
567
568
|
|
|
568
|
-
//# sourceMappingURL=kanidm-
|
|
569
|
+
//# sourceMappingURL=kanidm-XUwRGxXX.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"kanidm-YfZNisdt.js","names":[],"sources":["../../src/shared/providers/kanidm.ts"],"sourcesContent":["/**\n * Kanidm Provider - OAuth2/OIDC Authentication\n *\n * Implements OAuth2/OIDC authentication with Kanidm server including:\n * - OIDC Discovery (client-specific endpoints)\n * - Authorization Code Flow with PKCE (required by Kanidm)\n * - Token validation using JWKS (ES256)\n * - Token introspection\n * - User management via Kanidm's native /v1/ API\n */\n\nimport * as jose from 'jose';\n\nimport {\n AccessDeniedError,\n ConfigurationError,\n InvalidClientError,\n InvalidCredentialsError,\n InvalidGrantError,\n InvalidNonceError,\n InvalidTokenError,\n NetworkError,\n NotImplementedError,\n ProviderError,\n TokenExpiredError,\n UserAlreadyExistsError,\n UserNotFoundError,\n} from '../errors.js';\nimport type {\n AuthCapabilities,\n AuthCredentials,\n AuthInterface,\n AuthorizationOptions,\n AuthorizationResult,\n AuthResult,\n CodeExchangeParams,\n CreateUserRequest,\n KanidmOptions,\n LogoutOptions,\n OIDCDiscoveryDocument,\n Session,\n TokenClaims,\n TokenIntrospection,\n TokenPayload,\n TokenValidationOptions,\n UserListResult,\n UserProfile,\n UserQuery,\n} from '../types.js';\n\n/**\n * Generate a random string for state/nonce/PKCE.\n */\nfunction generateRandomString(length = 32): string {\n const array = new Uint8Array(length);\n crypto.getRandomValues(array);\n return Array.from(array, (byte) => byte.toString(16).padStart(2, '0')).join(\n '',\n );\n}\n\n/**\n * Generate PKCE code verifier and challenge.\n */\nasync function generatePKCE(): Promise<{\n verifier: string;\n challenge: string;\n}> {\n const verifier = generateRandomString(32);\n const encoder = new TextEncoder();\n const data = encoder.encode(verifier);\n const hash = await crypto.subtle.digest('SHA-256', data);\n const challenge = btoa(String.fromCharCode(...new Uint8Array(hash)))\n .replace(/\\+/g, '-')\n .replace(/\\//g, '_')\n .replace(/=+$/, '');\n return { verifier, challenge };\n}\n\n/**\n * Kanidm authentication provider.\n *\n * Implements OAuth2/OIDC authentication with Kanidm server.\n * Key differences from Keycloak:\n * - Client-specific OIDC endpoints: /oauth2/openid/{clientId}/...\n * - ES256 token signing (not RS256)\n * - Authorization code flow only (no password grant)\n * - Native /v1/ API for admin operations (multi-step auth)\n */\nexport class KanidmProvider implements AuthInterface {\n private options: Required<Pick<KanidmOptions, 'serverUrl' | 'clientId'>> &\n KanidmOptions;\n private discoveryDocument: OIDCDiscoveryDocument | null = null;\n private jwks: jose.JWTVerifyGetKey | null = null;\n private adminToken: string | null = null;\n private adminTokenExpiry: number = 0;\n\n constructor(options: KanidmOptions) {\n if (!options.serverUrl) {\n throw new ConfigurationError('serverUrl is required', 'kanidm');\n }\n if (!options.clientId) {\n throw new ConfigurationError('clientId is required', 'kanidm');\n }\n\n this.options = {\n usePKCE: true, // Required by Kanidm\n verifySsl: true,\n scopes: ['openid', 'profile', 'email', 'groups'],\n timeout: 30000,\n maxRetries: 3,\n ...options,\n };\n }\n\n // ---------------------------------------------------------------------------\n // INTERNAL HELPERS\n // ---------------------------------------------------------------------------\n\n /**\n * Get the OIDC base URL for this client.\n * Kanidm uses client-specific OIDC endpoints.\n */\n private getOidcBaseUrl(): string {\n return `${this.options.serverUrl}/oauth2/openid/${this.options.clientId}`;\n }\n\n /**\n * Get the native API base URL.\n */\n private getApiBaseUrl(): string {\n return `${this.options.serverUrl}/v1`;\n }\n\n /**\n * Make an HTTP request with error handling.\n */\n private async request<T>(\n url: string,\n options: RequestInit = {},\n token?: string,\n ): Promise<T> {\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n ...this.options.headers,\n ...(options.headers as Record<string, string>),\n };\n\n if (token) {\n headers.Authorization = `Bearer ${token}`;\n }\n\n try {\n const response = await fetch(url, {\n ...options,\n headers,\n signal: AbortSignal.timeout(this.options.timeout || 30000),\n });\n\n if (!response.ok) {\n const errorBody = await response.text().catch(() => '');\n let errorData: Record<string, unknown> = {};\n try {\n errorData = JSON.parse(errorBody);\n } catch {\n // Not JSON\n }\n\n this.handleHttpError(response.status, errorData, errorBody);\n }\n\n const text = await response.text();\n if (!text) return {} as T;\n return JSON.parse(text) as T;\n } catch (error) {\n if (error instanceof Error && error.name === 'TimeoutError') {\n throw new NetworkError('Request timed out', 'kanidm', error);\n }\n if (\n error instanceof InvalidCredentialsError ||\n error instanceof AccessDeniedError ||\n error instanceof InvalidGrantError ||\n error instanceof InvalidClientError ||\n error instanceof UserNotFoundError ||\n error instanceof ProviderError\n ) {\n throw error;\n }\n throw new NetworkError(\n `Network error: ${error instanceof Error ? error.message : 'Unknown error'}`,\n 'kanidm',\n error instanceof Error ? error : undefined,\n );\n }\n }\n\n /**\n * Handle HTTP error responses.\n */\n private handleHttpError(\n status: number,\n data: Record<string, unknown>,\n rawBody: string,\n ): never {\n const error = data.error as string | undefined;\n const errorDescription =\n data.message || data.error_description || data.errorMessage || rawBody;\n\n switch (status) {\n case 400:\n if (error === 'invalid_grant') {\n throw new InvalidGrantError(errorDescription as string, 'kanidm');\n }\n if (error === 'invalid_client') {\n throw new InvalidClientError('kanidm');\n }\n throw new ProviderError(`Bad request: ${errorDescription}`, 'kanidm');\n\n case 401:\n throw new InvalidCredentialsError('kanidm');\n\n case 403:\n throw new AccessDeniedError(errorDescription as string, 'kanidm');\n\n case 404:\n throw new UserNotFoundError(undefined, 'kanidm');\n\n case 409:\n throw new UserAlreadyExistsError(undefined, 'kanidm');\n\n default:\n throw new ProviderError(\n `Kanidm error (${status}): ${errorDescription}`,\n 'kanidm',\n );\n }\n }\n\n /**\n * Fetch and cache the OIDC discovery document.\n */\n private async fetchDiscoveryDocument(): Promise<OIDCDiscoveryDocument> {\n if (this.discoveryDocument) {\n return this.discoveryDocument;\n }\n\n const url = `${this.getOidcBaseUrl()}/.well-known/openid-configuration`;\n this.discoveryDocument = await this.request<OIDCDiscoveryDocument>(url);\n return this.discoveryDocument;\n }\n\n /**\n * Get JWKS for token validation.\n */\n private async getJWKS(): Promise<jose.JWTVerifyGetKey> {\n if (this.jwks) {\n return this.jwks;\n }\n\n const discovery = await this.fetchDiscoveryDocument();\n this.jwks = jose.createRemoteJWKSet(new URL(discovery.jwks_uri));\n return this.jwks;\n }\n\n /**\n * Authenticate with Kanidm's native API to get an admin token.\n * Uses the multi-step authentication flow.\n */\n private async getAdminToken(): Promise<string> {\n // Check if we have a valid cached token\n if (this.adminToken && Date.now() < this.adminTokenExpiry) {\n return this.adminToken;\n }\n\n if (!this.options.adminUsername || !this.options.adminPassword) {\n throw new ConfigurationError(\n 'adminUsername and adminPassword are required for admin operations',\n 'kanidm',\n );\n }\n\n const authUrl = `${this.getApiBaseUrl()}/auth`;\n\n // Step 1: Initialize auth\n const initResponse = await fetch(authUrl, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n step: {\n init2: {\n username: this.options.adminUsername,\n issue: 'token',\n privileged: true,\n },\n },\n }),\n signal: AbortSignal.timeout(this.options.timeout || 30000),\n });\n\n if (!initResponse.ok) {\n throw new InvalidCredentialsError('kanidm', {\n reason: 'Failed to initialize admin authentication',\n });\n }\n\n // Get session cookie from response\n const cookies = initResponse.headers.get('set-cookie');\n\n // Step 2: Begin password authentication\n const beginResponse = await fetch(authUrl, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n ...(cookies ? { Cookie: cookies } : {}),\n },\n body: JSON.stringify({\n step: {\n begin: 'password',\n },\n }),\n signal: AbortSignal.timeout(this.options.timeout || 30000),\n });\n\n if (!beginResponse.ok) {\n throw new InvalidCredentialsError('kanidm', {\n reason: 'Failed to begin password authentication',\n });\n }\n\n // Step 3: Provide password\n const credResponse = await fetch(authUrl, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n ...(cookies ? { Cookie: cookies } : {}),\n },\n body: JSON.stringify({\n step: {\n cred: {\n password: this.options.adminPassword,\n },\n },\n }),\n signal: AbortSignal.timeout(this.options.timeout || 30000),\n });\n\n if (!credResponse.ok) {\n throw new InvalidCredentialsError('kanidm', {\n reason: 'Invalid admin credentials',\n });\n }\n\n const result = (await credResponse.json()) as {\n state?: { success?: string };\n token?: string;\n };\n\n // Extract token from response\n const token = result.state?.success || result.token;\n if (!token) {\n throw new ProviderError(\n 'Failed to obtain admin token from Kanidm',\n 'kanidm',\n );\n }\n\n this.adminToken = token;\n // Cache for 1 hour (Kanidm tokens typically last longer, but this is safe)\n this.adminTokenExpiry = Date.now() + 3600000;\n\n return this.adminToken;\n }\n\n // ---------------------------------------------------------------------------\n // AUTHENTICATION FLOWS\n // ---------------------------------------------------------------------------\n\n async getAuthorizationUrl(\n options?: AuthorizationOptions,\n ): Promise<AuthorizationResult> {\n const discovery = await this.fetchDiscoveryDocument();\n\n const state = options?.state || generateRandomString();\n const nonce = options?.nonce || generateRandomString();\n const scopes = options?.scopes ||\n this.options.scopes || ['openid', 'profile', 'email', 'groups'];\n const redirectUri = options?.redirectUri || this.options.redirectUri;\n\n if (!redirectUri) {\n throw new ConfigurationError('redirectUri is required', 'kanidm');\n }\n\n const params = new URLSearchParams({\n client_id: this.options.clientId,\n redirect_uri: redirectUri,\n response_type: 'code',\n scope: scopes.join(' '),\n state,\n nonce,\n });\n\n if (options?.prompt) {\n params.set('prompt', options.prompt);\n }\n\n if (options?.loginHint) {\n params.set('login_hint', options.loginHint);\n }\n\n // Add extra params\n if (options?.extraParams) {\n for (const [key, value] of Object.entries(options.extraParams)) {\n params.set(key, value);\n }\n }\n\n // PKCE is required by Kanidm\n const pkce = await generatePKCE();\n params.set('code_challenge', pkce.challenge);\n params.set('code_challenge_method', 'S256');\n\n const url = `${discovery.authorization_endpoint}?${params.toString()}`;\n\n return {\n url,\n state,\n nonce,\n codeVerifier: pkce.verifier,\n };\n }\n\n async exchangeCode(params: CodeExchangeParams): Promise<AuthResult> {\n const discovery = await this.fetchDiscoveryDocument();\n\n const redirectUri = params.redirectUri || this.options.redirectUri;\n if (!redirectUri) {\n throw new ConfigurationError('redirectUri is required', 'kanidm');\n }\n\n const body = new URLSearchParams({\n grant_type: 'authorization_code',\n client_id: this.options.clientId,\n code: params.code,\n redirect_uri: redirectUri,\n });\n\n if (this.options.clientSecret) {\n body.set('client_secret', this.options.clientSecret);\n }\n\n // Code verifier is required for PKCE\n if (params.codeVerifier) {\n body.set('code_verifier', params.codeVerifier);\n }\n\n const response = await this.request<{\n access_token: string;\n refresh_token?: string;\n id_token?: string;\n expires_in: number;\n token_type: string;\n scope?: string;\n }>(discovery.token_endpoint, {\n method: 'POST',\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n body: body.toString(),\n });\n\n // Decode ID token to get user ID\n let userId = '';\n if (response.id_token) {\n const payload = jose.decodeJwt(response.id_token);\n userId = payload.sub || '';\n }\n\n return {\n accessToken: response.access_token,\n tokenType: response.token_type || 'Bearer',\n expiresIn: response.expires_in,\n refreshToken: response.refresh_token,\n idToken: response.id_token,\n scope: response.scope,\n userId,\n };\n }\n\n async authenticate(_credentials: AuthCredentials): Promise<AuthResult> {\n // Kanidm only supports authorization_code grant for OAuth2\n // Direct authentication is not supported via OAuth2\n throw new NotImplementedError('authenticate', 'kanidm', {\n reason:\n 'Kanidm only supports authorization code flow. Use getAuthorizationUrl() and exchangeCode() instead.',\n });\n }\n\n async refresh(refreshToken: string): Promise<AuthResult> {\n const discovery = await this.fetchDiscoveryDocument();\n\n const body = new URLSearchParams({\n grant_type: 'refresh_token',\n client_id: this.options.clientId,\n refresh_token: refreshToken,\n });\n\n if (this.options.clientSecret) {\n body.set('client_secret', this.options.clientSecret);\n }\n\n const response = await this.request<{\n access_token: string;\n refresh_token?: string;\n id_token?: string;\n expires_in: number;\n token_type: string;\n scope?: string;\n }>(discovery.token_endpoint, {\n method: 'POST',\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n body: body.toString(),\n });\n\n // Decode to get user ID\n let userId = '';\n if (response.access_token) {\n const payload = jose.decodeJwt(response.access_token);\n userId = payload.sub || '';\n }\n\n return {\n accessToken: response.access_token,\n tokenType: response.token_type || 'Bearer',\n expiresIn: response.expires_in,\n refreshToken: response.refresh_token || refreshToken,\n idToken: response.id_token,\n scope: response.scope,\n userId,\n };\n }\n\n async logout(options?: LogoutOptions): Promise<void> {\n const discovery = await this.fetchDiscoveryDocument();\n\n // Revoke refresh token if provided\n // Silently ignore failures - invalid tokens are effectively already revoked\n if (options?.refreshToken && discovery.revocation_endpoint) {\n const body = new URLSearchParams({\n client_id: this.options.clientId,\n token: options.refreshToken,\n token_type_hint: 'refresh_token',\n });\n\n if (this.options.clientSecret) {\n body.set('client_secret', this.options.clientSecret);\n }\n\n try {\n await this.request(discovery.revocation_endpoint, {\n method: 'POST',\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n body: body.toString(),\n });\n } catch {\n // Ignore revocation errors - token may already be invalid/revoked\n }\n }\n\n // Revoke access token if provided\n // Silently ignore failures - invalid tokens are effectively already revoked\n if (options?.token && discovery.revocation_endpoint) {\n const body = new URLSearchParams({\n client_id: this.options.clientId,\n token: options.token,\n token_type_hint: 'access_token',\n });\n\n if (this.options.clientSecret) {\n body.set('client_secret', this.options.clientSecret);\n }\n\n try {\n await this.request(discovery.revocation_endpoint, {\n method: 'POST',\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n body: body.toString(),\n });\n } catch {\n // Ignore revocation errors - token may already be invalid/revoked\n }\n }\n }\n\n // ---------------------------------------------------------------------------\n // TOKEN OPERATIONS\n // ---------------------------------------------------------------------------\n\n async validateToken(\n token: string,\n options?: TokenValidationOptions,\n ): Promise<TokenClaims | null> {\n try {\n const jwks = await this.getJWKS();\n const discovery = await this.fetchDiscoveryDocument();\n\n const verifyOptions: jose.JWTVerifyOptions = {\n issuer: options?.issuer || discovery.issuer,\n clockTolerance: options?.clockTolerance || 0,\n };\n\n if (options?.audience) {\n verifyOptions.audience = options.audience;\n }\n\n const { payload } = await jose.jwtVerify(token, jwks, verifyOptions);\n\n // Check nonce if provided\n if (options?.nonce && payload.nonce !== options.nonce) {\n throw new InvalidNonceError('kanidm');\n }\n\n // Extract groups from Kanidm token structure\n // Kanidm uses 'groups' claim directly\n const groups = (payload.groups as string[]) || [];\n\n return {\n sub: payload.sub || '',\n iss: payload.iss || '',\n aud: payload.aud || '',\n exp: payload.exp || 0,\n iat: payload.iat || 0,\n nbf: payload.nbf,\n azp: payload.azp as string | undefined,\n email: payload.email as string | undefined,\n email_verified: payload.email_verified as boolean | undefined,\n preferred_username: payload.preferred_username as string | undefined,\n name: payload.name as string | undefined,\n roles: groups, // Map groups to roles for consistency\n ...payload,\n };\n } catch (error) {\n if (error instanceof jose.errors.JWTExpired) {\n throw new TokenExpiredError('kanidm');\n }\n if (error instanceof jose.errors.JWTClaimValidationFailed) {\n return null;\n }\n if (error instanceof jose.errors.JWSSignatureVerificationFailed) {\n throw new InvalidTokenError('Invalid token signature', 'kanidm');\n }\n if (error instanceof InvalidNonceError) {\n throw error;\n }\n return null;\n }\n }\n\n decodeToken(token: string): TokenPayload {\n try {\n const parts = token.split('.');\n if (parts.length !== 3) {\n throw new InvalidTokenError('Invalid JWT format', 'kanidm');\n }\n\n const header = JSON.parse(atob(parts[0]));\n const payload = jose.decodeJwt(token);\n\n return {\n header: {\n alg: header.alg,\n typ: header.typ,\n kid: header.kid,\n },\n payload: payload as unknown as TokenClaims,\n signature: parts[2],\n };\n } catch {\n throw new InvalidTokenError('Failed to decode token', 'kanidm');\n }\n }\n\n async introspectToken(token: string): Promise<TokenIntrospection> {\n const discovery = await this.fetchDiscoveryDocument();\n\n if (!discovery.introspection_endpoint) {\n // Fallback to local validation\n const claims = await this.validateToken(token);\n return {\n active: claims !== null,\n claims: claims || undefined,\n };\n }\n\n const body = new URLSearchParams({\n client_id: this.options.clientId,\n token,\n });\n\n if (this.options.clientSecret) {\n body.set('client_secret', this.options.clientSecret);\n }\n\n const response = await this.request<{\n active: boolean;\n sub?: string;\n iss?: string;\n aud?: string | string[];\n exp?: number;\n iat?: number;\n client_id?: string;\n scope?: string;\n token_type?: string;\n [key: string]: unknown;\n }>(discovery.introspection_endpoint, {\n method: 'POST',\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n body: body.toString(),\n });\n\n if (!response.active) {\n return { active: false };\n }\n\n return {\n active: true,\n claims: {\n sub: response.sub || '',\n iss: response.iss || '',\n aud: response.aud || '',\n exp: response.exp || 0,\n iat: response.iat || 0,\n ...response,\n },\n tokenType: response.token_type,\n clientId: response.client_id,\n scope: response.scope,\n };\n }\n\n // ---------------------------------------------------------------------------\n // USER OPERATIONS\n // ---------------------------------------------------------------------------\n\n async getProfile(tokenOrSession: string): Promise<UserProfile> {\n const discovery = await this.fetchDiscoveryDocument();\n\n const response = await this.request<{\n sub: string;\n preferred_username?: string;\n email?: string;\n email_verified?: boolean;\n name?: string;\n given_name?: string;\n family_name?: string;\n picture?: string;\n groups?: string[];\n [key: string]: unknown;\n }>(discovery.userinfo_endpoint, {\n method: 'GET',\n headers: { Authorization: `Bearer ${tokenOrSession}` },\n });\n\n return {\n id: response.sub,\n username: response.preferred_username,\n email: response.email,\n emailVerified: response.email_verified,\n firstName: response.given_name,\n lastName: response.family_name,\n displayName: response.name,\n picture: response.picture,\n groups: response.groups,\n };\n }\n\n async updateProfile(\n _tokenOrSession: string,\n _profile: Partial<UserProfile>,\n ): Promise<UserProfile> {\n // Kanidm doesn't expose profile update via OAuth2/OIDC\n throw new NotImplementedError('updateProfile', 'kanidm', {\n reason:\n 'Profile updates are not supported via OAuth2. Use admin API with createUser/updateUser.',\n });\n }\n\n async getUser(userId: string, _adminToken?: string): Promise<UserProfile> {\n const token = await this.getAdminToken();\n\n const response = await this.request<KanidmPerson>(\n `${this.getApiBaseUrl()}/person/${userId}`,\n { method: 'GET' },\n token,\n );\n\n return this.mapKanidmPerson(response);\n }\n\n async createUser(\n user: CreateUserRequest,\n _adminToken: string,\n ): Promise<UserProfile> {\n const token = await this.getAdminToken();\n\n const kanidmPerson: Partial<KanidmPersonCreate> = {\n attrs: {\n name: [user.username],\n displayname: [\n user.firstName && user.lastName\n ? `${user.firstName} ${user.lastName}`\n : user.username,\n ],\n },\n };\n\n if (user.email && kanidmPerson.attrs) {\n kanidmPerson.attrs.mail = [user.email];\n }\n\n const response = await fetch(`${this.getApiBaseUrl()}/person`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${token}`,\n },\n body: JSON.stringify(kanidmPerson),\n });\n\n if (!response.ok) {\n const errorBody = await response.text();\n if (response.status === 409) {\n throw new UserAlreadyExistsError(user.username, 'kanidm');\n }\n throw new ProviderError(`Failed to create user: ${errorBody}`, 'kanidm');\n }\n\n // Fetch the created user\n return this.getUser(user.username, token);\n }\n\n async updateUser(\n userId: string,\n updates: Partial<CreateUserRequest>,\n _adminToken: string,\n ): Promise<UserProfile> {\n const token = await this.getAdminToken();\n\n const attrs: Record<string, string[]> = {};\n\n if (updates.email !== undefined) {\n attrs.mail = [updates.email];\n }\n\n if (updates.firstName !== undefined || updates.lastName !== undefined) {\n const displayName =\n updates.firstName && updates.lastName\n ? `${updates.firstName} ${updates.lastName}`\n : updates.firstName || updates.lastName || '';\n if (displayName) {\n attrs.displayname = [displayName];\n }\n }\n\n await this.request(\n `${this.getApiBaseUrl()}/person/${userId}`,\n {\n method: 'PATCH',\n body: JSON.stringify({ attrs }),\n },\n token,\n );\n\n return this.getUser(userId, token);\n }\n\n async deleteUser(userId: string, _adminToken: string): Promise<void> {\n const token = await this.getAdminToken();\n\n await this.request(\n `${this.getApiBaseUrl()}/person/${userId}`,\n { method: 'DELETE' },\n token,\n );\n }\n\n async listUsers(\n query: UserQuery,\n _adminToken?: string,\n ): Promise<UserListResult> {\n const token = await this.getAdminToken();\n\n // Kanidm uses a different query format\n let url = `${this.getApiBaseUrl()}/person`;\n\n // Add search filter if provided\n // Note: Kanidm API may have different filtering capabilities\n const params = new URLSearchParams();\n if (query.search) {\n params.set('search', query.search);\n }\n\n if (params.toString()) {\n url += `?${params.toString()}`;\n }\n\n const response = await this.request<KanidmPerson[]>(\n url,\n { method: 'GET' },\n token,\n );\n\n const users = Array.isArray(response) ? response : [];\n\n // Apply client-side filtering and pagination\n let filteredUsers = users.map((u) => this.mapKanidmPerson(u));\n\n if (query.email) {\n filteredUsers = filteredUsers.filter((u) => u.email === query.email);\n }\n\n if (query.username) {\n filteredUsers = filteredUsers.filter(\n (u) => u.username === query.username,\n );\n }\n\n const total = filteredUsers.length;\n const offset = query.offset || 0;\n const limit = query.limit || 100;\n\n return {\n users: filteredUsers.slice(offset, offset + limit),\n total,\n limit,\n offset,\n };\n }\n\n async requestPasswordReset(_email: string): Promise<void> {\n throw new NotImplementedError('requestPasswordReset', 'kanidm', {\n reason: 'Password reset is not exposed via Kanidm API',\n });\n }\n\n async resetPassword(_token: string, _newPassword: string): Promise<void> {\n throw new NotImplementedError('resetPassword', 'kanidm', {\n reason: 'Password reset is not exposed via Kanidm API',\n });\n }\n\n // ---------------------------------------------------------------------------\n // SESSION OPERATIONS\n // ---------------------------------------------------------------------------\n\n async listSessions(\n _userId: string,\n _adminToken?: string,\n ): Promise<Session[]> {\n throw new NotImplementedError('listSessions', 'kanidm', {\n reason: 'Session management is not exposed via Kanidm API',\n });\n }\n\n async revokeSession(_sessionId: string, _adminToken?: string): Promise<void> {\n throw new NotImplementedError('revokeSession', 'kanidm', {\n reason: 'Session management is not exposed via Kanidm API',\n });\n }\n\n async revokeAllSessions(\n _userId: string,\n _adminToken?: string,\n ): Promise<void> {\n throw new NotImplementedError('revokeAllSessions', 'kanidm', {\n reason: 'Session management is not exposed via Kanidm API',\n });\n }\n\n // ---------------------------------------------------------------------------\n // AUTHORIZATION\n // ---------------------------------------------------------------------------\n\n async hasRole(tokenOrUserId: string, role: string): Promise<boolean> {\n const roles = await this.getRoles(tokenOrUserId);\n return roles.includes(role);\n }\n\n async hasPermission(\n tokenOrUserId: string,\n permission: string,\n resource?: string,\n ): Promise<boolean> {\n // Check if the user has a group matching the permission\n const roles = await this.getRoles(tokenOrUserId);\n const permissionRole = resource ? `${resource}:${permission}` : permission;\n return roles.includes(permissionRole) || roles.includes(permission);\n }\n\n async getRoles(\n tokenOrUserId: string,\n _adminToken?: string,\n ): Promise<string[]> {\n // First try to decode as JWT\n try {\n const claims = await this.validateToken(tokenOrUserId);\n if (claims) {\n return claims.roles || [];\n }\n } catch {\n // Not a valid token\n }\n\n // Try to get user and return their groups\n try {\n const user = await this.getUser(tokenOrUserId);\n return user.groups || [];\n } catch {\n // User not found or other error\n }\n\n return [];\n }\n\n async assignRole(\n _userId: string,\n _role: string,\n _adminToken: string,\n ): Promise<void> {\n throw new NotImplementedError('assignRole', 'kanidm', {\n reason:\n 'Role assignment via API requires group management. Use Kanidm CLI to add users to groups.',\n });\n }\n\n async removeRole(\n _userId: string,\n _role: string,\n _adminToken: string,\n ): Promise<void> {\n throw new NotImplementedError('removeRole', 'kanidm', {\n reason:\n 'Role removal via API requires group management. Use Kanidm CLI to remove users from groups.',\n });\n }\n\n // ---------------------------------------------------------------------------\n // PROVIDER INFORMATION\n // ---------------------------------------------------------------------------\n\n async getCapabilities(): Promise<AuthCapabilities> {\n return {\n authorizationCode: true,\n passwordGrant: false, // Not supported\n clientCredentials: false, // Not supported\n tokenRefresh: true,\n oidc: true,\n userManagement: true, // Via /v1/person API\n sessionManagement: false, // Not exposed\n rbac: true, // Via groups claim\n passwordReset: false, // Not exposed via API\n mfa: true, // Webauthn/passkeys supported\n socialLogin: false,\n federation: true,\n decentralized: false,\n };\n }\n\n async getDiscoveryDocument(): Promise<OIDCDiscoveryDocument | null> {\n return this.fetchDiscoveryDocument();\n }\n\n // ---------------------------------------------------------------------------\n // PRIVATE HELPERS\n // ---------------------------------------------------------------------------\n\n private mapKanidmPerson(person: KanidmPerson): UserProfile {\n const attrs = person.attrs || {};\n\n return {\n id: attrs.uuid?.[0] || attrs.name?.[0] || '',\n username: attrs.name?.[0],\n email: attrs.mail?.[0],\n displayName: attrs.displayname?.[0],\n groups: attrs.memberof,\n enabled: true, // Kanidm doesn't expose this directly\n };\n }\n}\n\n// ---------------------------------------------------------------------------\n// KANIDM API TYPES\n// ---------------------------------------------------------------------------\n\ninterface KanidmPerson {\n attrs: {\n uuid?: string[];\n name?: string[];\n displayname?: string[];\n mail?: string[];\n memberof?: string[];\n [key: string]: string[] | undefined;\n };\n}\n\ninterface KanidmPersonCreate {\n attrs: {\n name: string[];\n displayname?: string[];\n mail?: string[];\n [key: string]: string[] | undefined;\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"kanidm-XUwRGxXX.js","names":[],"sources":["../../src/shared/providers/kanidm.ts"],"sourcesContent":["/**\n * Kanidm Provider - OAuth2/OIDC Authentication\n *\n * Implements OAuth2/OIDC authentication with Kanidm server including:\n * - OIDC Discovery (client-specific endpoints)\n * - Authorization Code Flow with PKCE (required by Kanidm)\n * - Token validation using JWKS (ES256)\n * - Token introspection\n * - User management via Kanidm's native /v1/ API\n */\n\nimport * as jose from 'jose';\n\nimport {\n AccessDeniedError,\n ConfigurationError,\n InvalidClientError,\n InvalidCredentialsError,\n InvalidGrantError,\n InvalidNonceError,\n InvalidTokenError,\n NetworkError,\n NotImplementedError,\n ProviderError,\n TokenExpiredError,\n UserAlreadyExistsError,\n UserNotFoundError,\n} from '../errors.js';\nimport type {\n AuthCapabilities,\n AuthCredentials,\n AuthInterface,\n AuthorizationOptions,\n AuthorizationResult,\n AuthResult,\n CodeExchangeParams,\n CreateUserRequest,\n KanidmOptions,\n LogoutOptions,\n OIDCDiscoveryDocument,\n Session,\n TokenClaims,\n TokenIntrospection,\n TokenPayload,\n TokenValidationOptions,\n UserListResult,\n UserProfile,\n UserQuery,\n} from '../types.js';\n\n/**\n * Generate a random string for state/nonce/PKCE.\n */\nfunction generateRandomString(length = 32): string {\n const array = new Uint8Array(length);\n crypto.getRandomValues(array);\n return Array.from(array, (byte) => byte.toString(16).padStart(2, '0')).join(\n '',\n );\n}\n\n/**\n * Generate PKCE code verifier and challenge.\n */\nasync function generatePKCE(): Promise<{\n verifier: string;\n challenge: string;\n}> {\n const verifier = generateRandomString(32);\n const encoder = new TextEncoder();\n const data = encoder.encode(verifier);\n const hash = await crypto.subtle.digest('SHA-256', data);\n const challenge = btoa(String.fromCharCode(...new Uint8Array(hash)))\n .replace(/\\+/g, '-')\n .replace(/\\//g, '_')\n .replace(/=+$/, '');\n return { verifier, challenge };\n}\n\n/**\n * Kanidm authentication provider.\n *\n * Implements OAuth2/OIDC authentication with Kanidm server.\n * Key differences from Keycloak:\n * - Client-specific OIDC endpoints: /oauth2/openid/{clientId}/...\n * - ES256 token signing (not RS256)\n * - Authorization code flow only (no password grant)\n * - Native /v1/ API for admin operations (multi-step auth)\n */\nexport class KanidmProvider implements AuthInterface {\n private options: Required<Pick<KanidmOptions, 'serverUrl' | 'clientId'>> &\n KanidmOptions;\n private discoveryDocument: OIDCDiscoveryDocument | null = null;\n private jwks: jose.JWTVerifyGetKey | null = null;\n private adminToken: string | null = null;\n private adminTokenExpiry: number = 0;\n\n constructor(options: KanidmOptions) {\n if (!options.serverUrl) {\n throw new ConfigurationError('serverUrl is required', 'kanidm');\n }\n if (!options.clientId) {\n throw new ConfigurationError('clientId is required', 'kanidm');\n }\n\n this.options = {\n usePKCE: true, // Required by Kanidm\n verifySsl: true,\n scopes: ['openid', 'profile', 'email', 'groups'],\n timeout: 30000,\n maxRetries: 3,\n ...options,\n };\n }\n\n // ---------------------------------------------------------------------------\n // INTERNAL HELPERS\n // ---------------------------------------------------------------------------\n\n /**\n * Get the OIDC base URL for this client.\n * Kanidm uses client-specific OIDC endpoints.\n */\n private getOidcBaseUrl(): string {\n return `${this.options.serverUrl}/oauth2/openid/${this.options.clientId}`;\n }\n\n /**\n * Get the native API base URL.\n */\n private getApiBaseUrl(): string {\n return `${this.options.serverUrl}/v1`;\n }\n\n /**\n * Make an HTTP request with error handling.\n */\n private async request<T>(\n url: string,\n options: RequestInit = {},\n token?: string,\n ): Promise<T> {\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n ...this.options.headers,\n ...(options.headers as Record<string, string>),\n };\n\n if (token) {\n headers.Authorization = `Bearer ${token}`;\n }\n\n try {\n const response = await fetch(url, {\n ...options,\n headers,\n signal: AbortSignal.timeout(this.options.timeout || 30000),\n });\n\n if (!response.ok) {\n const errorBody = await response.text().catch(() => '');\n let errorData: Record<string, unknown> = {};\n try {\n errorData = JSON.parse(errorBody);\n } catch {\n // Not JSON\n }\n\n this.handleHttpError(response.status, errorData, errorBody);\n }\n\n const text = await response.text();\n if (!text) return {} as T;\n return JSON.parse(text) as T;\n } catch (error) {\n if (error instanceof Error && error.name === 'TimeoutError') {\n throw new NetworkError('Request timed out', 'kanidm', error);\n }\n if (\n error instanceof InvalidCredentialsError ||\n error instanceof AccessDeniedError ||\n error instanceof InvalidGrantError ||\n error instanceof InvalidClientError ||\n error instanceof UserNotFoundError ||\n error instanceof ProviderError\n ) {\n throw error;\n }\n throw new NetworkError(\n `Network error: ${error instanceof Error ? error.message : 'Unknown error'}`,\n 'kanidm',\n error instanceof Error ? error : undefined,\n );\n }\n }\n\n /**\n * Handle HTTP error responses.\n */\n private handleHttpError(\n status: number,\n data: Record<string, unknown>,\n rawBody: string,\n ): never {\n const error = data.error as string | undefined;\n const errorDescription =\n data.message || data.error_description || data.errorMessage || rawBody;\n\n switch (status) {\n case 400:\n if (error === 'invalid_grant') {\n throw new InvalidGrantError(errorDescription as string, 'kanidm');\n }\n if (error === 'invalid_client') {\n throw new InvalidClientError('kanidm');\n }\n throw new ProviderError(`Bad request: ${errorDescription}`, 'kanidm');\n\n case 401:\n throw new InvalidCredentialsError('kanidm');\n\n case 403:\n throw new AccessDeniedError(errorDescription as string, 'kanidm');\n\n case 404:\n throw new UserNotFoundError(undefined, 'kanidm');\n\n case 409:\n throw new UserAlreadyExistsError(undefined, 'kanidm');\n\n default:\n throw new ProviderError(\n `Kanidm error (${status}): ${errorDescription}`,\n 'kanidm',\n );\n }\n }\n\n /**\n * Fetch and cache the OIDC discovery document.\n */\n private async fetchDiscoveryDocument(): Promise<OIDCDiscoveryDocument> {\n if (this.discoveryDocument) {\n return this.discoveryDocument;\n }\n\n const url = `${this.getOidcBaseUrl()}/.well-known/openid-configuration`;\n this.discoveryDocument = await this.request<OIDCDiscoveryDocument>(url);\n return this.discoveryDocument;\n }\n\n /**\n * Get JWKS for token validation.\n */\n private async getJWKS(): Promise<jose.JWTVerifyGetKey> {\n if (this.jwks) {\n return this.jwks;\n }\n\n const discovery = await this.fetchDiscoveryDocument();\n this.jwks = jose.createRemoteJWKSet(new URL(discovery.jwks_uri));\n return this.jwks;\n }\n\n /**\n * Authenticate with Kanidm's native API to get an admin token.\n * Uses the multi-step authentication flow.\n */\n private async getAdminToken(): Promise<string> {\n // Check if we have a valid cached token\n if (this.adminToken && Date.now() < this.adminTokenExpiry) {\n return this.adminToken;\n }\n\n if (!this.options.adminUsername || !this.options.adminPassword) {\n throw new ConfigurationError(\n 'adminUsername and adminPassword are required for admin operations',\n 'kanidm',\n );\n }\n\n const authUrl = `${this.getApiBaseUrl()}/auth`;\n\n // Step 1: Initialize auth\n const initResponse = await fetch(authUrl, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n step: {\n init2: {\n username: this.options.adminUsername,\n issue: 'token',\n privileged: true,\n },\n },\n }),\n signal: AbortSignal.timeout(this.options.timeout || 30000),\n });\n\n if (!initResponse.ok) {\n throw new InvalidCredentialsError('kanidm', {\n reason: 'Failed to initialize admin authentication',\n });\n }\n\n // Get session cookie from response\n const cookies = initResponse.headers.get('set-cookie');\n\n // Step 2: Begin password authentication\n const beginResponse = await fetch(authUrl, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n ...(cookies ? { Cookie: cookies } : {}),\n },\n body: JSON.stringify({\n step: {\n begin: 'password',\n },\n }),\n signal: AbortSignal.timeout(this.options.timeout || 30000),\n });\n\n if (!beginResponse.ok) {\n throw new InvalidCredentialsError('kanidm', {\n reason: 'Failed to begin password authentication',\n });\n }\n\n // Step 3: Provide password\n const credResponse = await fetch(authUrl, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n ...(cookies ? { Cookie: cookies } : {}),\n },\n body: JSON.stringify({\n step: {\n cred: {\n password: this.options.adminPassword,\n },\n },\n }),\n signal: AbortSignal.timeout(this.options.timeout || 30000),\n });\n\n if (!credResponse.ok) {\n throw new InvalidCredentialsError('kanidm', {\n reason: 'Invalid admin credentials',\n });\n }\n\n const result = (await credResponse.json()) as {\n state?: { success?: string };\n token?: string;\n };\n\n // Extract token from response\n const token = result.state?.success || result.token;\n if (!token) {\n throw new ProviderError(\n 'Failed to obtain admin token from Kanidm',\n 'kanidm',\n );\n }\n\n this.adminToken = token;\n // Cache for 1 hour (Kanidm tokens typically last longer, but this is safe)\n this.adminTokenExpiry = Date.now() + 3600000;\n\n return this.adminToken;\n }\n\n // ---------------------------------------------------------------------------\n // AUTHENTICATION FLOWS\n // ---------------------------------------------------------------------------\n\n async getAuthorizationUrl(\n options?: AuthorizationOptions,\n ): Promise<AuthorizationResult> {\n const discovery = await this.fetchDiscoveryDocument();\n\n const state = options?.state || generateRandomString();\n const nonce = options?.nonce || generateRandomString();\n const scopes = options?.scopes ||\n this.options.scopes || ['openid', 'profile', 'email', 'groups'];\n const redirectUri = options?.redirectUri || this.options.redirectUri;\n\n if (!redirectUri) {\n throw new ConfigurationError('redirectUri is required', 'kanidm');\n }\n\n const params = new URLSearchParams({\n client_id: this.options.clientId,\n redirect_uri: redirectUri,\n response_type: 'code',\n scope: scopes.join(' '),\n state,\n nonce,\n });\n\n if (options?.prompt) {\n params.set('prompt', options.prompt);\n }\n\n if (options?.loginHint) {\n params.set('login_hint', options.loginHint);\n }\n\n // Add extra params\n if (options?.extraParams) {\n for (const [key, value] of Object.entries(options.extraParams)) {\n params.set(key, value);\n }\n }\n\n // PKCE is required by Kanidm\n const pkce = await generatePKCE();\n params.set('code_challenge', pkce.challenge);\n params.set('code_challenge_method', 'S256');\n\n const url = `${discovery.authorization_endpoint}?${params.toString()}`;\n\n return {\n url,\n state,\n nonce,\n codeVerifier: pkce.verifier,\n };\n }\n\n async exchangeCode(params: CodeExchangeParams): Promise<AuthResult> {\n const discovery = await this.fetchDiscoveryDocument();\n\n const redirectUri = params.redirectUri || this.options.redirectUri;\n if (!redirectUri) {\n throw new ConfigurationError('redirectUri is required', 'kanidm');\n }\n\n const body = new URLSearchParams({\n grant_type: 'authorization_code',\n client_id: this.options.clientId,\n code: params.code,\n redirect_uri: redirectUri,\n });\n\n if (this.options.clientSecret) {\n body.set('client_secret', this.options.clientSecret);\n }\n\n // Code verifier is required for PKCE\n if (params.codeVerifier) {\n body.set('code_verifier', params.codeVerifier);\n }\n\n const response = await this.request<{\n access_token: string;\n refresh_token?: string;\n id_token?: string;\n expires_in: number;\n token_type: string;\n scope?: string;\n }>(discovery.token_endpoint, {\n method: 'POST',\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n body: body.toString(),\n });\n\n // Decode ID token to get user ID\n let userId = '';\n if (response.id_token) {\n const payload = jose.decodeJwt(response.id_token);\n userId = payload.sub || '';\n }\n\n return {\n accessToken: response.access_token,\n tokenType: response.token_type || 'Bearer',\n expiresIn: response.expires_in,\n refreshToken: response.refresh_token,\n idToken: response.id_token,\n scope: response.scope,\n userId,\n };\n }\n\n async authenticate(_credentials: AuthCredentials): Promise<AuthResult> {\n // Kanidm only supports authorization_code grant for OAuth2\n // Direct authentication is not supported via OAuth2\n throw new NotImplementedError('authenticate', 'kanidm', {\n reason:\n 'Kanidm only supports authorization code flow. Use getAuthorizationUrl() and exchangeCode() instead.',\n });\n }\n\n async refresh(refreshToken: string): Promise<AuthResult> {\n const discovery = await this.fetchDiscoveryDocument();\n\n const body = new URLSearchParams({\n grant_type: 'refresh_token',\n client_id: this.options.clientId,\n refresh_token: refreshToken,\n });\n\n if (this.options.clientSecret) {\n body.set('client_secret', this.options.clientSecret);\n }\n\n const response = await this.request<{\n access_token: string;\n refresh_token?: string;\n id_token?: string;\n expires_in: number;\n token_type: string;\n scope?: string;\n }>(discovery.token_endpoint, {\n method: 'POST',\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n body: body.toString(),\n });\n\n // Decode to get user ID\n let userId = '';\n if (response.access_token) {\n const payload = jose.decodeJwt(response.access_token);\n userId = payload.sub || '';\n }\n\n return {\n accessToken: response.access_token,\n tokenType: response.token_type || 'Bearer',\n expiresIn: response.expires_in,\n refreshToken: response.refresh_token || refreshToken,\n idToken: response.id_token,\n scope: response.scope,\n userId,\n };\n }\n\n async logout(options?: LogoutOptions): Promise<void> {\n const discovery = await this.fetchDiscoveryDocument();\n\n // Revoke refresh token if provided\n // Silently ignore failures - invalid tokens are effectively already revoked\n if (options?.refreshToken && discovery.revocation_endpoint) {\n const body = new URLSearchParams({\n client_id: this.options.clientId,\n token: options.refreshToken,\n token_type_hint: 'refresh_token',\n });\n\n if (this.options.clientSecret) {\n body.set('client_secret', this.options.clientSecret);\n }\n\n try {\n await this.request(discovery.revocation_endpoint, {\n method: 'POST',\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n body: body.toString(),\n });\n } catch {\n // Ignore revocation errors - token may already be invalid/revoked\n }\n }\n\n // Revoke access token if provided\n // Silently ignore failures - invalid tokens are effectively already revoked\n if (options?.token && discovery.revocation_endpoint) {\n const body = new URLSearchParams({\n client_id: this.options.clientId,\n token: options.token,\n token_type_hint: 'access_token',\n });\n\n if (this.options.clientSecret) {\n body.set('client_secret', this.options.clientSecret);\n }\n\n try {\n await this.request(discovery.revocation_endpoint, {\n method: 'POST',\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n body: body.toString(),\n });\n } catch {\n // Ignore revocation errors - token may already be invalid/revoked\n }\n }\n }\n\n // ---------------------------------------------------------------------------\n // TOKEN OPERATIONS\n // ---------------------------------------------------------------------------\n\n async validateToken(\n token: string,\n options?: TokenValidationOptions,\n ): Promise<TokenClaims | null> {\n try {\n const jwks = await this.getJWKS();\n const discovery = await this.fetchDiscoveryDocument();\n\n const verifyOptions: jose.JWTVerifyOptions = {\n issuer: options?.issuer || discovery.issuer,\n clockTolerance: options?.clockTolerance || 0,\n };\n\n if (options?.audience) {\n verifyOptions.audience = options.audience;\n }\n\n const { payload } = await jose.jwtVerify(token, jwks, verifyOptions);\n\n // Check nonce if provided\n if (options?.nonce && payload.nonce !== options.nonce) {\n throw new InvalidNonceError('kanidm');\n }\n\n // Extract groups from Kanidm token structure\n // Kanidm uses 'groups' claim directly\n const groups = (payload.groups as string[]) || [];\n\n return {\n sub: payload.sub || '',\n iss: payload.iss || '',\n aud: payload.aud || '',\n exp: payload.exp || 0,\n iat: payload.iat || 0,\n nbf: payload.nbf,\n azp: payload.azp as string | undefined,\n email: payload.email as string | undefined,\n email_verified: payload.email_verified as boolean | undefined,\n preferred_username: payload.preferred_username as string | undefined,\n name: payload.name as string | undefined,\n roles: groups, // Map groups to roles for consistency\n ...payload,\n };\n } catch (error) {\n if (error instanceof jose.errors.JWTExpired) {\n throw new TokenExpiredError('kanidm');\n }\n if (error instanceof jose.errors.JWTClaimValidationFailed) {\n return null;\n }\n if (error instanceof jose.errors.JWSSignatureVerificationFailed) {\n throw new InvalidTokenError('Invalid token signature', 'kanidm');\n }\n if (error instanceof InvalidNonceError) {\n throw error;\n }\n return null;\n }\n }\n\n decodeToken(token: string): TokenPayload {\n try {\n const parts = token.split('.');\n if (parts.length !== 3) {\n throw new InvalidTokenError('Invalid JWT format', 'kanidm');\n }\n\n const header = JSON.parse(atob(parts[0]));\n const payload = jose.decodeJwt(token);\n\n return {\n header: {\n alg: header.alg,\n typ: header.typ,\n kid: header.kid,\n },\n payload: payload as unknown as TokenClaims,\n signature: parts[2],\n };\n } catch {\n throw new InvalidTokenError('Failed to decode token', 'kanidm');\n }\n }\n\n async introspectToken(token: string): Promise<TokenIntrospection> {\n const discovery = await this.fetchDiscoveryDocument();\n\n if (!discovery.introspection_endpoint) {\n // Fallback to local validation\n const claims = await this.validateToken(token);\n return {\n active: claims !== null,\n claims: claims || undefined,\n };\n }\n\n const body = new URLSearchParams({\n client_id: this.options.clientId,\n token,\n });\n\n if (this.options.clientSecret) {\n body.set('client_secret', this.options.clientSecret);\n }\n\n const response = await this.request<{\n active: boolean;\n sub?: string;\n iss?: string;\n aud?: string | string[];\n exp?: number;\n iat?: number;\n client_id?: string;\n scope?: string;\n token_type?: string;\n [key: string]: unknown;\n }>(discovery.introspection_endpoint, {\n method: 'POST',\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n body: body.toString(),\n });\n\n if (!response.active) {\n return { active: false };\n }\n\n return {\n active: true,\n claims: {\n sub: response.sub || '',\n iss: response.iss || '',\n aud: response.aud || '',\n exp: response.exp || 0,\n iat: response.iat || 0,\n ...response,\n },\n tokenType: response.token_type,\n clientId: response.client_id,\n scope: response.scope,\n };\n }\n\n // ---------------------------------------------------------------------------\n // USER OPERATIONS\n // ---------------------------------------------------------------------------\n\n async getProfile(tokenOrSession: string): Promise<UserProfile> {\n const discovery = await this.fetchDiscoveryDocument();\n\n const response = await this.request<{\n sub: string;\n preferred_username?: string;\n email?: string;\n email_verified?: boolean;\n name?: string;\n given_name?: string;\n family_name?: string;\n picture?: string;\n groups?: string[];\n [key: string]: unknown;\n }>(discovery.userinfo_endpoint, {\n method: 'GET',\n headers: { Authorization: `Bearer ${tokenOrSession}` },\n });\n\n return {\n id: response.sub,\n username: response.preferred_username,\n email: response.email,\n emailVerified: response.email_verified,\n firstName: response.given_name,\n lastName: response.family_name,\n displayName: response.name,\n picture: response.picture,\n groups: response.groups,\n };\n }\n\n async updateProfile(\n _tokenOrSession: string,\n _profile: Partial<UserProfile>,\n ): Promise<UserProfile> {\n // Kanidm doesn't expose profile update via OAuth2/OIDC\n throw new NotImplementedError('updateProfile', 'kanidm', {\n reason:\n 'Profile updates are not supported via OAuth2. Use admin API with createUser/updateUser.',\n });\n }\n\n async getUser(userId: string, _adminToken?: string): Promise<UserProfile> {\n const token = await this.getAdminToken();\n\n const response = await this.request<KanidmPerson>(\n `${this.getApiBaseUrl()}/person/${userId}`,\n { method: 'GET' },\n token,\n );\n\n return this.mapKanidmPerson(response);\n }\n\n async createUser(\n user: CreateUserRequest,\n _adminToken: string,\n ): Promise<UserProfile> {\n const token = await this.getAdminToken();\n\n const kanidmPerson: Partial<KanidmPersonCreate> = {\n attrs: {\n name: [user.username],\n displayname: [\n user.firstName && user.lastName\n ? `${user.firstName} ${user.lastName}`\n : user.username,\n ],\n },\n };\n\n if (user.email && kanidmPerson.attrs) {\n kanidmPerson.attrs.mail = [user.email];\n }\n\n const response = await fetch(`${this.getApiBaseUrl()}/person`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${token}`,\n },\n body: JSON.stringify(kanidmPerson),\n });\n\n if (!response.ok) {\n const errorBody = await response.text();\n if (response.status === 409) {\n throw new UserAlreadyExistsError(user.username, 'kanidm');\n }\n throw new ProviderError(`Failed to create user: ${errorBody}`, 'kanidm');\n }\n\n // Fetch the created user\n return this.getUser(user.username, token);\n }\n\n async updateUser(\n userId: string,\n updates: Partial<CreateUserRequest>,\n _adminToken: string,\n ): Promise<UserProfile> {\n const token = await this.getAdminToken();\n\n const attrs: Record<string, string[]> = {};\n\n if (updates.email !== undefined) {\n attrs.mail = [updates.email];\n }\n\n if (updates.firstName !== undefined || updates.lastName !== undefined) {\n const displayName =\n updates.firstName && updates.lastName\n ? `${updates.firstName} ${updates.lastName}`\n : updates.firstName || updates.lastName || '';\n if (displayName) {\n attrs.displayname = [displayName];\n }\n }\n\n await this.request(\n `${this.getApiBaseUrl()}/person/${userId}`,\n {\n method: 'PATCH',\n body: JSON.stringify({ attrs }),\n },\n token,\n );\n\n return this.getUser(userId, token);\n }\n\n async deleteUser(userId: string, _adminToken: string): Promise<void> {\n const token = await this.getAdminToken();\n\n await this.request(\n `${this.getApiBaseUrl()}/person/${userId}`,\n { method: 'DELETE' },\n token,\n );\n }\n\n async listUsers(\n query: UserQuery,\n _adminToken?: string,\n ): Promise<UserListResult> {\n const token = await this.getAdminToken();\n\n // Kanidm uses a different query format\n let url = `${this.getApiBaseUrl()}/person`;\n\n // Add search filter if provided\n // Note: Kanidm API may have different filtering capabilities\n const params = new URLSearchParams();\n if (query.search) {\n params.set('search', query.search);\n }\n\n if (params.toString()) {\n url += `?${params.toString()}`;\n }\n\n const response = await this.request<KanidmPerson[]>(\n url,\n { method: 'GET' },\n token,\n );\n\n const users = Array.isArray(response) ? response : [];\n\n // Apply client-side filtering and pagination\n let filteredUsers = users.map((u) => this.mapKanidmPerson(u));\n\n if (query.email) {\n filteredUsers = filteredUsers.filter((u) => u.email === query.email);\n }\n\n if (query.username) {\n filteredUsers = filteredUsers.filter(\n (u) => u.username === query.username,\n );\n }\n\n const total = filteredUsers.length;\n const offset = query.offset || 0;\n const limit = query.limit || 100;\n\n return {\n users: filteredUsers.slice(offset, offset + limit),\n total,\n limit,\n offset,\n };\n }\n\n async requestPasswordReset(_email: string): Promise<void> {\n throw new NotImplementedError('requestPasswordReset', 'kanidm', {\n reason: 'Password reset is not exposed via Kanidm API',\n });\n }\n\n async resetPassword(_token: string, _newPassword: string): Promise<void> {\n throw new NotImplementedError('resetPassword', 'kanidm', {\n reason: 'Password reset is not exposed via Kanidm API',\n });\n }\n\n // ---------------------------------------------------------------------------\n // SESSION OPERATIONS\n // ---------------------------------------------------------------------------\n\n async listSessions(\n _userId: string,\n _adminToken?: string,\n ): Promise<Session[]> {\n throw new NotImplementedError('listSessions', 'kanidm', {\n reason: 'Session management is not exposed via Kanidm API',\n });\n }\n\n async revokeSession(_sessionId: string, _adminToken?: string): Promise<void> {\n throw new NotImplementedError('revokeSession', 'kanidm', {\n reason: 'Session management is not exposed via Kanidm API',\n });\n }\n\n async revokeAllSessions(\n _userId: string,\n _adminToken?: string,\n ): Promise<void> {\n throw new NotImplementedError('revokeAllSessions', 'kanidm', {\n reason: 'Session management is not exposed via Kanidm API',\n });\n }\n\n // ---------------------------------------------------------------------------\n // AUTHORIZATION\n // ---------------------------------------------------------------------------\n\n async hasRole(tokenOrUserId: string, role: string): Promise<boolean> {\n const roles = await this.getRoles(tokenOrUserId);\n return roles.includes(role);\n }\n\n async hasPermission(\n tokenOrUserId: string,\n permission: string,\n resource?: string,\n ): Promise<boolean> {\n // Check if the user has a group matching the permission\n const roles = await this.getRoles(tokenOrUserId);\n const permissionRole = resource ? `${resource}:${permission}` : permission;\n return roles.includes(permissionRole) || roles.includes(permission);\n }\n\n async getRoles(\n tokenOrUserId: string,\n _adminToken?: string,\n ): Promise<string[]> {\n // First try to decode as JWT\n try {\n const claims = await this.validateToken(tokenOrUserId);\n if (claims) {\n return claims.roles || [];\n }\n } catch {\n // Not a valid token\n }\n\n // Try to get user and return their groups\n try {\n const user = await this.getUser(tokenOrUserId);\n return user.groups || [];\n } catch {\n // User not found or other error\n }\n\n return [];\n }\n\n async assignRole(\n _userId: string,\n _role: string,\n _adminToken: string,\n ): Promise<void> {\n throw new NotImplementedError('assignRole', 'kanidm', {\n reason:\n 'Role assignment via API requires group management. Use Kanidm CLI to add users to groups.',\n });\n }\n\n async removeRole(\n _userId: string,\n _role: string,\n _adminToken: string,\n ): Promise<void> {\n throw new NotImplementedError('removeRole', 'kanidm', {\n reason:\n 'Role removal via API requires group management. Use Kanidm CLI to remove users from groups.',\n });\n }\n\n // ---------------------------------------------------------------------------\n // PROVIDER INFORMATION\n // ---------------------------------------------------------------------------\n\n async getCapabilities(): Promise<AuthCapabilities> {\n return {\n authorizationCode: true,\n passwordGrant: false, // Not supported\n clientCredentials: false, // Not supported\n tokenRefresh: true,\n oidc: true,\n userManagement: true, // Via /v1/person API\n sessionManagement: false, // Not exposed\n rbac: true, // Via groups claim\n passwordReset: false, // Not exposed via API\n mfa: true, // Webauthn/passkeys supported\n socialLogin: false,\n federation: true,\n decentralized: false,\n };\n }\n\n async getDiscoveryDocument(): Promise<OIDCDiscoveryDocument | null> {\n return this.fetchDiscoveryDocument();\n }\n\n // ---------------------------------------------------------------------------\n // PRIVATE HELPERS\n // ---------------------------------------------------------------------------\n\n private mapKanidmPerson(person: KanidmPerson): UserProfile {\n const attrs = person.attrs || {};\n\n return {\n id: attrs.uuid?.[0] || attrs.name?.[0] || '',\n username: attrs.name?.[0],\n email: attrs.mail?.[0],\n displayName: attrs.displayname?.[0],\n groups: attrs.memberof,\n enabled: true, // Kanidm doesn't expose this directly\n };\n }\n}\n\n// ---------------------------------------------------------------------------\n// KANIDM API TYPES\n// ---------------------------------------------------------------------------\n\ninterface KanidmPerson {\n attrs: {\n uuid?: string[];\n name?: string[];\n displayname?: string[];\n mail?: string[];\n memberof?: string[];\n [key: string]: string[] | undefined;\n };\n}\n\ninterface KanidmPersonCreate {\n attrs: {\n name: string[];\n displayname?: string[];\n mail?: string[];\n [key: string]: string[] | undefined;\n };\n}\n"],"mappings":"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"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { E as UserAlreadyExistsError, O as UserNotFoundError, S as ProviderError, T as TokenExpiredError, a as ConfigurationError, b as NetworkError, c as InvalidClientError, l as InvalidCredentialsError, p as InvalidNonceError, t as AccessDeniedError, u as InvalidGrantError, v as InvalidTokenError, x as NotImplementedError, y as MfaRequiredError } from "./errors-RgVH84_1.js";
|
|
2
|
-
import {
|
|
3
|
-
import { t as
|
|
2
|
+
import { b as JWTClaimValidationFailed, t as jwtVerify, x as JWTExpired, y as JWSSignatureVerificationFailed } from "./verify-CGYWoFss.js";
|
|
3
|
+
import { t as createRemoteJWKSet } from "./remote-D3qRdbiU.js";
|
|
4
|
+
import { t as decodeJwt } from "./decode_jwt-ob-gfrt8.js";
|
|
4
5
|
//#region src/shared/providers/keycloak.ts
|
|
5
6
|
/**
|
|
6
7
|
* Keycloak Provider - OIDC/OAuth2 Authentication
|
|
@@ -630,4 +631,4 @@ var KeycloakProvider = class {
|
|
|
630
631
|
//#endregion
|
|
631
632
|
export { KeycloakProvider };
|
|
632
633
|
|
|
633
|
-
//# sourceMappingURL=keycloak-
|
|
634
|
+
//# sourceMappingURL=keycloak-Bfk3iJUJ.js.map
|