@web-ts-toolkit/express-oidc-vault 0.28.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.js ADDED
@@ -0,0 +1,1001 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ DEFAULT_AUTHORIZATION_TRANSACTION_TTL_MS: () => DEFAULT_AUTHORIZATION_TRANSACTION_TTL_MS,
34
+ DEFAULT_EXCHANGE_CODE_TTL_MS: () => DEFAULT_EXCHANGE_CODE_TTL_MS,
35
+ DEFAULT_OIDC_SCOPES: () => DEFAULT_OIDC_SCOPES,
36
+ DEFAULT_OIDC_VAULT_BASE_PATH: () => DEFAULT_OIDC_VAULT_BASE_PATH,
37
+ DEFAULT_SESSION_COOKIE_NAME: () => DEFAULT_SESSION_COOKIE_NAME,
38
+ OIDC_VAULT_ROUTE_PATHS: () => OIDC_VAULT_ROUTE_PATHS,
39
+ OidcVaultStoreConflictError: () => OidcVaultStoreConflictError,
40
+ createOidcVaultAccessTokenMiddleware: () => createOidcVaultAccessTokenMiddleware,
41
+ createOidcVaultJwtAccessTokenValidator: () => createOidcVaultJwtAccessTokenValidator,
42
+ createOidcVaultMiddleware: () => createOidcVaultMiddleware,
43
+ normalizeOidcVaultBasePath: () => normalizeOidcVaultBasePath,
44
+ resolveOidcVaultConfig: () => resolveOidcVaultConfig,
45
+ resolveOidcVaultConfigFromEnv: () => resolveOidcVaultConfigFromEnv
46
+ });
47
+ module.exports = __toCommonJS(index_exports);
48
+ var import_node_crypto = require("crypto");
49
+ var import_express = __toESM(require("express"));
50
+ var import_jose = require("jose");
51
+
52
+ // src/config.ts
53
+ var DEFAULT_OIDC_SCOPES = "openid email profile";
54
+ var MANUAL_CONFIG_REQUIREMENTS = ["authorizationEndpoint", "tokenEndpoint", "jwksUri"];
55
+ var normalizeOptionalString = (value) => {
56
+ const trimmed = value?.trim();
57
+ return trimmed ? trimmed : void 0;
58
+ };
59
+ var withOptionalValue = (target, key, value) => {
60
+ if (value !== void 0) {
61
+ target[key] = value;
62
+ }
63
+ };
64
+ function resolveOidcVaultConfig(config = {}) {
65
+ const issuer = normalizeOptionalString(config.issuer);
66
+ const clientId = normalizeOptionalString(config.clientId);
67
+ const clientSecret = normalizeOptionalString(config.clientSecret);
68
+ const scopes = normalizeOptionalString(config.scopes) ?? DEFAULT_OIDC_SCOPES;
69
+ if (!clientId) {
70
+ throw new Error("OIDC clientId is required.");
71
+ }
72
+ if (issuer) {
73
+ const resolved2 = {
74
+ mode: "issuer",
75
+ issuer,
76
+ scopes
77
+ };
78
+ withOptionalValue(resolved2, "clientId", clientId);
79
+ withOptionalValue(resolved2, "clientSecret", clientSecret);
80
+ return resolved2;
81
+ }
82
+ const resolved = {
83
+ mode: "manual",
84
+ scopes
85
+ };
86
+ withOptionalValue(resolved, "authorizationEndpoint", normalizeOptionalString(config.authorizationEndpoint));
87
+ withOptionalValue(resolved, "tokenEndpoint", normalizeOptionalString(config.tokenEndpoint));
88
+ withOptionalValue(resolved, "userInfoEndpoint", normalizeOptionalString(config.userInfoEndpoint));
89
+ withOptionalValue(resolved, "jwksUri", normalizeOptionalString(config.jwksUri));
90
+ withOptionalValue(resolved, "endSessionEndpoint", normalizeOptionalString(config.endSessionEndpoint));
91
+ withOptionalValue(resolved, "clientId", clientId);
92
+ withOptionalValue(resolved, "clientSecret", clientSecret);
93
+ const missing = MANUAL_CONFIG_REQUIREMENTS.filter((key) => !resolved[key]);
94
+ if (missing.length > 0) {
95
+ throw new Error(`Missing required OIDC configuration for manual mode: ${missing.join(", ")}`);
96
+ }
97
+ return resolved;
98
+ }
99
+ function resolveOidcVaultConfigFromEnv(env = process.env) {
100
+ return resolveOidcVaultConfig({
101
+ issuer: env.OIDC_ISSUER,
102
+ authorizationEndpoint: env.OIDC_AUTHORIZATION_ENDPOINT,
103
+ tokenEndpoint: env.OIDC_TOKEN_ENDPOINT,
104
+ userInfoEndpoint: env.OIDC_USERINFO_ENDPOINT,
105
+ jwksUri: env.OIDC_JWKS_URI,
106
+ endSessionEndpoint: env.OIDC_END_SESSION_ENDPOINT,
107
+ clientId: env.OIDC_CLIENT_ID,
108
+ clientSecret: env.OIDC_CLIENT_SECRET,
109
+ scopes: env.OIDC_SCOPES
110
+ });
111
+ }
112
+
113
+ // src/types.ts
114
+ var OidcVaultStoreConflictError = class extends Error {
115
+ constructor(message = "OIDC vault store operation conflicted with concurrent state changes.") {
116
+ super(message);
117
+ this.name = "OidcVaultStoreConflictError";
118
+ }
119
+ };
120
+
121
+ // src/index.ts
122
+ var DEFAULT_OIDC_VAULT_BASE_PATH = "/auth/oidc";
123
+ var DEFAULT_AUTHORIZATION_TRANSACTION_TTL_MS = 10 * 60 * 1e3;
124
+ var DEFAULT_EXCHANGE_CODE_TTL_MS = 30 * 1e3;
125
+ var DEFAULT_SESSION_COOKIE_NAME = "oidc_vault_session";
126
+ var OIDC_VAULT_ROUTE_PATHS = {
127
+ login: "/login",
128
+ callback: "/callback",
129
+ exchange: "/exchange",
130
+ refresh: "/refresh",
131
+ logout: "/logout",
132
+ "backchannel-logout": "/backchannel-logout"
133
+ };
134
+ var discoveryCache = /* @__PURE__ */ new Map();
135
+ var jwksCache = /* @__PURE__ */ new Map();
136
+ var OidcVaultHttpError = class extends Error {
137
+ constructor(status, code, message) {
138
+ super(message);
139
+ this.status = status;
140
+ this.code = code;
141
+ }
142
+ };
143
+ function normalizeOidcVaultBasePath(value) {
144
+ if (!value || value === "/") {
145
+ return DEFAULT_OIDC_VAULT_BASE_PATH;
146
+ }
147
+ return `/${value.replace(/^\/+|\/+$/g, "")}`;
148
+ }
149
+ var isRecord = (value) => typeof value === "object" && value !== null;
150
+ var isString = (value) => typeof value === "string" && value.length > 0;
151
+ var getNow = (options) => (options.now ?? Date.now)();
152
+ var getSessionTransport = (options) => options.sessionTransport ?? "body";
153
+ var usesCookieTransport = (options) => getSessionTransport(options) === "cookie";
154
+ var createOpaqueId = (prefix) => `${prefix}_${(0, import_node_crypto.randomBytes)(16).toString("base64url")}`;
155
+ var createPkceVerifier = () => (0, import_node_crypto.randomBytes)(32).toString("base64url");
156
+ var createPkceChallenge = (verifier) => (0, import_node_crypto.createHash)("sha256").update(verifier).digest("base64url");
157
+ var toErrorPayload = (error) => {
158
+ if (error instanceof OidcVaultHttpError) {
159
+ return {
160
+ status: error.status,
161
+ code: error.code,
162
+ message: error.message
163
+ };
164
+ }
165
+ return {
166
+ status: 500,
167
+ code: "OIDC_VAULT_INTERNAL_ERROR",
168
+ message: error instanceof Error ? error.message : "Unexpected OIDC vault error."
169
+ };
170
+ };
171
+ var getRequestOrigin = (req) => `${req.protocol}://${req.get("host") ?? "localhost"}`;
172
+ var getCallbackUri = (req, basePath) => `${getRequestOrigin(req)}${basePath}${OIDC_VAULT_ROUTE_PATHS.callback}`;
173
+ var getRequiredString = (value, message, code, status = 400) => {
174
+ if (!isString(value)) {
175
+ throw new OidcVaultHttpError(status, code, message);
176
+ }
177
+ return value;
178
+ };
179
+ var parseCookieHeader = (headerValue) => {
180
+ if (!headerValue) {
181
+ return {};
182
+ }
183
+ return headerValue.split(";").reduce((cookies, part) => {
184
+ const separatorIndex = part.indexOf("=");
185
+ if (separatorIndex === -1) {
186
+ return cookies;
187
+ }
188
+ const name = part.slice(0, separatorIndex).trim();
189
+ const value = part.slice(separatorIndex + 1).trim();
190
+ if (!name) {
191
+ return cookies;
192
+ }
193
+ cookies[name] = decodeURIComponent(value);
194
+ return cookies;
195
+ }, {});
196
+ };
197
+ var resolveCookieOptions = (options) => {
198
+ const cookieOptions = options.cookie ?? {};
199
+ const deploymentMode = cookieOptions.deploymentMode ?? "same-origin";
200
+ const sameSite = cookieOptions.sameSite ?? (deploymentMode === "cross-site" ? "none" : "lax");
201
+ const secure = cookieOptions.secure ?? (sameSite === "none" || deploymentMode === "cross-site");
202
+ return {
203
+ name: cookieOptions.name ?? DEFAULT_SESSION_COOKIE_NAME,
204
+ sameSite,
205
+ secure,
206
+ domain: cookieOptions.domain,
207
+ path: cookieOptions.path ?? "/",
208
+ httpOnly: cookieOptions.httpOnly ?? true
209
+ };
210
+ };
211
+ var usesCrossSiteCookieTransport = (options) => usesCookieTransport(options) && resolveCookieOptions(options).sameSite === "none";
212
+ var resolveTrustedOrigins = (options) => new Set((options.trustedOrigins ?? []).map((value) => new URL(value).origin));
213
+ var getRequestSourceOrigin = (req) => {
214
+ const origin = req.get("origin");
215
+ if (isString(origin)) {
216
+ return origin;
217
+ }
218
+ const referer = req.get("referer");
219
+ if (!isString(referer)) {
220
+ return void 0;
221
+ }
222
+ try {
223
+ return new URL(referer).origin;
224
+ } catch {
225
+ return void 0;
226
+ }
227
+ };
228
+ var assertTrustedOrigin = (req, options, trustedOrigins, action) => {
229
+ if (!usesCrossSiteCookieTransport(options)) {
230
+ return;
231
+ }
232
+ const origin = getRequestSourceOrigin(req);
233
+ if (!origin || !trustedOrigins.has(origin)) {
234
+ throw new OidcVaultHttpError(
235
+ 403,
236
+ "OIDC_VAULT_UNTRUSTED_ORIGIN",
237
+ `${action === "refresh" ? "Refresh" : "Logout"} request origin is not trusted.`
238
+ );
239
+ }
240
+ };
241
+ var validateOidcVaultOptions = (options) => {
242
+ const config = resolveOidcVaultConfig(options.config);
243
+ const trustedOrigins = resolveTrustedOrigins(options);
244
+ if (usesCrossSiteCookieTransport(options) && trustedOrigins.size === 0) {
245
+ throw new Error("trustedOrigins is required when using cross-site cookie transport.");
246
+ }
247
+ return { config, trustedOrigins };
248
+ };
249
+ var serializeCookie = (name, value, options, overrides) => {
250
+ const sameSite = options.sameSite.charAt(0).toUpperCase() + options.sameSite.slice(1);
251
+ const parts = [`${name}=${encodeURIComponent(value)}`, `Path=${options.path}`, `SameSite=${sameSite}`];
252
+ if (options.httpOnly) {
253
+ parts.push("HttpOnly");
254
+ }
255
+ if (options.secure || options.sameSite === "none") {
256
+ parts.push("Secure");
257
+ }
258
+ if (options.domain) {
259
+ parts.push(`Domain=${options.domain}`);
260
+ }
261
+ if (typeof overrides?.maxAge === "number") {
262
+ parts.push(`Max-Age=${overrides.maxAge}`);
263
+ }
264
+ if (overrides?.expires) {
265
+ parts.push(`Expires=${overrides.expires.toUTCString()}`);
266
+ }
267
+ return parts.join("; ");
268
+ };
269
+ var setSessionCookie = (res, options, sessionId) => {
270
+ const cookieOptions = resolveCookieOptions(options);
271
+ res.append("Set-Cookie", serializeCookie(cookieOptions.name, sessionId, cookieOptions));
272
+ };
273
+ var clearSessionCookie = (res, options) => {
274
+ const cookieOptions = resolveCookieOptions(options);
275
+ res.append(
276
+ "Set-Cookie",
277
+ serializeCookie(cookieOptions.name, "", cookieOptions, {
278
+ maxAge: 0,
279
+ expires: /* @__PURE__ */ new Date(0)
280
+ })
281
+ );
282
+ };
283
+ var getSessionIdFromCookie = (req, options) => {
284
+ const cookies = parseCookieHeader(req.headers.cookie);
285
+ return cookies[resolveCookieOptions(options).name];
286
+ };
287
+ var getSessionIdFromRequest = (req, options, action) => {
288
+ const body = getBody(req);
289
+ if (usesCookieTransport(options)) {
290
+ const cookieSessionId = getSessionIdFromCookie(req, options);
291
+ if (isString(cookieSessionId)) {
292
+ return cookieSessionId;
293
+ }
294
+ if (isString(body.sessionId)) {
295
+ return body.sessionId;
296
+ }
297
+ throw new OidcVaultHttpError(
298
+ 400,
299
+ "OIDC_VAULT_MISSING_SESSION_ID",
300
+ `${action === "refresh" ? "Refresh" : "Logout"} request is missing the session cookie.`
301
+ );
302
+ }
303
+ return getRequiredString(
304
+ body.sessionId,
305
+ `${action === "refresh" ? "Refresh" : "Logout"} request is missing sessionId.`,
306
+ "OIDC_VAULT_MISSING_SESSION_ID"
307
+ );
308
+ };
309
+ var createExchangeResponse = (options, session, issuedToken) => ({
310
+ sessionId: usesCookieTransport(options) ? void 0 : session.sessionId,
311
+ user: session.user,
312
+ ...issuedToken
313
+ });
314
+ var extractBearerToken = (authorizationHeader) => {
315
+ if (!authorizationHeader) {
316
+ throw new OidcVaultHttpError(401, "OIDC_VAULT_MISSING_BEARER_TOKEN", "Missing bearer token.");
317
+ }
318
+ const [scheme, token, extra] = authorizationHeader.trim().split(/\s+/);
319
+ if (scheme?.toLowerCase() !== "bearer" || !token || extra) {
320
+ throw new OidcVaultHttpError(
321
+ 401,
322
+ "OIDC_VAULT_INVALID_AUTHORIZATION_HEADER",
323
+ "Authorization header must use the Bearer scheme."
324
+ );
325
+ }
326
+ return token;
327
+ };
328
+ var setBearerChallengeHeader = (res) => {
329
+ res.setHeader("WWW-Authenticate", "Bearer");
330
+ };
331
+ var BACKCHANNEL_LOGOUT_EVENT_CLAIM = "http://schemas.openid.net/event/backchannel-logout";
332
+ var defaultJwtClaimsMapper = (claims) => {
333
+ const subject = getRequiredString(
334
+ claims.sub,
335
+ "JWT access token is missing sub.",
336
+ "OIDC_VAULT_INVALID_ACCESS_TOKEN",
337
+ 401
338
+ );
339
+ return {
340
+ subject,
341
+ sessionId: typeof claims.sid === "string" ? claims.sid : void 0,
342
+ scope: typeof claims.scope === "string" ? claims.scope : void 0,
343
+ claims
344
+ };
345
+ };
346
+ var buildWellKnownUrl = (issuer) => {
347
+ const normalizedIssuer = issuer.endsWith("/") ? issuer : `${issuer}/`;
348
+ return new URL(".well-known/openid-configuration", normalizedIssuer);
349
+ };
350
+ var resolveJwks = (jwksUri) => {
351
+ let jwks = jwksCache.get(jwksUri);
352
+ if (!jwks) {
353
+ jwks = (0, import_jose.createRemoteJWKSet)(new URL(jwksUri));
354
+ jwksCache.set(jwksUri, jwks);
355
+ }
356
+ return jwks;
357
+ };
358
+ var appendQueryParam = (url, name, value) => {
359
+ if (value) {
360
+ url.searchParams.set(name, value);
361
+ }
362
+ };
363
+ var buildAuthorizationUrl = (metadata, transaction, redirectUri) => {
364
+ const url = new URL(metadata.authorizationEndpoint);
365
+ appendQueryParam(url, "response_type", "code");
366
+ appendQueryParam(url, "client_id", metadata.clientId);
367
+ appendQueryParam(url, "redirect_uri", redirectUri);
368
+ appendQueryParam(url, "scope", metadata.scopes);
369
+ appendQueryParam(url, "state", transaction.state);
370
+ appendQueryParam(url, "nonce", transaction.nonce);
371
+ appendQueryParam(url, "code_challenge", transaction.codeChallenge);
372
+ appendQueryParam(url, "code_challenge_method", "S256");
373
+ return url.toString();
374
+ };
375
+ var buildLogoutUrl = (endSessionEndpoint, idTokenHint, postLogoutRedirectUri) => {
376
+ const url = new URL(endSessionEndpoint);
377
+ appendQueryParam(url, "id_token_hint", idTokenHint);
378
+ appendQueryParam(url, "post_logout_redirect_uri", postLogoutRedirectUri);
379
+ return url.toString();
380
+ };
381
+ var toBasicAuthorization = (clientId, clientSecret) => `Basic ${Buffer.from(`${clientId}:${clientSecret}`).toString("base64")}`;
382
+ var readJsonResponse = async (response, errorCode) => {
383
+ const text = await response.text();
384
+ if (!text) {
385
+ return {};
386
+ }
387
+ try {
388
+ return JSON.parse(text);
389
+ } catch {
390
+ throw new OidcVaultHttpError(response.status, errorCode, text);
391
+ }
392
+ };
393
+ var getLogoutTokenFromRequest = (req) => {
394
+ const body = getBody(req);
395
+ return getRequiredString(
396
+ body.logout_token,
397
+ "Backchannel logout request is missing logout_token.",
398
+ "OIDC_VAULT_MISSING_LOGOUT_TOKEN"
399
+ );
400
+ };
401
+ var createHookContext = (route, req, res, session, metadata) => ({ route, req, res, session, metadata });
402
+ var mergeUserProfile = (sub, idTokenClaims, userInfo) => ({
403
+ ...idTokenClaims,
404
+ ...userInfo ?? {},
405
+ sub
406
+ });
407
+ var resolveReturnTo = (req, options) => {
408
+ const rawReturnTo = req.query.returnTo;
409
+ if (!isString(rawReturnTo)) {
410
+ return void 0;
411
+ }
412
+ const configuredFrontendUri = options.frontendRedirectUri;
413
+ if (!configuredFrontendUri) {
414
+ throw new OidcVaultHttpError(
415
+ 400,
416
+ "OIDC_VAULT_INVALID_RETURN_TO",
417
+ "Custom returnTo requires a configured frontendRedirectUri."
418
+ );
419
+ }
420
+ const configuredUrl = new URL(configuredFrontendUri);
421
+ const resolvedUrl = rawReturnTo.startsWith("/") ? new URL(rawReturnTo, configuredUrl) : new URL(rawReturnTo);
422
+ if (resolvedUrl.origin !== configuredUrl.origin) {
423
+ throw new OidcVaultHttpError(
424
+ 400,
425
+ "OIDC_VAULT_INVALID_RETURN_TO",
426
+ "returnTo must stay on the configured frontend origin."
427
+ );
428
+ }
429
+ return resolvedUrl.toString();
430
+ };
431
+ var appendCodeToRedirectUri = (redirectUri, code) => {
432
+ const url = new URL(redirectUri);
433
+ url.searchParams.set("code", code);
434
+ return url.toString();
435
+ };
436
+ var getBody = (req) => isRecord(req.body) ? req.body : {};
437
+ var withIssuedToken = async (req, res, options, session) => {
438
+ if (!options.tokenIssuer) {
439
+ return {};
440
+ }
441
+ return options.tokenIssuer.issue({ req, res, session });
442
+ };
443
+ var resolveFrontendRedirectUri = (transaction, options) => {
444
+ const redirectUri = transaction.returnTo ?? options.frontendRedirectUri;
445
+ if (!redirectUri) {
446
+ throw new OidcVaultHttpError(
447
+ 500,
448
+ "OIDC_VAULT_MISSING_FRONTEND_REDIRECT_URI",
449
+ "A frontendRedirectUri or login returnTo is required to complete the callback flow."
450
+ );
451
+ }
452
+ return redirectUri;
453
+ };
454
+ var shouldFetchUserInfo = (options, metadata) => options.fetchUserInfo !== false && Boolean(metadata.userInfoEndpoint);
455
+ async function callHook(route, hook, req, res, session, metadata) {
456
+ if (!hook) {
457
+ return;
458
+ }
459
+ await hook(createHookContext(route, req, res, session, metadata));
460
+ }
461
+ async function handleRouteError(route, req, res, next, options, error) {
462
+ try {
463
+ await options.hooks?.onError?.({
464
+ ...createHookContext(route, req, res),
465
+ error
466
+ });
467
+ } catch {
468
+ }
469
+ if (res.headersSent) {
470
+ next(error);
471
+ return;
472
+ }
473
+ const payload = toErrorPayload(error);
474
+ res.status(payload.status).json({
475
+ code: payload.code,
476
+ message: payload.message
477
+ });
478
+ }
479
+ function createAsyncHandler(route, options, handler) {
480
+ return async (req, res, next) => {
481
+ try {
482
+ await handler(req, res, next);
483
+ } catch (error) {
484
+ await handleRouteError(route, req, res, next, options, error);
485
+ }
486
+ };
487
+ }
488
+ async function discoverIssuerMetadata(issuer) {
489
+ let discoveryPromise = discoveryCache.get(issuer);
490
+ if (!discoveryPromise) {
491
+ discoveryPromise = (async () => {
492
+ const discoveryUrl = buildWellKnownUrl(issuer);
493
+ const response = await fetch(discoveryUrl);
494
+ if (!response.ok) {
495
+ const errorText = await response.text();
496
+ throw new OidcVaultHttpError(
497
+ 502,
498
+ "OIDC_VAULT_DISCOVERY_FAILED",
499
+ errorText || `OIDC discovery failed with status ${response.status}.`
500
+ );
501
+ }
502
+ const discovered = await response.json();
503
+ const authorizationEndpoint = getRequiredString(
504
+ discovered.authorization_endpoint,
505
+ "OIDC discovery response is missing authorization_endpoint.",
506
+ "OIDC_VAULT_DISCOVERY_INVALID",
507
+ 502
508
+ );
509
+ const tokenEndpoint = getRequiredString(
510
+ discovered.token_endpoint,
511
+ "OIDC discovery response is missing token_endpoint.",
512
+ "OIDC_VAULT_DISCOVERY_INVALID",
513
+ 502
514
+ );
515
+ const jwksUri = getRequiredString(
516
+ discovered.jwks_uri,
517
+ "OIDC discovery response is missing jwks_uri.",
518
+ "OIDC_VAULT_DISCOVERY_INVALID",
519
+ 502
520
+ );
521
+ return {
522
+ issuer: getRequiredString(
523
+ discovered.issuer,
524
+ "OIDC discovery response is missing issuer.",
525
+ "OIDC_VAULT_DISCOVERY_INVALID",
526
+ 502
527
+ ),
528
+ authorizationEndpoint,
529
+ tokenEndpoint,
530
+ jwksUri,
531
+ userInfoEndpoint: typeof discovered.userinfo_endpoint === "string" ? discovered.userinfo_endpoint : void 0,
532
+ endSessionEndpoint: typeof discovered.end_session_endpoint === "string" ? discovered.end_session_endpoint : void 0
533
+ };
534
+ })();
535
+ discoveryCache.set(issuer, discoveryPromise);
536
+ discoveryPromise.catch(() => {
537
+ if (discoveryCache.get(issuer) === discoveryPromise) {
538
+ discoveryCache.delete(issuer);
539
+ }
540
+ });
541
+ }
542
+ return discoveryPromise;
543
+ }
544
+ async function resolveProviderMetadata(config) {
545
+ if (config.mode === "manual") {
546
+ return {
547
+ issuer: config.issuer,
548
+ authorizationEndpoint: config.authorizationEndpoint,
549
+ tokenEndpoint: config.tokenEndpoint,
550
+ jwksUri: config.jwksUri,
551
+ userInfoEndpoint: config.userInfoEndpoint,
552
+ endSessionEndpoint: config.endSessionEndpoint,
553
+ clientId: config.clientId,
554
+ clientSecret: config.clientSecret,
555
+ scopes: config.scopes
556
+ };
557
+ }
558
+ if (!config.issuer) {
559
+ throw new OidcVaultHttpError(500, "OIDC_VAULT_INVALID_CONFIG", "Issuer discovery requires an issuer URL.");
560
+ }
561
+ return {
562
+ ...await discoverIssuerMetadata(config.issuer),
563
+ clientId: config.clientId,
564
+ clientSecret: config.clientSecret,
565
+ scopes: config.scopes
566
+ };
567
+ }
568
+ async function requestToken(metadata, params) {
569
+ const clientId = metadata.clientId;
570
+ if (!clientId) {
571
+ throw new OidcVaultHttpError(500, "OIDC_VAULT_MISSING_CLIENT_ID", "OIDC clientId is required.");
572
+ }
573
+ const body = new URLSearchParams();
574
+ body.set("client_id", clientId);
575
+ for (const [key, value] of Object.entries(params)) {
576
+ if (value !== void 0) {
577
+ body.set(key, value);
578
+ }
579
+ }
580
+ const headers = {
581
+ "content-type": "application/x-www-form-urlencoded"
582
+ };
583
+ if (metadata.clientSecret) {
584
+ headers.authorization = toBasicAuthorization(clientId, metadata.clientSecret);
585
+ }
586
+ const response = await fetch(metadata.tokenEndpoint, {
587
+ method: "POST",
588
+ headers,
589
+ body
590
+ });
591
+ const json = await readJsonResponse(response, "OIDC_VAULT_TOKEN_REQUEST_FAILED");
592
+ if (!response.ok) {
593
+ throw new OidcVaultHttpError(
594
+ 502,
595
+ "OIDC_VAULT_TOKEN_REQUEST_FAILED",
596
+ typeof json.error_description === "string" ? json.error_description : `OIDC token request failed with status ${response.status}.`
597
+ );
598
+ }
599
+ return json;
600
+ }
601
+ async function fetchUserInfo(metadata, accessToken) {
602
+ if (!metadata.userInfoEndpoint) {
603
+ return {};
604
+ }
605
+ const response = await fetch(metadata.userInfoEndpoint, {
606
+ headers: {
607
+ authorization: `Bearer ${accessToken}`
608
+ }
609
+ });
610
+ const json = await readJsonResponse(response, "OIDC_VAULT_USERINFO_FAILED");
611
+ if (!response.ok) {
612
+ throw new OidcVaultHttpError(
613
+ 502,
614
+ "OIDC_VAULT_USERINFO_FAILED",
615
+ `OIDC userinfo request failed with status ${response.status}.`
616
+ );
617
+ }
618
+ return json;
619
+ }
620
+ async function verifyIdToken(metadata, idToken, nonce) {
621
+ if (!metadata.jwksUri) {
622
+ throw new OidcVaultHttpError(500, "OIDC_VAULT_MISSING_JWKS_URI", "OIDC jwksUri is required to validate id_token.");
623
+ }
624
+ if (!metadata.clientId) {
625
+ throw new OidcVaultHttpError(
626
+ 500,
627
+ "OIDC_VAULT_MISSING_CLIENT_ID",
628
+ "OIDC clientId is required to validate id_token."
629
+ );
630
+ }
631
+ const jwks = resolveJwks(metadata.jwksUri);
632
+ const result = await (0, import_jose.jwtVerify)(idToken, jwks, {
633
+ audience: metadata.clientId,
634
+ issuer: metadata.issuer
635
+ });
636
+ if (nonce && result.payload.nonce !== nonce) {
637
+ throw new OidcVaultHttpError(502, "OIDC_VAULT_INVALID_ID_TOKEN", "OIDC id_token nonce validation failed.");
638
+ }
639
+ return result.payload;
640
+ }
641
+ async function verifyBackchannelLogoutToken(metadata, logoutToken) {
642
+ if (!metadata.jwksUri) {
643
+ throw new OidcVaultHttpError(
644
+ 500,
645
+ "OIDC_VAULT_MISSING_JWKS_URI",
646
+ "OIDC jwksUri is required to validate logout_token."
647
+ );
648
+ }
649
+ if (!metadata.clientId) {
650
+ throw new OidcVaultHttpError(
651
+ 500,
652
+ "OIDC_VAULT_MISSING_CLIENT_ID",
653
+ "OIDC clientId is required to validate logout_token."
654
+ );
655
+ }
656
+ const jwks = resolveJwks(metadata.jwksUri);
657
+ const result = await (0, import_jose.jwtVerify)(logoutToken, jwks, {
658
+ audience: metadata.clientId,
659
+ issuer: metadata.issuer
660
+ });
661
+ const claims = result.payload;
662
+ if (claims.nonce !== void 0) {
663
+ throw new OidcVaultHttpError(
664
+ 400,
665
+ "OIDC_VAULT_INVALID_LOGOUT_TOKEN",
666
+ "Backchannel logout token must not contain nonce."
667
+ );
668
+ }
669
+ if (!isRecord(claims.events) || !(BACKCHANNEL_LOGOUT_EVENT_CLAIM in claims.events)) {
670
+ throw new OidcVaultHttpError(
671
+ 400,
672
+ "OIDC_VAULT_INVALID_LOGOUT_TOKEN",
673
+ "Backchannel logout token is missing the required event claim."
674
+ );
675
+ }
676
+ if (!isString(claims.sid) && !isString(claims.sub)) {
677
+ throw new OidcVaultHttpError(
678
+ 400,
679
+ "OIDC_VAULT_INVALID_LOGOUT_TOKEN",
680
+ "Backchannel logout token must include sid or sub."
681
+ );
682
+ }
683
+ if (!isString(claims.jti)) {
684
+ throw new OidcVaultHttpError(400, "OIDC_VAULT_INVALID_LOGOUT_TOKEN", "Backchannel logout token must include jti.");
685
+ }
686
+ return claims;
687
+ }
688
+ var createLoginHandler = (options, config, basePath) => createAsyncHandler("login", options, async (req, res) => {
689
+ const metadata = await resolveProviderMetadata(config);
690
+ await callHook("login", options.hooks?.onLoginStart, req, res, void 0, { provider: metadata });
691
+ const now = getNow(options);
692
+ const pkceVerifier = createPkceVerifier();
693
+ const transaction = {
694
+ state: createOpaqueId("state"),
695
+ nonce: createOpaqueId("nonce"),
696
+ pkceVerifier,
697
+ codeChallenge: createPkceChallenge(pkceVerifier),
698
+ returnTo: resolveReturnTo(req, options),
699
+ createdAt: now,
700
+ expiresAt: now + (options.authorizationTransactionTtlMs ?? DEFAULT_AUTHORIZATION_TRANSACTION_TTL_MS)
701
+ };
702
+ await options.storeProvider.createAuthorizationTransaction(transaction);
703
+ const authorizationUrl = buildAuthorizationUrl(metadata, transaction, getCallbackUri(req, basePath));
704
+ await callHook("login", options.hooks?.onAuthorizationUrl, req, res, void 0, {
705
+ authorizationUrl,
706
+ state: transaction.state
707
+ });
708
+ res.redirect(302, authorizationUrl);
709
+ });
710
+ var createCallbackHandler = (options, config, basePath) => createAsyncHandler("callback", options, async (req, res) => {
711
+ if (isString(req.query.error)) {
712
+ throw new OidcVaultHttpError(400, "OIDC_VAULT_CALLBACK_ERROR", req.query.error);
713
+ }
714
+ const code = getRequiredString(
715
+ req.query.code,
716
+ "OIDC callback is missing the authorization code.",
717
+ "OIDC_VAULT_MISSING_CODE"
718
+ );
719
+ const state = getRequiredString(req.query.state, "OIDC callback is missing state.", "OIDC_VAULT_MISSING_STATE");
720
+ const transaction = await options.storeProvider.consumeAuthorizationTransaction(state);
721
+ if (!transaction) {
722
+ throw new OidcVaultHttpError(400, "OIDC_VAULT_INVALID_STATE", "OIDC state is invalid or expired.");
723
+ }
724
+ const metadata = await resolveProviderMetadata(config);
725
+ const tokenResponse = await requestToken(metadata, {
726
+ grant_type: "authorization_code",
727
+ code,
728
+ code_verifier: transaction.pkceVerifier,
729
+ redirect_uri: getCallbackUri(req, basePath)
730
+ });
731
+ if (!isString(tokenResponse.id_token)) {
732
+ throw new OidcVaultHttpError(502, "OIDC_VAULT_MISSING_ID_TOKEN", "OIDC token response is missing id_token.");
733
+ }
734
+ if (!isString(tokenResponse.refresh_token)) {
735
+ throw new OidcVaultHttpError(
736
+ 502,
737
+ "OIDC_VAULT_MISSING_REFRESH_TOKEN",
738
+ "OIDC token response is missing refresh_token."
739
+ );
740
+ }
741
+ await callHook("callback", options.hooks?.onCallbackTokens, req, res, void 0, {
742
+ hasAccessToken: Boolean(tokenResponse.access_token),
743
+ hasRefreshToken: true,
744
+ hasIdToken: true
745
+ });
746
+ const claims = await verifyIdToken(metadata, tokenResponse.id_token, transaction.nonce);
747
+ const subject = getRequiredString(claims.sub, "OIDC id_token is missing sub.", "OIDC_VAULT_INVALID_ID_TOKEN", 502);
748
+ const userInfo = shouldFetchUserInfo(options, metadata) && isString(tokenResponse.access_token) ? await fetchUserInfo(metadata, tokenResponse.access_token) : void 0;
749
+ if (userInfo) {
750
+ await callHook("callback", options.hooks?.onUserInfo, req, res, void 0, { subject });
751
+ }
752
+ const now = getNow(options);
753
+ const sessionId = createOpaqueId("sess");
754
+ const session = {
755
+ sessionId,
756
+ subject,
757
+ providerSessionId: typeof claims.sid === "string" ? claims.sid : void 0,
758
+ provider: {
759
+ issuer: metadata.issuer ?? (typeof claims.iss === "string" ? claims.iss : void 0),
760
+ clientId: metadata.clientId
761
+ },
762
+ refreshToken: tokenResponse.refresh_token,
763
+ idToken: tokenResponse.id_token,
764
+ accessToken: isString(tokenResponse.access_token) ? tokenResponse.access_token : void 0,
765
+ scope: typeof tokenResponse.scope === "string" ? tokenResponse.scope : metadata.scopes,
766
+ expiresAt: typeof tokenResponse.expires_in === "number" ? now + tokenResponse.expires_in * 1e3 : void 0,
767
+ createdAt: now,
768
+ updatedAt: now,
769
+ user: mergeUserProfile(subject, claims, userInfo),
770
+ metadata: {
771
+ tokenType: tokenResponse.token_type
772
+ }
773
+ };
774
+ await callHook("callback", options.hooks?.onBeforeSessionCreate, req, res, session, { subject });
775
+ const createdSession = await options.storeProvider.createSession(session);
776
+ await callHook("callback", options.hooks?.onSessionCreated, req, res, createdSession, { subject });
777
+ const exchangeCode = createOpaqueId("code");
778
+ await options.storeProvider.createExchangeCode({
779
+ code: exchangeCode,
780
+ sessionId: createdSession.sessionId,
781
+ returnTo: transaction.returnTo,
782
+ createdAt: now,
783
+ expiresAt: now + (options.exchangeCodeTtlMs ?? DEFAULT_EXCHANGE_CODE_TTL_MS)
784
+ });
785
+ res.redirect(302, appendCodeToRedirectUri(resolveFrontendRedirectUri(transaction, options), exchangeCode));
786
+ });
787
+ var createExchangeHandler = (options) => createAsyncHandler("exchange", options, async (req, res) => {
788
+ const body = getBody(req);
789
+ const code = getRequiredString(body.code, "Exchange request is missing code.", "OIDC_VAULT_MISSING_EXCHANGE_CODE");
790
+ const exchangeRecord = await options.storeProvider.consumeExchangeCode(code);
791
+ if (!exchangeRecord) {
792
+ throw new OidcVaultHttpError(400, "OIDC_VAULT_INVALID_EXCHANGE_CODE", "Exchange code is invalid or expired.");
793
+ }
794
+ const session = await options.storeProvider.getSession(exchangeRecord.sessionId);
795
+ if (!session) {
796
+ throw new OidcVaultHttpError(401, "OIDC_VAULT_INVALID_SESSION", "Session is missing or expired.");
797
+ }
798
+ const issuedToken = await withIssuedToken(req, res, options, session);
799
+ if (usesCookieTransport(options)) {
800
+ setSessionCookie(res, options, session.sessionId);
801
+ }
802
+ const response = createExchangeResponse(options, session, issuedToken);
803
+ res.status(200).json(response);
804
+ });
805
+ var createRefreshHandler = (options, config, trustedOrigins) => createAsyncHandler("refresh", options, async (req, res) => {
806
+ assertTrustedOrigin(req, options, trustedOrigins, "refresh");
807
+ const sessionId = getSessionIdFromRequest(req, options, "refresh");
808
+ const currentSession = await options.storeProvider.getSession(sessionId);
809
+ if (!currentSession) {
810
+ if (usesCookieTransport(options)) {
811
+ clearSessionCookie(res, options);
812
+ }
813
+ throw new OidcVaultHttpError(401, "OIDC_VAULT_INVALID_SESSION", "Session is missing or expired.");
814
+ }
815
+ const metadata = await resolveProviderMetadata(config);
816
+ const tokenResponse = await requestToken(metadata, {
817
+ grant_type: "refresh_token",
818
+ refresh_token: currentSession.refreshToken
819
+ });
820
+ const idToken = isString(tokenResponse.id_token) ? tokenResponse.id_token : currentSession.idToken;
821
+ const claims = await verifyIdToken(metadata, idToken);
822
+ const subject = getRequiredString(claims.sub, "OIDC id_token is missing sub.", "OIDC_VAULT_INVALID_ID_TOKEN", 502);
823
+ const userInfo = shouldFetchUserInfo(options, metadata) && isString(tokenResponse.access_token) ? await fetchUserInfo(metadata, tokenResponse.access_token) : void 0;
824
+ const now = getNow(options);
825
+ const nextSession = {
826
+ ...currentSession,
827
+ sessionId: createOpaqueId("sess"),
828
+ subject,
829
+ providerSessionId: typeof claims.sid === "string" ? claims.sid : currentSession.providerSessionId,
830
+ refreshToken: isString(tokenResponse.refresh_token) ? tokenResponse.refresh_token : currentSession.refreshToken,
831
+ idToken,
832
+ accessToken: isString(tokenResponse.access_token) ? tokenResponse.access_token : currentSession.accessToken,
833
+ scope: typeof tokenResponse.scope === "string" ? tokenResponse.scope : currentSession.scope,
834
+ expiresAt: typeof tokenResponse.expires_in === "number" ? now + tokenResponse.expires_in * 1e3 : currentSession.expiresAt,
835
+ updatedAt: now,
836
+ user: mergeUserProfile(subject, claims, userInfo ?? currentSession.user)
837
+ };
838
+ const issuedToken = await withIssuedToken(req, res, options, nextSession);
839
+ let rotatedSession;
840
+ try {
841
+ rotatedSession = await options.storeProvider.rotateSession({
842
+ sessionId: currentSession.sessionId,
843
+ nextSession
844
+ });
845
+ } catch (error) {
846
+ if (error instanceof OidcVaultStoreConflictError) {
847
+ if (usesCookieTransport(options)) {
848
+ clearSessionCookie(res, options);
849
+ }
850
+ throw new OidcVaultHttpError(401, "OIDC_VAULT_INVALID_SESSION", "Session is missing or expired.");
851
+ }
852
+ throw error;
853
+ }
854
+ await callHook("refresh", options.hooks?.onSessionRefreshed, req, res, rotatedSession, {
855
+ previousSessionId: currentSession.sessionId
856
+ });
857
+ if (usesCookieTransport(options)) {
858
+ setSessionCookie(res, options, rotatedSession.sessionId);
859
+ }
860
+ const response = createExchangeResponse(options, rotatedSession, issuedToken);
861
+ res.status(200).json(response);
862
+ });
863
+ var createLogoutHandler = (options, config, trustedOrigins) => createAsyncHandler("logout", options, async (req, res) => {
864
+ assertTrustedOrigin(req, options, trustedOrigins, "logout");
865
+ const body = getBody(req);
866
+ const sessionId = getSessionIdFromRequest(req, options, "logout");
867
+ const redirect = body.redirect === true;
868
+ const session = await options.storeProvider.getSession(sessionId);
869
+ if (!session) {
870
+ if (usesCookieTransport(options)) {
871
+ clearSessionCookie(res, options);
872
+ }
873
+ res.status(200).json({ loggedOut: true });
874
+ return;
875
+ }
876
+ await callHook("logout", options.hooks?.onBeforeLogout, req, res, session, void 0);
877
+ await options.storeProvider.deleteSession(sessionId);
878
+ if (usesCookieTransport(options)) {
879
+ clearSessionCookie(res, options);
880
+ }
881
+ const metadata = await resolveProviderMetadata(config);
882
+ const upstreamLogoutUrl = metadata.endSessionEndpoint ? buildLogoutUrl(metadata.endSessionEndpoint, session.idToken, options.postLogoutRedirectUri) : void 0;
883
+ await callHook("logout", options.hooks?.onLogout, req, res, session, { upstreamLogoutUrl });
884
+ if (redirect && upstreamLogoutUrl) {
885
+ res.redirect(302, upstreamLogoutUrl);
886
+ return;
887
+ }
888
+ res.status(200).json({
889
+ loggedOut: true,
890
+ upstreamLogoutUrl
891
+ });
892
+ });
893
+ var createBackchannelLogoutHandler = (options, config) => createAsyncHandler("backchannel-logout", options, async (req, res) => {
894
+ const metadata = await resolveProviderMetadata(config);
895
+ const logoutToken = getLogoutTokenFromRequest(req);
896
+ const claims = await verifyBackchannelLogoutToken(metadata, logoutToken);
897
+ const revokedSessions = isString(claims.sid) ? await options.storeProvider.deleteSessionsByProviderSessionId({
898
+ providerSessionId: claims.sid,
899
+ issuer: metadata.issuer,
900
+ clientId: metadata.clientId
901
+ }) : await options.storeProvider.deleteSessionsBySubject({
902
+ subject: String(claims.sub),
903
+ issuer: metadata.issuer,
904
+ clientId: metadata.clientId
905
+ });
906
+ await callHook("backchannel-logout", options.hooks?.onLogout, req, res, void 0, {
907
+ providerSessionId: claims.sid,
908
+ subject: claims.sub,
909
+ revokedSessions
910
+ });
911
+ res.status(200).json({
912
+ loggedOut: true,
913
+ revokedSessions
914
+ });
915
+ });
916
+ function registerRoutes(router, options, config, trustedOrigins, basePath) {
917
+ router.get(OIDC_VAULT_ROUTE_PATHS.login, createLoginHandler(options, config, basePath));
918
+ router.get(OIDC_VAULT_ROUTE_PATHS.callback, createCallbackHandler(options, config, basePath));
919
+ router.post(OIDC_VAULT_ROUTE_PATHS.exchange, createExchangeHandler(options));
920
+ router.post(OIDC_VAULT_ROUTE_PATHS.refresh, createRefreshHandler(options, config, trustedOrigins));
921
+ router.post(OIDC_VAULT_ROUTE_PATHS.logout, createLogoutHandler(options, config, trustedOrigins));
922
+ router.post(OIDC_VAULT_ROUTE_PATHS["backchannel-logout"], createBackchannelLogoutHandler(options, config));
923
+ }
924
+ function createOidcVaultJwtAccessTokenValidator(options) {
925
+ return {
926
+ async validate(token) {
927
+ const result = await (0, import_jose.jwtVerify)(token, options.key, {
928
+ issuer: options.issuer,
929
+ audience: options.audience,
930
+ algorithms: options.algorithms
931
+ });
932
+ const claims = result.payload;
933
+ return options.mapClaims ? options.mapClaims(claims) : defaultJwtClaimsMapper(claims);
934
+ }
935
+ };
936
+ }
937
+ function createOidcVaultAccessTokenMiddleware(options) {
938
+ return async (req, res, next) => {
939
+ try {
940
+ const token = extractBearerToken(req.get("authorization"));
941
+ const validationResult = await options.validator.validate(token);
942
+ const auth = {
943
+ token,
944
+ ...validationResult
945
+ };
946
+ const authenticatedRequest = req;
947
+ authenticatedRequest.auth = auth;
948
+ await options.onAuthContext?.({ req: authenticatedRequest, res, auth });
949
+ next();
950
+ } catch (error) {
951
+ if (error instanceof OidcVaultHttpError) {
952
+ setBearerChallengeHeader(res);
953
+ res.status(error.status).json({
954
+ code: error.code,
955
+ message: error.message
956
+ });
957
+ return;
958
+ }
959
+ if (error instanceof Error) {
960
+ setBearerChallengeHeader(res);
961
+ res.status(401).json({
962
+ code: "OIDC_VAULT_INVALID_ACCESS_TOKEN",
963
+ message: error.message || "Access token validation failed."
964
+ });
965
+ return;
966
+ }
967
+ setBearerChallengeHeader(res);
968
+ res.status(401).json({
969
+ code: "OIDC_VAULT_INVALID_ACCESS_TOKEN",
970
+ message: "Access token validation failed."
971
+ });
972
+ }
973
+ };
974
+ }
975
+ function createOidcVaultMiddleware(options) {
976
+ const { config, trustedOrigins } = validateOidcVaultOptions(options);
977
+ const rootRouter = import_express.default.Router();
978
+ const baseRouter = import_express.default.Router();
979
+ const basePath = normalizeOidcVaultBasePath(options.basePath);
980
+ baseRouter.use(import_express.default.json());
981
+ baseRouter.use(import_express.default.urlencoded({ extended: false }));
982
+ registerRoutes(baseRouter, options, config, trustedOrigins, basePath);
983
+ rootRouter.use(basePath, baseRouter);
984
+ return rootRouter;
985
+ }
986
+ // Annotate the CommonJS export names for ESM import in node:
987
+ 0 && (module.exports = {
988
+ DEFAULT_AUTHORIZATION_TRANSACTION_TTL_MS,
989
+ DEFAULT_EXCHANGE_CODE_TTL_MS,
990
+ DEFAULT_OIDC_SCOPES,
991
+ DEFAULT_OIDC_VAULT_BASE_PATH,
992
+ DEFAULT_SESSION_COOKIE_NAME,
993
+ OIDC_VAULT_ROUTE_PATHS,
994
+ OidcVaultStoreConflictError,
995
+ createOidcVaultAccessTokenMiddleware,
996
+ createOidcVaultJwtAccessTokenValidator,
997
+ createOidcVaultMiddleware,
998
+ normalizeOidcVaultBasePath,
999
+ resolveOidcVaultConfig,
1000
+ resolveOidcVaultConfigFromEnv
1001
+ });