@flink-app/oidc-plugin 1.0.0 → 2.0.0-alpha.101

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.
Files changed (52) hide show
  1. package/CHANGELOG.md +566 -0
  2. package/README.md +23 -2
  3. package/dist/OidcPlugin.d.ts +1 -1
  4. package/dist/OidcPlugin.d.ts.map +1 -1
  5. package/dist/OidcPlugin.js +32 -18
  6. package/dist/OidcPluginOptions.d.ts +75 -8
  7. package/dist/OidcPluginOptions.d.ts.map +1 -1
  8. package/dist/OidcProviderConfig.d.ts +21 -1
  9. package/dist/OidcProviderConfig.d.ts.map +1 -1
  10. package/dist/handlers/CallbackOidc.d.ts.map +1 -1
  11. package/dist/handlers/CallbackOidc.js +50 -5
  12. package/dist/handlers/InitiateOidc.d.ts.map +1 -1
  13. package/dist/handlers/InitiateOidc.js +14 -1
  14. package/dist/index.d.ts +1 -1
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/log.d.ts +27 -0
  17. package/dist/log.d.ts.map +1 -0
  18. package/dist/log.js +37 -0
  19. package/dist/providers/OidcProvider.d.ts +1 -1
  20. package/dist/providers/OidcProvider.d.ts.map +1 -1
  21. package/dist/providers/OidcProvider.js +46 -5
  22. package/dist/providers/ProviderRegistry.d.ts +2 -1
  23. package/dist/providers/ProviderRegistry.d.ts.map +1 -1
  24. package/dist/providers/ProviderRegistry.js +10 -2
  25. package/dist/schemas/CallbackRequest.d.ts +3 -6
  26. package/dist/schemas/CallbackRequest.d.ts.map +1 -1
  27. package/dist/schemas/InitiateRequest.d.ts +3 -6
  28. package/dist/schemas/InitiateRequest.d.ts.map +1 -1
  29. package/dist/schemas/OidcSession.d.ts +5 -0
  30. package/dist/schemas/OidcSession.d.ts.map +1 -1
  31. package/dist/utils/response-utils.d.ts +16 -5
  32. package/dist/utils/response-utils.d.ts.map +1 -1
  33. package/dist/utils/response-utils.js +31 -10
  34. package/examples/multi-provider.ts +1 -1
  35. package/package.json +8 -11
  36. package/spec/helpers/test-helpers.ts +14 -0
  37. package/spec/plugin/OidcPlugin.spec.ts +104 -3
  38. package/spec/providers/ProviderRegistry.spec.ts +8 -7
  39. package/spec/utils/response-utils.spec.ts +85 -0
  40. package/src/OidcPlugin.ts +54 -21
  41. package/src/OidcPluginOptions.ts +84 -9
  42. package/src/OidcProviderConfig.ts +23 -1
  43. package/src/handlers/CallbackOidc.ts +66 -5
  44. package/src/handlers/InitiateOidc.ts +19 -1
  45. package/src/index.ts +1 -1
  46. package/src/log.ts +33 -0
  47. package/src/providers/OidcProvider.ts +60 -7
  48. package/src/providers/ProviderRegistry.ts +22 -3
  49. package/src/schemas/CallbackRequest.ts +4 -6
  50. package/src/schemas/InitiateRequest.ts +3 -6
  51. package/src/schemas/OidcSession.ts +6 -0
  52. package/src/utils/response-utils.ts +53 -10
@@ -28,6 +28,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
28
28
  Object.defineProperty(exports, "__esModule", { value: true });
29
29
  exports.oidcPlugin = oidcPlugin;
30
30
  const flink_1 = require("@flink-app/flink");
31
+ const log_1 = require("./log");
31
32
  const OidcSessionRepo_1 = __importDefault(require("./repos/OidcSessionRepo"));
32
33
  const OidcConnectionRepo_1 = __importDefault(require("./repos/OidcConnectionRepo"));
33
34
  const encryption_utils_1 = require("./utils/encryption-utils");
@@ -109,13 +110,14 @@ const CallbackOidc = __importStar(require("./handlers/CallbackOidc"));
109
110
  */
110
111
  function oidcPlugin(options) {
111
112
  // Validation
112
- if (!options.providers || Object.keys(options.providers).length === 0) {
113
- throw new Error("OIDC Plugin: At least one provider must be configured");
113
+ const providers = options.providers || {};
114
+ if (Object.keys(providers).length === 0 && !options.providerLoader) {
115
+ throw new Error("OIDC Plugin: At least one provider must be configured, or providerLoader must be provided");
114
116
  }
115
117
  // Validate provider configurations
116
- const configuredProviders = Object.keys(options.providers);
118
+ const configuredProviders = Object.keys(providers);
117
119
  for (const providerName of configuredProviders) {
118
- const providerConfig = options.providers[providerName];
120
+ const providerConfig = providers[providerName];
119
121
  if (!providerConfig)
120
122
  continue;
121
123
  if (!providerConfig.issuer) {
@@ -124,9 +126,6 @@ function oidcPlugin(options) {
124
126
  if (!providerConfig.clientId) {
125
127
  throw new Error(`OIDC Plugin: ${providerName} clientId is required`);
126
128
  }
127
- if (!providerConfig.clientSecret) {
128
- throw new Error(`OIDC Plugin: ${providerName} clientSecret is required`);
129
- }
130
129
  if (!providerConfig.callbackUrl) {
131
130
  throw new Error(`OIDC Plugin: ${providerName} callbackUrl is required`);
132
131
  }
@@ -137,6 +136,7 @@ function oidcPlugin(options) {
137
136
  `(authorizationEndpoint, tokenEndpoint, jwksUri)`);
138
137
  }
139
138
  }
139
+ log_1.oidcLog.debug(`Provider "${providerName}" config:`, `issuer=${providerConfig.issuer}`, `clientId=${providerConfig.clientId}`, `clientSecret=${(0, log_1.maskSecret)(providerConfig.clientSecret)}`, `callbackUrl=${providerConfig.callbackUrl}`, providerConfig.discoveryUrl ? `discoveryUrl=${providerConfig.discoveryUrl}` : `authorizationEndpoint=${providerConfig.authorizationEndpoint} tokenEndpoint=${providerConfig.tokenEndpoint}`, `scope=${(providerConfig.scope || ["openid", "email", "profile"]).join(" ")}`, providerConfig.tokenEndpointAuthMethod ? `authMethod=${providerConfig.tokenEndpointAuthMethod}` : "");
140
140
  }
141
141
  if (!options.onAuthSuccess) {
142
142
  throw new Error("OIDC Plugin: onAuthSuccess callback is required");
@@ -144,19 +144,32 @@ function oidcPlugin(options) {
144
144
  // Determine encryption key
145
145
  let encryptionKey = options.encryptionKey;
146
146
  if (!encryptionKey) {
147
- // Derive from first configured provider's client secret
148
- const firstProvider = configuredProviders[0];
149
- const firstProviderConfig = options.providers[firstProvider];
150
- if (firstProviderConfig) {
151
- encryptionKey = firstProviderConfig.clientSecret;
147
+ // Derive from the first configured provider that has a client secret
148
+ const providerWithSecret = configuredProviders.find((name) => providers[name]?.clientSecret);
149
+ if (providerWithSecret) {
150
+ encryptionKey = providers[providerWithSecret].clientSecret;
152
151
  flink_1.log.warn("OIDC Plugin: No encryption key provided, deriving from client secret. " + "For better security, provide a dedicated encryptionKey in options.");
152
+ log_1.oidcLog.debug(`Encryption key derived from provider "${providerWithSecret}" clientSecret`);
153
153
  }
154
154
  }
155
- if (!encryptionKey || encryptionKey.length < 32) {
156
- throw new Error("OIDC Plugin: Encryption key must be at least 32 characters");
155
+ else {
156
+ log_1.oidcLog.debug(`Encryption key: ${(0, log_1.maskSecret)(encryptionKey)} (explicit)`);
157
+ }
158
+ // Encryption key is required when storing tokens
159
+ if (options.storeTokens) {
160
+ if (!encryptionKey || encryptionKey.length < 32) {
161
+ throw new Error("OIDC Plugin: Encryption key must be at least 32 characters. " +
162
+ "Provide an explicit encryptionKey when using storeTokens with public clients (no clientSecret).");
163
+ }
164
+ (0, encryption_utils_1.validateEncryptionSecret)(encryptionKey);
165
+ }
166
+ else if (encryptionKey) {
167
+ // Validate if provided, even when not storing tokens (used for getConnection/getConnections)
168
+ if (encryptionKey.length < 32) {
169
+ throw new Error("OIDC Plugin: Encryption key must be at least 32 characters");
170
+ }
171
+ (0, encryption_utils_1.validateEncryptionSecret)(encryptionKey);
157
172
  }
158
- // Validate encryption key
159
- (0, encryption_utils_1.validateEncryptionSecret)(encryptionKey);
160
173
  let flinkApp;
161
174
  let sessionRepo;
162
175
  let connectionRepo;
@@ -174,6 +187,7 @@ function oidcPlugin(options) {
174
187
  // Initialize repositories
175
188
  const sessionsCollectionName = options.sessionsCollectionName || "oidc_sessions";
176
189
  const connectionsCollectionName = options.connectionsCollectionName || "oidc_connections";
190
+ log_1.oidcLog.debug(`Init: sessionsCollection=${sessionsCollectionName}`, `connectionsCollection=${connectionsCollectionName}`, `storeTokens=${!!options.storeTokens}`, `sessionTTL=${options.sessionTTL ?? 600}s`, `registerRoutes=${options.registerRoutes !== false}`, configuredProviders.length > 0 ? `staticProviders=[${configuredProviders.join(", ")}]` : "staticProviders=[] (dynamic only)", options.providerLoader ? "providerLoader=yes" : "providerLoader=no");
177
191
  sessionRepo = new OidcSessionRepo_1.default(sessionsCollectionName, db);
178
192
  connectionRepo = new OidcConnectionRepo_1.default(connectionsCollectionName, db);
179
193
  flinkApp.addRepo("oidcSessionRepo", sessionRepo);
@@ -183,7 +197,7 @@ function oidcPlugin(options) {
183
197
  await db.collection(sessionsCollectionName).createIndex({ createdAt: 1 }, { expireAfterSeconds: sessionTTL });
184
198
  flink_1.log.info(`OIDC Plugin: Created TTL index on ${sessionsCollectionName} with ${sessionTTL}s expiration`);
185
199
  // Initialize provider registry
186
- providerRegistry = new ProviderRegistry_1.ProviderRegistry(options.providers, options.providerLoader);
200
+ providerRegistry = new ProviderRegistry_1.ProviderRegistry(providers, options.providerLoader, flinkApp.ctx);
187
201
  // Store provider registry in app context for handlers to access
188
202
  flinkApp.ctx.oidcProviderRegistry = providerRegistry;
189
203
  // Register OIDC handlers
@@ -192,7 +206,7 @@ function oidcPlugin(options) {
192
206
  flinkApp.addHandler(InitiateOidc);
193
207
  flinkApp.addHandler(CallbackOidc);
194
208
  }
195
- flink_1.log.info(`OIDC Plugin initialized with providers: ${configuredProviders.join(", ")}`);
209
+ flink_1.log.info(`OIDC Plugin initialized${configuredProviders.length > 0 ? ` with providers: ${configuredProviders.join(", ")}` : " with dynamic provider loader"}`);
196
210
  }
197
211
  catch (error) {
198
212
  flink_1.log.error("Failed to initialize OIDC Plugin:", error);
@@ -1,6 +1,7 @@
1
1
  import OidcProfile from "./schemas/OidcProfile";
2
2
  import OidcTokenSet from "./schemas/OidcTokenSet";
3
3
  import { OidcProviderConfig } from "./OidcProviderConfig";
4
+ import { TokenDeliveryMode } from "./utils/response-utils";
4
5
  /**
5
6
  * OIDC error information passed to onAuthError callback
6
7
  */
@@ -35,7 +36,9 @@ export interface AuthSuccessCallbackResponse {
35
36
  token: string;
36
37
  /**
37
38
  * Optional redirect URL after authentication
38
- * Plugin will append #token=... to this URL
39
+ *
40
+ * The plugin will attach the JWT token to this URL according to the
41
+ * top-level `tokenDelivery` option (fragment by default, or query).
39
42
  */
40
43
  redirectUrl?: string;
41
44
  }
@@ -56,7 +59,7 @@ export interface AuthErrorCallbackResponse {
56
59
  * The onAuthSuccess callback receives the Flink context as a second parameter,
57
60
  * allowing the application to generate JWT tokens using ctx.plugins.jwtAuth.createToken().
58
61
  */
59
- export interface OidcPluginOptions {
62
+ export interface OidcPluginOptions<TCtx = any> {
60
63
  /**
61
64
  * OIDC provider configurations
62
65
  * Key = provider name (used in URLs: /oidc/{provider}/initiate)
@@ -81,7 +84,7 @@ export interface OidcPluginOptions {
81
84
  * }
82
85
  * }
83
86
  */
84
- providers: Record<string, OidcProviderConfig>;
87
+ providers?: Record<string, OidcProviderConfig>;
85
88
  /**
86
89
  * Whether to store OIDC tokens for future API access
87
90
  * If false, tokens are discarded after authentication (auth-only mode)
@@ -110,7 +113,7 @@ export interface OidcPluginOptions {
110
113
  *
111
114
  * Example:
112
115
  * ```typescript
113
- * onAuthSuccess: async ({ profile, claims, provider }, ctx) => {
116
+ * onAuthSuccess: async ({ profile, claims, provider, metadata }, ctx) => {
114
117
  * // Find user by OIDC subject + issuer
115
118
  * let user = await ctx.repos.userRepo.getOne({
116
119
  * 'oidcConnections.subject': claims.sub,
@@ -158,12 +161,32 @@ export interface OidcPluginOptions {
158
161
  * Provider name (e.g., "acme", "contoso")
159
162
  */
160
163
  provider: string;
164
+ /**
165
+ * The redirectUri passed on the initiate request (stored in session)
166
+ * Use this to redirect back to the originating client application.
167
+ *
168
+ * Example:
169
+ * ```typescript
170
+ * return { user, token, redirectUrl: redirectUri || DEFAULT_REDIRECT_URL };
171
+ * ```
172
+ */
173
+ redirectUri: string;
174
+ /**
175
+ * Arbitrary metadata collected from meta.* query parameters on the initiate request.
176
+ * Opaque to the plugin — it only transports these values.
177
+ *
178
+ * Example: GET /oidc/acme/initiate?meta.intent=admin&meta.source=mobile
179
+ * Produces: { intent: "admin", source: "mobile" }
180
+ *
181
+ * Empty object ({}) if no meta.* params were provided.
182
+ */
183
+ metadata: Record<string, string>;
161
184
  /**
162
185
  * OIDC tokens (only if storeTokens: true)
163
186
  * Includes accessToken, idToken, refreshToken
164
187
  */
165
188
  tokens?: OidcTokenSet;
166
- }, ctx: any) => Promise<AuthSuccessCallbackResponse>;
189
+ }, ctx: TCtx) => Promise<AuthSuccessCallbackResponse>;
167
190
  /**
168
191
  * Callback invoked on OIDC authentication errors
169
192
  *
@@ -194,6 +217,17 @@ export interface OidcPluginOptions {
194
217
  onAuthError?: (params: {
195
218
  error: OidcError;
196
219
  provider: string;
220
+ /**
221
+ * The redirectUri from the initiate request (if available)
222
+ * May be undefined for errors that occur before session lookup
223
+ * (e.g., IdP returns an error before we can retrieve the session)
224
+ */
225
+ redirectUri?: string;
226
+ /**
227
+ * Metadata from the initiate request (meta.* query params, prefix stripped).
228
+ * Empty object ({}) if no meta.* params were provided or session was not found.
229
+ */
230
+ metadata: Record<string, string>;
197
231
  }) => Promise<AuthErrorCallbackResponse>;
198
232
  /**
199
233
  * Dynamic provider loader callback
@@ -204,11 +238,12 @@ export interface OidcPluginOptions {
204
238
  * Use this for multi-tenant scenarios where each organization has different IdPs.
205
239
  *
206
240
  * @param providerName - Name of the provider to load
241
+ * @param ctx - Flink context with access to repos and plugins
207
242
  * @returns Provider configuration or null if not found
208
243
  *
209
244
  * Example:
210
245
  * ```typescript
211
- * providerLoader: async (providerName) => {
246
+ * providerLoader: async (providerName, ctx) => {
212
247
  * const config = await ctx.repos.oidcProviderRepo.getByName(providerName);
213
248
  * if (!config || !config.enabled) {
214
249
  * return null;
@@ -224,7 +259,7 @@ export interface OidcPluginOptions {
224
259
  * }
225
260
  * ```
226
261
  */
227
- providerLoader?: (providerName: string) => Promise<OidcProviderConfig | null>;
262
+ providerLoader?: (providerName: string, ctx: TCtx) => Promise<OidcProviderConfig | null>;
228
263
  /**
229
264
  * Custom collection name for OIDC sessions
230
265
  * Default: 'oidc_sessions'
@@ -246,7 +281,9 @@ export interface OidcPluginOptions {
246
281
  sessionTTL?: number;
247
282
  /**
248
283
  * Encryption key for encrypting stored OIDC tokens
249
- * If not provided, will be derived from first configured provider's client secret
284
+ * If not provided, will be derived from the first configured provider's client secret
285
+ *
286
+ * Required when using storeTokens with public clients (no clientSecret).
250
287
  *
251
288
  * Recommended: Use a dedicated encryption key from environment variables
252
289
  * Must be at least 32 characters
@@ -254,6 +291,36 @@ export interface OidcPluginOptions {
254
291
  * Example: process.env.OIDC_ENCRYPTION_KEY
255
292
  */
256
293
  encryptionKey?: string;
294
+ /**
295
+ * Controls how the JWT token is delivered back to the client on the
296
+ * post-callback redirect (ignored when the caller uses `?response_type=json`).
297
+ *
298
+ * - `"fragment"` (default): appends `#token=...` to the redirectUrl. Best
299
+ * for web browsers — URL fragments are not sent to the server in HTTP
300
+ * requests, which preserves a small but real security property.
301
+ * - `"query"`: appends `?token=...` (or `&token=...`). Required for native
302
+ * mobile flows where the callback comes back via a custom URL scheme,
303
+ * because iOS `ASWebAuthenticationSession` strips URL fragments before
304
+ * delivering the URL to the app.
305
+ * - function: called once per callback to decide the delivery mode. Use
306
+ * this when one app serves both web and native clients. The function
307
+ * receives the resolved redirectUrl and the metadata bag from the
308
+ * initiate request, so the host can route by URL scheme, by
309
+ * `meta.intent`, or any other signal — the plugin makes no assumptions.
310
+ *
311
+ * Default: `"fragment"` (unchanged from prior versions).
312
+ *
313
+ * Example — route native (custom-scheme) clients to query, browsers to fragment:
314
+ * ```typescript
315
+ * tokenDelivery: ({ redirectUrl }) =>
316
+ * /^https?:/i.test(redirectUrl) ? "fragment" : "query",
317
+ * ```
318
+ */
319
+ tokenDelivery?: TokenDeliveryMode | ((params: {
320
+ redirectUrl: string;
321
+ provider: string;
322
+ metadata: Record<string, string>;
323
+ }) => TokenDeliveryMode);
257
324
  /**
258
325
  * Whether to register OIDC routes automatically
259
326
  * If false, you must manually handle OIDC flow
@@ -1 +1 @@
1
- {"version":3,"file":"OidcPluginOptions.d.ts","sourceRoot":"","sources":["../src/OidcPluginOptions.ts"],"names":[],"mappings":"AAAA,OAAO,WAAW,MAAM,uBAAuB,CAAC;AAChD,OAAO,YAAY,MAAM,wBAAwB,CAAC;AAClD,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAE1D;;GAEG;AACH,MAAM,WAAW,SAAS;IACtB;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb;;OAEG;IACH,OAAO,EAAE,MAAM,CAAC;IAEhB;;OAEG;IACH,OAAO,CAAC,EAAE,GAAG,CAAC;CACjB;AAED;;;GAGG;AACH,MAAM,WAAW,2BAA2B;IACxC;;;OAGG;IACH,IAAI,EAAE,GAAG,CAAC;IAEV;;;OAGG;IACH,KAAK,EAAE,MAAM,CAAC;IAEd;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,yBAAyB;IACtC;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,iBAAiB;IAC9B;;;;;;;;;;;;;;;;;;;;;;;OAuBG;IACH,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC;IAE9C;;;;;;;;;;;OAWG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IAEtB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAgDG;IACH,aAAa,EAAE,CACX,MAAM,EAAE;QACJ;;WAEG;QACH,OAAO,EAAE,WAAW,CAAC;QAErB;;;WAGG;QACH,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QAE5B;;WAEG;QACH,QAAQ,EAAE,MAAM,CAAC;QAEjB;;;WAGG;QACH,MAAM,CAAC,EAAE,YAAY,CAAC;KACzB,EACD,GAAG,EAAE,GAAG,KACP,OAAO,CAAC,2BAA2B,CAAC,CAAC;IAE1C;;;;;;;;;;;;;;;;;;;;;;;;;;OA0BG;IACH,WAAW,CAAC,EAAE,CAAC,MAAM,EAAE;QAAE,KAAK,EAAE,SAAS,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,CAAC,yBAAyB,CAAC,CAAC;IAErG;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA4BG;IACH,cAAc,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,CAAC;IAE9E;;;OAGG;IACH,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAEhC;;;OAGG;IACH,yBAAyB,CAAC,EAAE,MAAM,CAAC;IAEnC;;;;;;;OAOG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;;;;;;;OAQG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB;;;;;;;OAOG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;CAC5B"}
1
+ {"version":3,"file":"OidcPluginOptions.d.ts","sourceRoot":"","sources":["../src/OidcPluginOptions.ts"],"names":[],"mappings":"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"}
@@ -18,8 +18,19 @@ export interface OidcProviderConfig {
18
18
  /**
19
19
  * OAuth 2.0 client secret
20
20
  * Provided by the IdP - keep this secure!
21
+ *
22
+ * Optional for public clients using PKCE-only authentication.
23
+ * When omitted, token_endpoint_auth_method defaults to "none".
21
24
  */
22
- clientSecret: string;
25
+ clientSecret?: string;
26
+ /**
27
+ * Token endpoint authentication method
28
+ *
29
+ * - "client_secret_basic": HTTP Basic auth with client_id and client_secret (default when clientSecret is provided)
30
+ * - "client_secret_post": client_id and client_secret in POST body
31
+ * - "none": No client authentication (default when clientSecret is omitted, for public/PKCE-only clients)
32
+ */
33
+ tokenEndpointAuthMethod?: "client_secret_basic" | "client_secret_post" | "none";
23
34
  /**
24
35
  * Callback URL for OAuth redirect
25
36
  * Must match the redirect URI registered with the IdP
@@ -73,5 +84,14 @@ export interface OidcProviderConfig {
73
84
  * e.g., { "department": "custom:department", "role": "custom:role" }
74
85
  */
75
86
  claimMapping?: Record<string, string>;
87
+ /**
88
+ * Whether to fetch additional claims from the UserInfo endpoint.
89
+ * Default: true
90
+ *
91
+ * Set to `false` for IdPs whose UserInfo endpoint is unreliable or
92
+ * returns no additional data beyond what's already in the ID token.
93
+ * When disabled, profiles are built from ID token claims only.
94
+ */
95
+ useUserInfoEndpoint?: boolean;
76
96
  }
77
97
  //# sourceMappingURL=OidcProviderConfig.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"OidcProviderConfig.d.ts","sourceRoot":"","sources":["../src/OidcProviderConfig.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,WAAW,kBAAkB;IAC/B;;;;OAIG;IACH,MAAM,EAAE,MAAM,CAAC;IAEf;;;OAGG;IACH,QAAQ,EAAE,MAAM,CAAC;IAEjB;;;OAGG;IACH,YAAY,EAAE,MAAM,CAAC;IAErB;;;;OAIG;IACH,WAAW,EAAE,MAAM,CAAC;IAEpB;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IAEjB;;;;;OAKG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;OAEG;IAEH;;;;OAIG;IACH,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAE/B;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAE1B;;;;;OAKG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACzC"}
1
+ {"version":3,"file":"OidcProviderConfig.d.ts","sourceRoot":"","sources":["../src/OidcProviderConfig.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,WAAW,kBAAkB;IAC/B;;;;OAIG;IACH,MAAM,EAAE,MAAM,CAAC;IAEf;;;OAGG;IACH,QAAQ,EAAE,MAAM,CAAC;IAEjB;;;;;;OAMG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;;;;;OAMG;IACH,uBAAuB,CAAC,EAAE,qBAAqB,GAAG,oBAAoB,GAAG,MAAM,CAAC;IAEhF;;;;OAIG;IACH,WAAW,EAAE,MAAM,CAAC;IAEpB;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IAEjB;;;;;OAKG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;OAEG;IAEH;;;;OAIG;IACH,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAE/B;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAE1B;;;;;OAKG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAEtC;;;;;;;OAOG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;CACjC"}
@@ -1 +1 @@
1
- {"version":3,"file":"CallbackOidc.d.ts","sourceRoot":"","sources":["../../src/handlers/CallbackOidc.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,UAAU,EAAc,UAAU,EAAwC,MAAM,kBAAkB,CAAC;AAC5G,OAAO,eAAe,MAAM,4BAA4B,CAAC;AAMzD;;GAEG;AACH,UAAU,UAAU;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;CACzB;AAED;;;GAGG;AACH,eAAO,MAAM,KAAK,EAAE,UAGnB,CAAC;AAEF;;;;;;GAMG;AACH,QAAA,MAAM,YAAY,EAAE,UAAU,CAAC,GAAG,EAAE,GAAG,EAAE,UAAU,EAAE,eAAe,CAgNnE,CAAC;AAEF,eAAe,YAAY,CAAC"}
1
+ {"version":3,"file":"CallbackOidc.d.ts","sourceRoot":"","sources":["../../src/handlers/CallbackOidc.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,UAAU,EAAc,UAAU,EAAwC,MAAM,kBAAkB,CAAC;AAC5G,OAAO,eAAe,MAAM,4BAA4B,CAAC;AAOzD;;GAEG;AACH,UAAU,UAAU;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;CACzB;AAED;;;GAGG;AACH,eAAO,MAAM,KAAK,EAAE,UAGnB,CAAC;AAEF;;;;;;GAMG;AACH,QAAA,MAAM,YAAY,EAAE,UAAU,CAAC,GAAG,EAAE,GAAG,EAAE,UAAU,EAAE,eAAe,CA4QnE,CAAC;AAEF,eAAe,YAAY,CAAC"}
@@ -20,6 +20,7 @@ const state_utils_1 = require("../utils/state-utils");
20
20
  const response_utils_1 = require("../utils/response-utils");
21
21
  const encryption_utils_1 = require("../utils/encryption-utils");
22
22
  const error_utils_1 = require("../utils/error-utils");
23
+ const log_1 = require("../log");
23
24
  /**
24
25
  * Route configuration
25
26
  * This handler is registered programmatically by the plugin
@@ -38,19 +39,35 @@ exports.Route = {
38
39
  const CallbackOidc = async ({ ctx, req }) => {
39
40
  const { provider } = req.params;
40
41
  const { code, state, error: oidcError, error_description, response_type } = req.query;
42
+ // Track session outside try/catch so redirectUri and metadata are available in error handling
43
+ let sessionRedirectUri;
44
+ let sessionMetadata = {};
41
45
  try {
42
46
  // Validate provider and response_type
43
47
  (0, error_utils_1.validateProvider)(provider);
44
48
  (0, error_utils_1.validateResponseType)(response_type);
49
+ log_1.oidcLog.debug(`Callback: provider="${provider}" hasCode=${!!code} hasState=${!!state} responseType="${response_type || "redirect"}"`);
45
50
  // Check for OIDC provider errors (e.g., user denied access)
46
51
  if (oidcError) {
52
+ log_1.oidcLog.debug(`Callback: IdP returned error="${oidcError}" description="${error_description || ""}"`);
47
53
  const error = (0, error_utils_1.handleProviderError)({ error: oidcError, error_description });
54
+ // Try to retrieve session redirectUri (state may be available even on IdP errors)
55
+ if (state) {
56
+ const errorSession = await ctx.repos.oidcSessionRepo.getByState(state);
57
+ sessionRedirectUri = errorSession?.redirectUri;
58
+ sessionMetadata = errorSession?.metadata ?? {};
59
+ if (errorSession) {
60
+ await ctx.repos.oidcSessionRepo.deleteBySessionId(errorSession.sessionId);
61
+ }
62
+ }
48
63
  // Call onAuthError callback if provided
49
64
  const { options } = ctx.plugins.oidc;
50
65
  if (options.onAuthError) {
51
66
  const errorResult = await options.onAuthError({
52
67
  error,
53
68
  provider,
69
+ redirectUri: sessionRedirectUri,
70
+ metadata: sessionMetadata,
54
71
  });
55
72
  if (errorResult.redirectUrl) {
56
73
  return {
@@ -72,16 +89,22 @@ const CallbackOidc = async ({ ctx, req }) => {
72
89
  // Find OIDC session by state
73
90
  const session = await ctx.repos.oidcSessionRepo.getByState(state);
74
91
  if (!session) {
92
+ log_1.oidcLog.debug(`Callback: no session found for state (may be expired)`);
75
93
  throw (0, error_utils_1.createOidcError)(error_utils_1.OidcErrorCodes.SESSION_EXPIRED, "OIDC session not found or expired. Please try logging in again.", { state });
76
94
  }
95
+ log_1.oidcLog.debug(`Callback: session found sessionId=${session.sessionId} provider="${session.provider}" redirectUri="${session.redirectUri}"`);
77
96
  // Validate state parameter (CSRF protection)
78
97
  if (!(0, state_utils_1.validateState)(state, session.state)) {
79
98
  throw (0, error_utils_1.createOidcError)(error_utils_1.OidcErrorCodes.INVALID_STATE, "Invalid state parameter. Possible CSRF attack detected.", {
80
99
  providedState: state.substring(0, 10) + "...",
81
100
  });
82
101
  }
102
+ // Track redirectUri and metadata for error handling
103
+ sessionRedirectUri = session.redirectUri;
104
+ sessionMetadata = session.metadata ?? {};
83
105
  // Delete session immediately after validation (one-time use)
84
106
  await ctx.repos.oidcSessionRepo.deleteBySessionId(session.sessionId);
107
+ log_1.oidcLog.debug(`Callback: state validated, session deleted (one-time use)`);
85
108
  // Get plugin options
86
109
  const { options } = ctx.plugins.oidc;
87
110
  // Get provider instance
@@ -97,13 +120,17 @@ const CallbackOidc = async ({ ctx, req }) => {
97
120
  state: session.state,
98
121
  nonce: session.nonce,
99
122
  });
100
- // Build user profile from ID token and UserInfo
101
- const profile = await oidcProvider.buildProfile(tokenSet, true);
123
+ log_1.oidcLog.debug(`Callback: token exchange successful`, `sub="${tokenSet.claims.sub}"`, `iss="${tokenSet.claims.iss}"`, `email="${tokenSet.claims.email || "(none)"}"`, `hasRefreshToken=${!!tokenSet.refreshToken}`, `expiresIn=${tokenSet.expiresIn ?? "(none)"}s`);
124
+ // Build user profile from ID token and (optionally) UserInfo
125
+ const profile = await oidcProvider.buildProfile(tokenSet, oidcProvider.config.useUserInfoEndpoint ?? true);
126
+ log_1.oidcLog.debug(`Callback: profile built id="${profile.id}" email="${profile.email || "(none)"}" name="${profile.name || "(none)"}"`);
102
127
  // Call onAuthSuccess callback to create/link user and generate JWT token
103
128
  const authSuccessParams = {
104
129
  profile,
105
130
  claims: tokenSet.claims,
106
131
  provider,
132
+ redirectUri: session.redirectUri,
133
+ metadata: sessionMetadata,
107
134
  ...(options.storeTokens ? { tokens: tokenSet } : {}),
108
135
  };
109
136
  let authResult;
@@ -121,6 +148,8 @@ const CallbackOidc = async ({ ctx, req }) => {
121
148
  const errorResult = await options.onAuthError({
122
149
  error: oidcError,
123
150
  provider,
151
+ redirectUri: session.redirectUri,
152
+ metadata: sessionMetadata,
124
153
  });
125
154
  if (errorResult.redirectUrl) {
126
155
  return {
@@ -140,7 +169,7 @@ const CallbackOidc = async ({ ctx, req }) => {
140
169
  // Store OIDC connection if token storage is enabled
141
170
  if (options.storeTokens && user && user._id) {
142
171
  // Get encryption secret (use provider client secret or global encryption key)
143
- const staticProviderConfig = options.providers[provider];
172
+ const staticProviderConfig = options.providers?.[provider];
144
173
  const encryptionSecret = options.encryptionKey || staticProviderConfig?.clientSecret;
145
174
  if (!encryptionSecret) {
146
175
  flink_1.log.warn("No encryption secret available, tokens will not be stored");
@@ -155,7 +184,8 @@ const CallbackOidc = async ({ ctx, req }) => {
155
184
  // Create or update OIDC connection
156
185
  const existingConnection = await ctx.repos.oidcConnectionRepo.findByUserAndProvider(user._id, provider);
157
186
  if (existingConnection) {
158
- await ctx.repos.oidcConnectionRepo.updateOne(existingConnection._id, {
187
+ log_1.oidcLog.debug(`Callback: updating existing connection for userId=${user._id} provider="${provider}"`);
188
+ await ctx.repos.oidcConnectionRepo.updateById(existingConnection._id, {
159
189
  accessToken: encryptedAccessToken,
160
190
  idToken: encryptedIdToken,
161
191
  refreshToken: encryptedRefreshToken,
@@ -165,6 +195,7 @@ const CallbackOidc = async ({ ctx, req }) => {
165
195
  });
166
196
  }
167
197
  else {
198
+ log_1.oidcLog.debug(`Callback: creating new connection for userId=${user._id} provider="${provider}" subject="${tokenSet.claims.sub}"`);
168
199
  await ctx.repos.oidcConnectionRepo.create({
169
200
  userId: user._id,
170
201
  provider,
@@ -182,8 +213,20 @@ const CallbackOidc = async ({ ctx, req }) => {
182
213
  }
183
214
  }
184
215
  }
216
+ const finalRedirectUrl = redirectUrl || session.redirectUri;
217
+ log_1.oidcLog.debug(`Callback: auth complete, responding via ${response_type === "json" ? "JSON" : `redirect to "${finalRedirectUrl}"`}`);
218
+ // Resolve token delivery mode: literal, function, or default "fragment".
219
+ // The plugin stays a dumb transport — the host app makes the policy decision.
220
+ const tokenDeliverySetting = options.tokenDelivery ?? "fragment";
221
+ const resolvedTokenDelivery = typeof tokenDeliverySetting === "function"
222
+ ? tokenDeliverySetting({
223
+ redirectUrl: finalRedirectUrl,
224
+ provider,
225
+ metadata: sessionMetadata,
226
+ })
227
+ : tokenDeliverySetting;
185
228
  // Return JWT token in requested format
186
- return (0, response_utils_1.formatTokenResponse)(token, user, redirectUrl || session.redirectUri, response_type);
229
+ return (0, response_utils_1.formatTokenResponse)(token, user, finalRedirectUrl, response_type, resolvedTokenDelivery);
187
230
  }
188
231
  catch (error) {
189
232
  flink_1.log.error("OIDC callback error:", error);
@@ -196,6 +239,8 @@ const CallbackOidc = async ({ ctx, req }) => {
196
239
  const errorResult = await options.onAuthError({
197
240
  error,
198
241
  provider,
242
+ redirectUri: sessionRedirectUri,
243
+ metadata: sessionMetadata,
199
244
  });
200
245
  if (errorResult.redirectUrl) {
201
246
  return {
@@ -1 +1 @@
1
- {"version":3,"file":"InitiateOidc.d.ts","sourceRoot":"","sources":["../../src/handlers/InitiateOidc.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,UAAU,EAAc,UAAU,EAAmC,MAAM,kBAAkB,CAAC;AACvG,OAAO,eAAe,MAAM,4BAA4B,CAAC;AAKzD;;GAEG;AACH,UAAU,UAAU;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;CACzB;AAED;;;GAGG;AACH,eAAO,MAAM,KAAK,EAAE,UAGnB,CAAC;AAEF;;;;;GAKG;AACH,QAAA,MAAM,YAAY,EAAE,UAAU,CAAC,GAAG,EAAE,GAAG,EAAE,UAAU,EAAE,eAAe,CAiEnE,CAAC;AAEF,eAAe,YAAY,CAAC"}
1
+ {"version":3,"file":"InitiateOidc.d.ts","sourceRoot":"","sources":["../../src/handlers/InitiateOidc.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,UAAU,EAAc,UAAU,EAAmC,MAAM,kBAAkB,CAAC;AACvG,OAAO,eAAe,MAAM,4BAA4B,CAAC;AAMzD;;GAEG;AACH,UAAU,UAAU;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;CACzB;AAED;;;GAGG;AACH,eAAO,MAAM,KAAK,EAAE,UAGnB,CAAC;AAEF;;;;;GAKG;AACH,QAAA,MAAM,YAAY,EAAE,UAAU,CAAC,GAAG,EAAE,GAAG,EAAE,UAAU,EAAE,eAAe,CAkFnE,CAAC;AAEF,eAAe,YAAY,CAAC"}
@@ -17,6 +17,7 @@ const flink_1 = require("@flink-app/flink");
17
17
  const state_utils_1 = require("../utils/state-utils");
18
18
  const error_utils_1 = require("../utils/error-utils");
19
19
  const openid_client_1 = require("openid-client");
20
+ const log_1 = require("../log");
20
21
  /**
21
22
  * Route configuration
22
23
  * This handler is registered programmatically by the plugin
@@ -37,6 +38,7 @@ const InitiateOidc = async ({ ctx, req }) => {
37
38
  try {
38
39
  // Validate provider name format
39
40
  (0, error_utils_1.validateProvider)(provider);
41
+ log_1.oidcLog.debug(`Initiate: provider="${provider}" redirectUri="${redirectUri || "(not set)"}"`);
40
42
  // Get provider registry from context
41
43
  const providerRegistry = ctx.oidcProviderRegistry;
42
44
  if (!providerRegistry) {
@@ -47,8 +49,16 @@ const InitiateOidc = async ({ ctx, req }) => {
47
49
  // Get plugin options
48
50
  const { options } = ctx.plugins.oidc;
49
51
  // Determine redirect URI (use provided or default to callback URL)
50
- const staticProviderConfig = options.providers[provider];
52
+ const staticProviderConfig = options.providers?.[provider];
51
53
  const finalRedirectUri = redirectUri || staticProviderConfig?.callbackUrl || `${req.protocol}://${req.get("host")}/oidc/${provider}/callback`;
54
+ // Collect meta.* query params as opaque metadata (prefix stripped)
55
+ const metadata = {};
56
+ for (const [key, value] of Object.entries(req.query)) {
57
+ if (key.startsWith("meta.") && typeof value === "string") {
58
+ metadata[key.slice(5)] = value;
59
+ }
60
+ }
61
+ log_1.oidcLog.debug(`Initiate: resolved redirectUri="${finalRedirectUri}" (source: ${redirectUri ? "query param" : staticProviderConfig?.callbackUrl ? "provider config" : "auto-detected"})`);
52
62
  // Generate cryptographically secure parameters
53
63
  const state = (0, state_utils_1.generateState)();
54
64
  const sessionId = (0, state_utils_1.generateSessionId)();
@@ -62,14 +72,17 @@ const InitiateOidc = async ({ ctx, req }) => {
62
72
  nonce,
63
73
  provider,
64
74
  redirectUri: finalRedirectUri,
75
+ metadata,
65
76
  createdAt: new Date(),
66
77
  });
78
+ log_1.oidcLog.debug(`Initiate: session created sessionId=${sessionId}`);
67
79
  // Build authorization URL with PKCE and nonce
68
80
  const authorizationUrl = await oidcProvider.getAuthorizationUrl({
69
81
  state,
70
82
  codeVerifier,
71
83
  nonce,
72
84
  });
85
+ log_1.oidcLog.debug(`Initiate: redirecting to IdP authorization URL: ${authorizationUrl}`);
73
86
  // Redirect user to provider's authorization page
74
87
  return {
75
88
  status: 302,
package/dist/index.d.ts CHANGED
@@ -22,6 +22,6 @@ export { ProviderRegistry } from "./providers/ProviderRegistry";
22
22
  export { generateState, generateSessionId, generateNonce, validateState } from "./utils/state-utils";
23
23
  export { encryptToken, decryptToken, validateEncryptionSecret } from "./utils/encryption-utils";
24
24
  export { mapClaimsToProfile, extractCustomClaims } from "./utils/claims-mapper";
25
- export { formatTokenResponse } from "./utils/response-utils";
25
+ export { formatTokenResponse, TokenDeliveryMode } from "./utils/response-utils";
26
26
  export { createOidcError, validateProvider, handleProviderError, OidcErrorCodes } from "./utils/error-utils";
27
27
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAG1C,OAAO,EAAE,iBAAiB,EAAE,SAAS,EAAE,2BAA2B,EAAE,yBAAyB,EAAE,MAAM,qBAAqB,CAAC;AAC3H,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAG1D,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAGxD,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,wBAAwB,CAAC;AACjE,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,EAAE,OAAO,IAAI,cAAc,EAAE,MAAM,0BAA0B,CAAC;AACrE,OAAO,EAAE,OAAO,IAAI,eAAe,EAAE,MAAM,2BAA2B,CAAC;AACvE,OAAO,EAAE,OAAO,IAAI,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAGvE,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AACxD,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAGhE,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACrG,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,wBAAwB,EAAE,MAAM,0BAA0B,CAAC;AAChG,OAAO,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAChF,OAAO,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAC7D,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAG1C,OAAO,EAAE,iBAAiB,EAAE,SAAS,EAAE,2BAA2B,EAAE,yBAAyB,EAAE,MAAM,qBAAqB,CAAC;AAC3H,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAG1D,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAGxD,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,wBAAwB,CAAC;AACjE,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,EAAE,OAAO,IAAI,cAAc,EAAE,MAAM,0BAA0B,CAAC;AACrE,OAAO,EAAE,OAAO,IAAI,eAAe,EAAE,MAAM,2BAA2B,CAAC;AACvE,OAAO,EAAE,OAAO,IAAI,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAGvE,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AACxD,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAGhE,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACrG,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,wBAAwB,EAAE,MAAM,0BAA0B,CAAC;AAChG,OAAO,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAChF,OAAO,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAChF,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC"}
package/dist/log.d.ts ADDED
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Named logger for the OIDC plugin.
3
+ *
4
+ * Enables independent log level control via flink.config.js:
5
+ * ```js
6
+ * module.exports = {
7
+ * logging: {
8
+ * components: {
9
+ * "flink.oidc": "debug"
10
+ * }
11
+ * }
12
+ * };
13
+ * ```
14
+ *
15
+ * Or at runtime:
16
+ * ```ts
17
+ * FlinkLogFactory.setComponentLevel("flink.oidc", "debug");
18
+ * ```
19
+ */
20
+ export declare const oidcLog: import("@flink-app/flink").ComponentLogger;
21
+ /**
22
+ * Mask a secret value for safe logging.
23
+ * Shows the first 4 characters followed by **** to help identify which secret
24
+ * is in use without exposing the full value.
25
+ */
26
+ export declare function maskSecret(value: string | undefined): string;
27
+ //# sourceMappingURL=log.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"log.d.ts","sourceRoot":"","sources":["../src/log.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,OAAO,4CAA6C,CAAC;AAElE;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAI5D"}
package/dist/log.js ADDED
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.oidcLog = void 0;
4
+ exports.maskSecret = maskSecret;
5
+ const flink_1 = require("@flink-app/flink");
6
+ /**
7
+ * Named logger for the OIDC plugin.
8
+ *
9
+ * Enables independent log level control via flink.config.js:
10
+ * ```js
11
+ * module.exports = {
12
+ * logging: {
13
+ * components: {
14
+ * "flink.oidc": "debug"
15
+ * }
16
+ * }
17
+ * };
18
+ * ```
19
+ *
20
+ * Or at runtime:
21
+ * ```ts
22
+ * FlinkLogFactory.setComponentLevel("flink.oidc", "debug");
23
+ * ```
24
+ */
25
+ exports.oidcLog = flink_1.FlinkLogFactory.createLogger("flink.oidc");
26
+ /**
27
+ * Mask a secret value for safe logging.
28
+ * Shows the first 4 characters followed by **** to help identify which secret
29
+ * is in use without exposing the full value.
30
+ */
31
+ function maskSecret(value) {
32
+ if (!value)
33
+ return "(not set)";
34
+ if (value.length <= 4)
35
+ return "****";
36
+ return `${value.slice(0, 4)}****`;
37
+ }
@@ -14,7 +14,7 @@ import OidcTokenSet from "../schemas/OidcTokenSet";
14
14
  * - Claims mapping to profile
15
15
  */
16
16
  export declare class OidcProvider {
17
- private config;
17
+ readonly config: OidcProviderConfig;
18
18
  private issuer;
19
19
  private client;
20
20
  private initialized;