@backstage/plugin-auth-backend 0.25.5 → 0.25.6-next.1

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/CHANGELOG.md CHANGED
@@ -1,5 +1,32 @@
1
1
  # @backstage/plugin-auth-backend
2
2
 
3
+ ## 0.25.6-next.1
4
+
5
+ ### Patch Changes
6
+
7
+ - 51ff7d8: Allow configuring dynamic client registration token expiration with config `auth.experimentalDynamicClientRegistration.tokenExpiration`.
8
+
9
+ Maximum expiration for the DCR token is 24 hours. Default expiration is 1 hour.
10
+
11
+ - Updated dependencies
12
+ - @backstage/plugin-catalog-node@1.20.0-next.1
13
+ - @backstage/backend-plugin-api@1.5.0-next.1
14
+ - @backstage/plugin-auth-node@0.6.9-next.1
15
+
16
+ ## 0.25.6-next.0
17
+
18
+ ### Patch Changes
19
+
20
+ - 05f60e1: Refactored constructor parameter properties to explicit property declarations for compatibility with TypeScript's `erasableSyntaxOnly` setting. This internal refactoring maintains all existing functionality while ensuring TypeScript compilation compatibility.
21
+ - Updated dependencies
22
+ - @backstage/plugin-auth-node@0.6.9-next.0
23
+ - @backstage/config@1.3.6-next.0
24
+ - @backstage/catalog-model@1.7.6-next.0
25
+ - @backstage/backend-plugin-api@1.4.5-next.0
26
+ - @backstage/errors@1.2.7
27
+ - @backstage/types@1.2.2
28
+ - @backstage/plugin-catalog-node@1.19.2-next.0
29
+
3
30
  ## 0.25.5
4
31
 
5
32
  ### Patch Changes
package/config.d.ts CHANGED
@@ -95,6 +95,7 @@ export interface Config {
95
95
 
96
96
  /**
97
97
  * The backstage token expiration.
98
+ * Defaults to 1 hour (3600s). Maximum allowed is 24 hours.
98
99
  */
99
100
  backstageTokenExpiration?: HumanDuration | string;
100
101
 
@@ -102,5 +103,28 @@ export interface Config {
102
103
  * Additional app origins to allow for authenticating
103
104
  */
104
105
  experimentalExtraAllowedOrigins?: string[];
106
+
107
+ /**
108
+ * Configuration for dynamic client registration
109
+ */
110
+ experimentalDynamicClientRegistration?: {
111
+ /**
112
+ * Whether to enable dynamic client registration
113
+ * Defaults to false
114
+ */
115
+ enabled?: boolean;
116
+
117
+ /**
118
+ * A list of allowed URI patterns to use for redirect URIs during
119
+ * dynamic client registration. Defaults to '[*]' which allows any redirect URI.
120
+ */
121
+ allowedRedirectUriPatterns?: string[];
122
+
123
+ /**
124
+ * The expiration time for the client registration access tokens.
125
+ * Defaults to 1 hour (3600s). Maximum allowed is 24 hours.
126
+ */
127
+ tokenExpiration?: HumanDuration | string;
128
+ };
105
129
  };
106
130
  }
@@ -5,11 +5,6 @@ var firestore = require('@google-cloud/firestore');
5
5
  const DEFAULT_TIMEOUT_MS = 1e4;
6
6
  const DEFAULT_DOCUMENT_PATH = "sessions";
7
7
  class FirestoreKeyStore {
8
- constructor(database, path, timeout) {
9
- this.database = database;
10
- this.path = path;
11
- this.timeout = timeout;
12
- }
13
8
  static async create(settings) {
14
9
  const { path, timeout, ...firestoreSettings } = settings ?? {};
15
10
  const database = new firestore.Firestore(firestoreSettings);
@@ -19,6 +14,14 @@ class FirestoreKeyStore {
19
14
  timeout ?? DEFAULT_TIMEOUT_MS
20
15
  );
21
16
  }
17
+ database;
18
+ path;
19
+ timeout;
20
+ constructor(database, path, timeout) {
21
+ this.database = database;
22
+ this.path = path;
23
+ this.timeout = timeout;
24
+ }
22
25
  static async verifyConnection(keyStore, logger) {
23
26
  try {
24
27
  await keyStore.verify();
@@ -1 +1 @@
1
- {"version":3,"file":"FirestoreKeyStore.cjs.js","sources":["../../src/identity/FirestoreKeyStore.ts"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { LoggerService } from '@backstage/backend-plugin-api';\nimport {\n DocumentData,\n Firestore,\n QuerySnapshot,\n Settings,\n WriteResult,\n} from '@google-cloud/firestore';\n\nimport { AnyJWK, KeyStore, StoredKey } from './types';\n\nexport type FirestoreKeyStoreSettings = Settings & Options;\n\ntype Options = {\n path?: string;\n timeout?: number;\n};\n\nexport const DEFAULT_TIMEOUT_MS = 10000;\nexport const DEFAULT_DOCUMENT_PATH = 'sessions';\n\nexport class FirestoreKeyStore implements KeyStore {\n static async create(\n settings?: FirestoreKeyStoreSettings,\n ): Promise<FirestoreKeyStore> {\n const { path, timeout, ...firestoreSettings } = settings ?? {};\n const database = new Firestore(firestoreSettings);\n\n return new FirestoreKeyStore(\n database,\n path ?? DEFAULT_DOCUMENT_PATH,\n timeout ?? DEFAULT_TIMEOUT_MS,\n );\n }\n\n private constructor(\n private readonly database: Firestore,\n private readonly path: string,\n private readonly timeout: number,\n ) {}\n\n static async verifyConnection(\n keyStore: FirestoreKeyStore,\n logger?: LoggerService,\n ): Promise<void> {\n try {\n await keyStore.verify();\n } catch (error) {\n if (process.env.NODE_ENV !== 'development') {\n throw new Error(\n `Failed to connect to database: ${(error as Error).message}`,\n );\n }\n logger?.warn(\n `Failed to connect to database: ${(error as Error).message}`,\n );\n }\n }\n\n async addKey(key: AnyJWK): Promise<void> {\n await this.withTimeout<WriteResult>(\n this.database.collection(this.path).doc(key.kid).set({\n kid: key.kid,\n key: key,\n }),\n );\n }\n\n async listKeys(): Promise<{ items: StoredKey[] }> {\n const keys = await this.withTimeout<QuerySnapshot<DocumentData>>(\n this.database.collection(this.path).get(),\n );\n\n return {\n items: keys.docs.map(doc => {\n const { key } = doc.data();\n\n return {\n createdAt: doc.createTime.toDate(),\n key: typeof key === 'string' ? JSON.parse(key) : key,\n };\n }),\n };\n }\n\n async removeKeys(kids: string[]): Promise<void> {\n // This is probably really slow, but it's done async in the background\n for (const kid of kids) {\n await this.withTimeout<WriteResult>(\n this.database.collection(this.path).doc(kid).delete(),\n );\n }\n\n /**\n * This could be achieved with batching but there's a couple of limitations with that:\n *\n * - A batched write can contain a maximum of 500 operations\n * https://firebase.google.com/docs/firestore/manage-data/transactions#batched-writes\n *\n * - The \"in\" operator can combine a maximum of 10 equality clauses\n * https://firebase.google.com/docs/firestore/query-data/queries#in_not-in_and_array-contains-any\n *\n * Example:\n *\n * const batch = this.database.batch();\n * const docs = await this.database\n * .collection(this.path)\n * .where('kid', 'in', kids)\n * .get();\n * docs.forEach(doc => {\n * batch.delete(doc.ref);\n * });\n * await batch.commit();\n *\n */\n }\n\n /**\n * Helper function to allow us to modify the timeout used when\n * performing Firestore database operations.\n *\n * The reason for this is that it seems that there's no other\n * practical solution to change the default timeout of 10mins\n * that Firestore has.\n *\n */\n private async withTimeout<T>(operation: Promise<T>): Promise<T> {\n const timer = new Promise<never>((_, reject) =>\n setTimeout(() => {\n reject(new Error(`Operation timed out after ${this.timeout}ms`));\n }, this.timeout),\n );\n return Promise.race<T>([operation, timer]);\n }\n\n /**\n * Used to verify that the database is reachable.\n */\n private async verify(): Promise<void> {\n await this.withTimeout(this.database.collection(this.path).limit(1).get());\n }\n}\n"],"names":["Firestore"],"mappings":"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"}
1
+ {"version":3,"file":"FirestoreKeyStore.cjs.js","sources":["../../src/identity/FirestoreKeyStore.ts"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { LoggerService } from '@backstage/backend-plugin-api';\nimport {\n DocumentData,\n Firestore,\n QuerySnapshot,\n Settings,\n WriteResult,\n} from '@google-cloud/firestore';\n\nimport { AnyJWK, KeyStore, StoredKey } from './types';\n\nexport type FirestoreKeyStoreSettings = Settings & Options;\n\ntype Options = {\n path?: string;\n timeout?: number;\n};\n\nexport const DEFAULT_TIMEOUT_MS = 10000;\nexport const DEFAULT_DOCUMENT_PATH = 'sessions';\n\nexport class FirestoreKeyStore implements KeyStore {\n static async create(\n settings?: FirestoreKeyStoreSettings,\n ): Promise<FirestoreKeyStore> {\n const { path, timeout, ...firestoreSettings } = settings ?? {};\n const database = new Firestore(firestoreSettings);\n\n return new FirestoreKeyStore(\n database,\n path ?? DEFAULT_DOCUMENT_PATH,\n timeout ?? DEFAULT_TIMEOUT_MS,\n );\n }\n\n private readonly database: Firestore;\n private readonly path: string;\n private readonly timeout: number;\n\n private constructor(database: Firestore, path: string, timeout: number) {\n this.database = database;\n this.path = path;\n this.timeout = timeout;\n }\n\n static async verifyConnection(\n keyStore: FirestoreKeyStore,\n logger?: LoggerService,\n ): Promise<void> {\n try {\n await keyStore.verify();\n } catch (error) {\n if (process.env.NODE_ENV !== 'development') {\n throw new Error(\n `Failed to connect to database: ${(error as Error).message}`,\n );\n }\n logger?.warn(\n `Failed to connect to database: ${(error as Error).message}`,\n );\n }\n }\n\n async addKey(key: AnyJWK): Promise<void> {\n await this.withTimeout<WriteResult>(\n this.database.collection(this.path).doc(key.kid).set({\n kid: key.kid,\n key: key,\n }),\n );\n }\n\n async listKeys(): Promise<{ items: StoredKey[] }> {\n const keys = await this.withTimeout<QuerySnapshot<DocumentData>>(\n this.database.collection(this.path).get(),\n );\n\n return {\n items: keys.docs.map(doc => {\n const { key } = doc.data();\n\n return {\n createdAt: doc.createTime.toDate(),\n key: typeof key === 'string' ? JSON.parse(key) : key,\n };\n }),\n };\n }\n\n async removeKeys(kids: string[]): Promise<void> {\n // This is probably really slow, but it's done async in the background\n for (const kid of kids) {\n await this.withTimeout<WriteResult>(\n this.database.collection(this.path).doc(kid).delete(),\n );\n }\n\n /**\n * This could be achieved with batching but there's a couple of limitations with that:\n *\n * - A batched write can contain a maximum of 500 operations\n * https://firebase.google.com/docs/firestore/manage-data/transactions#batched-writes\n *\n * - The \"in\" operator can combine a maximum of 10 equality clauses\n * https://firebase.google.com/docs/firestore/query-data/queries#in_not-in_and_array-contains-any\n *\n * Example:\n *\n * const batch = this.database.batch();\n * const docs = await this.database\n * .collection(this.path)\n * .where('kid', 'in', kids)\n * .get();\n * docs.forEach(doc => {\n * batch.delete(doc.ref);\n * });\n * await batch.commit();\n *\n */\n }\n\n /**\n * Helper function to allow us to modify the timeout used when\n * performing Firestore database operations.\n *\n * The reason for this is that it seems that there's no other\n * practical solution to change the default timeout of 10mins\n * that Firestore has.\n *\n */\n private async withTimeout<T>(operation: Promise<T>): Promise<T> {\n const timer = new Promise<never>((_, reject) =>\n setTimeout(() => {\n reject(new Error(`Operation timed out after ${this.timeout}ms`));\n }, this.timeout),\n );\n return Promise.race<T>([operation, timer]);\n }\n\n /**\n * Used to verify that the database is reachable.\n */\n private async verify(): Promise<void> {\n await this.withTimeout(this.database.collection(this.path).limit(1).get());\n }\n}\n"],"names":["Firestore"],"mappings":"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"}
@@ -11,15 +11,6 @@ function getDefaultOwnershipEntityRefs(entity) {
11
11
  return Array.from(/* @__PURE__ */ new Set([catalogModel.stringifyEntityRef(entity), ...membershipRefs]));
12
12
  }
13
13
  class CatalogAuthResolverContext {
14
- constructor(logger, tokenIssuer, catalogIdentityClient, catalog, auth, userInfo, ownershipResolver) {
15
- this.logger = logger;
16
- this.tokenIssuer = tokenIssuer;
17
- this.catalogIdentityClient = catalogIdentityClient;
18
- this.catalog = catalog;
19
- this.auth = auth;
20
- this.userInfo = userInfo;
21
- this.ownershipResolver = ownershipResolver;
22
- }
23
14
  static create(options) {
24
15
  const catalogIdentityClient = new CatalogIdentityClient.CatalogIdentityClient({
25
16
  catalog: options.catalog,
@@ -35,6 +26,22 @@ class CatalogAuthResolverContext {
35
26
  options.ownershipResolver
36
27
  );
37
28
  }
29
+ logger;
30
+ tokenIssuer;
31
+ catalogIdentityClient;
32
+ catalog;
33
+ auth;
34
+ userInfo;
35
+ ownershipResolver;
36
+ constructor(logger, tokenIssuer, catalogIdentityClient, catalog, auth, userInfo, ownershipResolver) {
37
+ this.logger = logger;
38
+ this.tokenIssuer = tokenIssuer;
39
+ this.catalogIdentityClient = catalogIdentityClient;
40
+ this.catalog = catalog;
41
+ this.auth = auth;
42
+ this.userInfo = userInfo;
43
+ this.ownershipResolver = ownershipResolver;
44
+ }
38
45
  async issueToken(params) {
39
46
  const { sub, ent = [sub], ...additionalClaims } = params.claims;
40
47
  const claims = {
@@ -1 +1 @@
1
- {"version":3,"file":"CatalogAuthResolverContext.cjs.js","sources":["../../../src/lib/resolvers/CatalogAuthResolverContext.ts"],"sourcesContent":["/*\n * Copyright 2022 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n DEFAULT_NAMESPACE,\n Entity,\n parseEntityRef,\n RELATION_MEMBER_OF,\n stringifyEntityRef,\n} from '@backstage/catalog-model';\nimport { ConflictError, InputError, NotFoundError } from '@backstage/errors';\nimport { AuthService, LoggerService } from '@backstage/backend-plugin-api';\nimport { CatalogService } from '@backstage/plugin-catalog-node';\nimport { TokenIssuer } from '../../identity/types';\nimport {\n AuthOwnershipResolver,\n AuthResolverCatalogUserQuery,\n AuthResolverContext,\n TokenParams,\n} from '@backstage/plugin-auth-node';\nimport { CatalogIdentityClient } from '../catalog/CatalogIdentityClient';\nimport { UserInfoDatabase } from '../../database/UserInfoDatabase';\n\nfunction getDefaultOwnershipEntityRefs(entity: Entity) {\n const membershipRefs =\n entity.relations\n ?.filter(\n r => r.type === RELATION_MEMBER_OF && r.targetRef.startsWith('group:'),\n )\n .map(r => r.targetRef) ?? [];\n\n return Array.from(new Set([stringifyEntityRef(entity), ...membershipRefs]));\n}\n\nexport class CatalogAuthResolverContext implements AuthResolverContext {\n static create(options: {\n logger: LoggerService;\n catalog: CatalogService;\n tokenIssuer: TokenIssuer;\n auth: AuthService;\n ownershipResolver?: AuthOwnershipResolver;\n userInfo: UserInfoDatabase;\n }): CatalogAuthResolverContext {\n const catalogIdentityClient = new CatalogIdentityClient({\n catalog: options.catalog,\n auth: options.auth,\n });\n\n return new CatalogAuthResolverContext(\n options.logger,\n options.tokenIssuer,\n catalogIdentityClient,\n options.catalog,\n options.auth,\n options.userInfo,\n options.ownershipResolver,\n );\n }\n\n private constructor(\n public readonly logger: LoggerService,\n public readonly tokenIssuer: TokenIssuer,\n public readonly catalogIdentityClient: CatalogIdentityClient,\n private readonly catalog: CatalogService,\n private readonly auth: AuthService,\n private readonly userInfo: UserInfoDatabase,\n private readonly ownershipResolver?: AuthOwnershipResolver,\n ) {}\n\n async issueToken(params: TokenParams) {\n const { sub, ent = [sub], ...additionalClaims } = params.claims;\n const claims = {\n sub,\n ent,\n ...additionalClaims,\n };\n\n const issuedToken = await this.tokenIssuer.issueToken({\n claims,\n });\n\n // Store the user info in the database upon successful token\n // issuance so that it can be retrieved later by limited user tokens\n await this.userInfo.addUserInfo({\n claims,\n });\n\n return issuedToken;\n }\n\n async findCatalogUser(query: AuthResolverCatalogUserQuery) {\n let result: Entity[] | Entity | undefined = undefined;\n\n if ('entityRef' in query) {\n const entityRef = parseEntityRef(query.entityRef, {\n defaultKind: 'User',\n defaultNamespace: DEFAULT_NAMESPACE,\n });\n result = await this.catalog.getEntityByRef(entityRef, {\n credentials: await this.auth.getOwnServiceCredentials(),\n });\n } else if ('annotations' in query) {\n const filter: Record<string, string> = {\n kind: 'user',\n };\n for (const [key, value] of Object.entries(query.annotations)) {\n filter[`metadata.annotations.${key}`] = value;\n }\n const res = await this.catalog.getEntities(\n { filter },\n { credentials: await this.auth.getOwnServiceCredentials() },\n );\n result = res.items;\n } else if ('filter' in query) {\n const filter = [query.filter].flat().map(value => {\n if (\n !Object.keys(value).some(\n key => key.toLocaleLowerCase('en-US') === 'kind',\n )\n ) {\n return {\n ...value,\n kind: 'user',\n };\n }\n return value;\n });\n const res = await this.catalog.getEntities(\n { filter: filter },\n { credentials: await this.auth.getOwnServiceCredentials() },\n );\n result = res.items;\n } else {\n throw new InputError('Invalid user lookup query');\n }\n\n if (Array.isArray(result)) {\n if (result.length > 1) {\n throw new ConflictError('User lookup resulted in multiple matches');\n }\n result = result[0];\n }\n if (!result) {\n throw new NotFoundError('User not found');\n }\n\n return { entity: result };\n }\n\n async signInWithCatalogUser(\n query: AuthResolverCatalogUserQuery,\n options?: {\n dangerousEntityRefFallback?: {\n entityRef:\n | string\n | {\n kind?: string;\n namespace?: string;\n name: string;\n };\n };\n },\n ) {\n try {\n const { entity } = await this.findCatalogUser(query);\n\n const { ownershipEntityRefs } = await this.resolveOwnershipEntityRefs(\n entity,\n );\n\n return await this.issueToken({\n claims: {\n sub: stringifyEntityRef(entity),\n ent: ownershipEntityRefs,\n },\n });\n } catch (error) {\n if (\n error?.name !== 'NotFoundError' ||\n !options?.dangerousEntityRefFallback\n ) {\n throw error;\n }\n const userEntityRef = stringifyEntityRef(\n parseEntityRef(options.dangerousEntityRefFallback.entityRef, {\n defaultKind: 'User',\n defaultNamespace: DEFAULT_NAMESPACE,\n }),\n );\n\n return await this.issueToken({\n claims: {\n sub: userEntityRef,\n ent: [userEntityRef],\n },\n });\n }\n }\n\n async resolveOwnershipEntityRefs(\n entity: Entity,\n ): Promise<{ ownershipEntityRefs: string[] }> {\n if (this.ownershipResolver) {\n return this.ownershipResolver.resolveOwnershipEntityRefs(entity);\n }\n return { ownershipEntityRefs: getDefaultOwnershipEntityRefs(entity) };\n }\n}\n"],"names":["RELATION_MEMBER_OF","stringifyEntityRef","CatalogIdentityClient","parseEntityRef","DEFAULT_NAMESPACE","InputError","ConflictError","NotFoundError"],"mappings":";;;;;;AAoCA,SAAS,8BAA8B,MAAA,EAAgB;AACrD,EAAA,MAAM,cAAA,GACJ,OAAO,SAAA,EACH,MAAA;AAAA,IACA,OAAK,CAAA,CAAE,IAAA,KAASA,mCAAsB,CAAA,CAAE,SAAA,CAAU,WAAW,QAAQ;AAAA,IAEtE,GAAA,CAAI,CAAA,CAAA,KAAK,CAAA,CAAE,SAAS,KAAK,EAAC;AAE/B,EAAA,OAAO,KAAA,CAAM,IAAA,iBAAK,IAAI,GAAA,CAAI,CAACC,+BAAA,CAAmB,MAAM,CAAA,EAAG,GAAG,cAAc,CAAC,CAAC,CAAA;AAC5E;AAEO,MAAM,0BAAA,CAA0D;AAAA,EAyB7D,YACU,MAAA,EACA,WAAA,EACA,uBACC,OAAA,EACA,IAAA,EACA,UACA,iBAAA,EACjB;AAPgB,IAAA,IAAA,CAAA,MAAA,GAAA,MAAA;AACA,IAAA,IAAA,CAAA,WAAA,GAAA,WAAA;AACA,IAAA,IAAA,CAAA,qBAAA,GAAA,qBAAA;AACC,IAAA,IAAA,CAAA,OAAA,GAAA,OAAA;AACA,IAAA,IAAA,CAAA,IAAA,GAAA,IAAA;AACA,IAAA,IAAA,CAAA,QAAA,GAAA,QAAA;AACA,IAAA,IAAA,CAAA,iBAAA,GAAA,iBAAA;AAAA,EAChB;AAAA,EAhCH,OAAO,OAAO,OAAA,EAOiB;AAC7B,IAAA,MAAM,qBAAA,GAAwB,IAAIC,2CAAA,CAAsB;AAAA,MACtD,SAAS,OAAA,CAAQ,OAAA;AAAA,MACjB,MAAM,OAAA,CAAQ;AAAA,KACf,CAAA;AAED,IAAA,OAAO,IAAI,0BAAA;AAAA,MACT,OAAA,CAAQ,MAAA;AAAA,MACR,OAAA,CAAQ,WAAA;AAAA,MACR,qBAAA;AAAA,MACA,OAAA,CAAQ,OAAA;AAAA,MACR,OAAA,CAAQ,IAAA;AAAA,MACR,OAAA,CAAQ,QAAA;AAAA,MACR,OAAA,CAAQ;AAAA,KACV;AAAA,EACF;AAAA,EAYA,MAAM,WAAW,MAAA,EAAqB;AACpC,IAAA,MAAM,EAAE,KAAK,GAAA,GAAM,CAAC,GAAG,CAAA,EAAG,GAAG,gBAAA,EAAiB,GAAI,MAAA,CAAO,MAAA;AACzD,IAAA,MAAM,MAAA,GAAS;AAAA,MACb,GAAA;AAAA,MACA,GAAA;AAAA,MACA,GAAG;AAAA,KACL;AAEA,IAAA,MAAM,WAAA,GAAc,MAAM,IAAA,CAAK,WAAA,CAAY,UAAA,CAAW;AAAA,MACpD;AAAA,KACD,CAAA;AAID,IAAA,MAAM,IAAA,CAAK,SAAS,WAAA,CAAY;AAAA,MAC9B;AAAA,KACD,CAAA;AAED,IAAA,OAAO,WAAA;AAAA,EACT;AAAA,EAEA,MAAM,gBAAgB,KAAA,EAAqC;AACzD,IAAA,IAAI,MAAA,GAAwC,MAAA;AAE5C,IAAA,IAAI,eAAe,KAAA,EAAO;AACxB,MAAA,MAAM,SAAA,GAAYC,2BAAA,CAAe,KAAA,CAAM,SAAA,EAAW;AAAA,QAChD,WAAA,EAAa,MAAA;AAAA,QACb,gBAAA,EAAkBC;AAAA,OACnB,CAAA;AACD,MAAA,MAAA,GAAS,MAAM,IAAA,CAAK,OAAA,CAAQ,cAAA,CAAe,SAAA,EAAW;AAAA,QACpD,WAAA,EAAa,MAAM,IAAA,CAAK,IAAA,CAAK,wBAAA;AAAyB,OACvD,CAAA;AAAA,IACH,CAAA,MAAA,IAAW,iBAAiB,KAAA,EAAO;AACjC,MAAA,MAAM,MAAA,GAAiC;AAAA,QACrC,IAAA,EAAM;AAAA,OACR;AACA,MAAA,KAAA,MAAW,CAAC,KAAK,KAAK,CAAA,IAAK,OAAO,OAAA,CAAQ,KAAA,CAAM,WAAW,CAAA,EAAG;AAC5D,QAAA,MAAA,CAAO,CAAA,qBAAA,EAAwB,GAAG,CAAA,CAAE,CAAA,GAAI,KAAA;AAAA,MAC1C;AACA,MAAA,MAAM,GAAA,GAAM,MAAM,IAAA,CAAK,OAAA,CAAQ,WAAA;AAAA,QAC7B,EAAE,MAAA,EAAO;AAAA,QACT,EAAE,WAAA,EAAa,MAAM,IAAA,CAAK,IAAA,CAAK,0BAAyB;AAAE,OAC5D;AACA,MAAA,MAAA,GAAS,GAAA,CAAI,KAAA;AAAA,IACf,CAAA,MAAA,IAAW,YAAY,KAAA,EAAO;AAC5B,MAAA,MAAM,MAAA,GAAS,CAAC,KAAA,CAAM,MAAM,EAAE,IAAA,EAAK,CAAE,IAAI,CAAA,KAAA,KAAS;AAChD,QAAA,IACE,CAAC,MAAA,CAAO,IAAA,CAAK,KAAK,CAAA,CAAE,IAAA;AAAA,UAClB,CAAA,GAAA,KAAO,GAAA,CAAI,iBAAA,CAAkB,OAAO,CAAA,KAAM;AAAA,SAC5C,EACA;AACA,UAAA,OAAO;AAAA,YACL,GAAG,KAAA;AAAA,YACH,IAAA,EAAM;AAAA,WACR;AAAA,QACF;AACA,QAAA,OAAO,KAAA;AAAA,MACT,CAAC,CAAA;AACD,MAAA,MAAM,GAAA,GAAM,MAAM,IAAA,CAAK,OAAA,CAAQ,WAAA;AAAA,QAC7B,EAAE,MAAA,EAAe;AAAA,QACjB,EAAE,WAAA,EAAa,MAAM,IAAA,CAAK,IAAA,CAAK,0BAAyB;AAAE,OAC5D;AACA,MAAA,MAAA,GAAS,GAAA,CAAI,KAAA;AAAA,IACf,CAAA,MAAO;AACL,MAAA,MAAM,IAAIC,kBAAW,2BAA2B,CAAA;AAAA,IAClD;AAEA,IAAA,IAAI,KAAA,CAAM,OAAA,CAAQ,MAAM,CAAA,EAAG;AACzB,MAAA,IAAI,MAAA,CAAO,SAAS,CAAA,EAAG;AACrB,QAAA,MAAM,IAAIC,qBAAc,0CAA0C,CAAA;AAAA,MACpE;AACA,MAAA,MAAA,GAAS,OAAO,CAAC,CAAA;AAAA,IACnB;AACA,IAAA,IAAI,CAAC,MAAA,EAAQ;AACX,MAAA,MAAM,IAAIC,qBAAc,gBAAgB,CAAA;AAAA,IAC1C;AAEA,IAAA,OAAO,EAAE,QAAQ,MAAA,EAAO;AAAA,EAC1B;AAAA,EAEA,MAAM,qBAAA,CACJ,KAAA,EACA,OAAA,EAWA;AACA,IAAA,IAAI;AACF,MAAA,MAAM,EAAE,MAAA,EAAO,GAAI,MAAM,IAAA,CAAK,gBAAgB,KAAK,CAAA;AAEnD,MAAA,MAAM,EAAE,mBAAA,EAAoB,GAAI,MAAM,IAAA,CAAK,0BAAA;AAAA,QACzC;AAAA,OACF;AAEA,MAAA,OAAO,MAAM,KAAK,UAAA,CAAW;AAAA,QAC3B,MAAA,EAAQ;AAAA,UACN,GAAA,EAAKN,gCAAmB,MAAM,CAAA;AAAA,UAC9B,GAAA,EAAK;AAAA;AACP,OACD,CAAA;AAAA,IACH,SAAS,KAAA,EAAO;AACd,MAAA,IACE,KAAA,EAAO,IAAA,KAAS,eAAA,IAChB,CAAC,SAAS,0BAAA,EACV;AACA,QAAA,MAAM,KAAA;AAAA,MACR;AACA,MAAA,MAAM,aAAA,GAAgBA,+BAAA;AAAA,QACpBE,2BAAA,CAAe,OAAA,CAAQ,0BAAA,CAA2B,SAAA,EAAW;AAAA,UAC3D,WAAA,EAAa,MAAA;AAAA,UACb,gBAAA,EAAkBC;AAAA,SACnB;AAAA,OACH;AAEA,MAAA,OAAO,MAAM,KAAK,UAAA,CAAW;AAAA,QAC3B,MAAA,EAAQ;AAAA,UACN,GAAA,EAAK,aAAA;AAAA,UACL,GAAA,EAAK,CAAC,aAAa;AAAA;AACrB,OACD,CAAA;AAAA,IACH;AAAA,EACF;AAAA,EAEA,MAAM,2BACJ,MAAA,EAC4C;AAC5C,IAAA,IAAI,KAAK,iBAAA,EAAmB;AAC1B,MAAA,OAAO,IAAA,CAAK,iBAAA,CAAkB,0BAAA,CAA2B,MAAM,CAAA;AAAA,IACjE;AACA,IAAA,OAAO,EAAE,mBAAA,EAAqB,6BAAA,CAA8B,MAAM,CAAA,EAAE;AAAA,EACtE;AACF;;;;"}
1
+ {"version":3,"file":"CatalogAuthResolverContext.cjs.js","sources":["../../../src/lib/resolvers/CatalogAuthResolverContext.ts"],"sourcesContent":["/*\n * Copyright 2022 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n DEFAULT_NAMESPACE,\n Entity,\n parseEntityRef,\n RELATION_MEMBER_OF,\n stringifyEntityRef,\n} from '@backstage/catalog-model';\nimport { ConflictError, InputError, NotFoundError } from '@backstage/errors';\nimport { AuthService, LoggerService } from '@backstage/backend-plugin-api';\nimport { CatalogService } from '@backstage/plugin-catalog-node';\nimport { TokenIssuer } from '../../identity/types';\nimport {\n AuthOwnershipResolver,\n AuthResolverCatalogUserQuery,\n AuthResolverContext,\n TokenParams,\n} from '@backstage/plugin-auth-node';\nimport { CatalogIdentityClient } from '../catalog/CatalogIdentityClient';\nimport { UserInfoDatabase } from '../../database/UserInfoDatabase';\n\nfunction getDefaultOwnershipEntityRefs(entity: Entity) {\n const membershipRefs =\n entity.relations\n ?.filter(\n r => r.type === RELATION_MEMBER_OF && r.targetRef.startsWith('group:'),\n )\n .map(r => r.targetRef) ?? [];\n\n return Array.from(new Set([stringifyEntityRef(entity), ...membershipRefs]));\n}\n\nexport class CatalogAuthResolverContext implements AuthResolverContext {\n static create(options: {\n logger: LoggerService;\n catalog: CatalogService;\n tokenIssuer: TokenIssuer;\n auth: AuthService;\n ownershipResolver?: AuthOwnershipResolver;\n userInfo: UserInfoDatabase;\n }): CatalogAuthResolverContext {\n const catalogIdentityClient = new CatalogIdentityClient({\n catalog: options.catalog,\n auth: options.auth,\n });\n\n return new CatalogAuthResolverContext(\n options.logger,\n options.tokenIssuer,\n catalogIdentityClient,\n options.catalog,\n options.auth,\n options.userInfo,\n options.ownershipResolver,\n );\n }\n\n public readonly logger: LoggerService;\n public readonly tokenIssuer: TokenIssuer;\n public readonly catalogIdentityClient: CatalogIdentityClient;\n private readonly catalog: CatalogService;\n private readonly auth: AuthService;\n private readonly userInfo: UserInfoDatabase;\n private readonly ownershipResolver?: AuthOwnershipResolver;\n\n private constructor(\n logger: LoggerService,\n tokenIssuer: TokenIssuer,\n catalogIdentityClient: CatalogIdentityClient,\n catalog: CatalogService,\n auth: AuthService,\n userInfo: UserInfoDatabase,\n ownershipResolver?: AuthOwnershipResolver,\n ) {\n this.logger = logger;\n this.tokenIssuer = tokenIssuer;\n this.catalogIdentityClient = catalogIdentityClient;\n this.catalog = catalog;\n this.auth = auth;\n this.userInfo = userInfo;\n this.ownershipResolver = ownershipResolver;\n }\n\n async issueToken(params: TokenParams) {\n const { sub, ent = [sub], ...additionalClaims } = params.claims;\n const claims = {\n sub,\n ent,\n ...additionalClaims,\n };\n\n const issuedToken = await this.tokenIssuer.issueToken({\n claims,\n });\n\n // Store the user info in the database upon successful token\n // issuance so that it can be retrieved later by limited user tokens\n await this.userInfo.addUserInfo({\n claims,\n });\n\n return issuedToken;\n }\n\n async findCatalogUser(query: AuthResolverCatalogUserQuery) {\n let result: Entity[] | Entity | undefined = undefined;\n\n if ('entityRef' in query) {\n const entityRef = parseEntityRef(query.entityRef, {\n defaultKind: 'User',\n defaultNamespace: DEFAULT_NAMESPACE,\n });\n result = await this.catalog.getEntityByRef(entityRef, {\n credentials: await this.auth.getOwnServiceCredentials(),\n });\n } else if ('annotations' in query) {\n const filter: Record<string, string> = {\n kind: 'user',\n };\n for (const [key, value] of Object.entries(query.annotations)) {\n filter[`metadata.annotations.${key}`] = value;\n }\n const res = await this.catalog.getEntities(\n { filter },\n { credentials: await this.auth.getOwnServiceCredentials() },\n );\n result = res.items;\n } else if ('filter' in query) {\n const filter = [query.filter].flat().map(value => {\n if (\n !Object.keys(value).some(\n key => key.toLocaleLowerCase('en-US') === 'kind',\n )\n ) {\n return {\n ...value,\n kind: 'user',\n };\n }\n return value;\n });\n const res = await this.catalog.getEntities(\n { filter: filter },\n { credentials: await this.auth.getOwnServiceCredentials() },\n );\n result = res.items;\n } else {\n throw new InputError('Invalid user lookup query');\n }\n\n if (Array.isArray(result)) {\n if (result.length > 1) {\n throw new ConflictError('User lookup resulted in multiple matches');\n }\n result = result[0];\n }\n if (!result) {\n throw new NotFoundError('User not found');\n }\n\n return { entity: result };\n }\n\n async signInWithCatalogUser(\n query: AuthResolverCatalogUserQuery,\n options?: {\n dangerousEntityRefFallback?: {\n entityRef:\n | string\n | {\n kind?: string;\n namespace?: string;\n name: string;\n };\n };\n },\n ) {\n try {\n const { entity } = await this.findCatalogUser(query);\n\n const { ownershipEntityRefs } = await this.resolveOwnershipEntityRefs(\n entity,\n );\n\n return await this.issueToken({\n claims: {\n sub: stringifyEntityRef(entity),\n ent: ownershipEntityRefs,\n },\n });\n } catch (error) {\n if (\n error?.name !== 'NotFoundError' ||\n !options?.dangerousEntityRefFallback\n ) {\n throw error;\n }\n const userEntityRef = stringifyEntityRef(\n parseEntityRef(options.dangerousEntityRefFallback.entityRef, {\n defaultKind: 'User',\n defaultNamespace: DEFAULT_NAMESPACE,\n }),\n );\n\n return await this.issueToken({\n claims: {\n sub: userEntityRef,\n ent: [userEntityRef],\n },\n });\n }\n }\n\n async resolveOwnershipEntityRefs(\n entity: Entity,\n ): Promise<{ ownershipEntityRefs: string[] }> {\n if (this.ownershipResolver) {\n return this.ownershipResolver.resolveOwnershipEntityRefs(entity);\n }\n return { ownershipEntityRefs: getDefaultOwnershipEntityRefs(entity) };\n }\n}\n"],"names":["RELATION_MEMBER_OF","stringifyEntityRef","CatalogIdentityClient","parseEntityRef","DEFAULT_NAMESPACE","InputError","ConflictError","NotFoundError"],"mappings":"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"}
@@ -4,12 +4,19 @@ var Router = require('express-promise-router');
4
4
  var OidcService = require('./OidcService.cjs.js');
5
5
  var errors = require('@backstage/errors');
6
6
  var express = require('express');
7
+ var readTokenExpiration = require('./readTokenExpiration.cjs.js');
7
8
 
8
9
  function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; }
9
10
 
10
11
  var Router__default = /*#__PURE__*/_interopDefaultCompat(Router);
11
12
 
12
13
  class OidcRouter {
14
+ oidc;
15
+ logger;
16
+ auth;
17
+ appUrl;
18
+ httpAuth;
19
+ config;
13
20
  constructor(oidc, logger, auth, appUrl, httpAuth, config) {
14
21
  this.oidc = oidc;
15
22
  this.logger = logger;
@@ -235,12 +242,14 @@ class OidcRouter {
235
242
  error_description: "Missing required parameters"
236
243
  });
237
244
  }
245
+ const expiresIn = readTokenExpiration.readDcrTokenExpiration(this.config);
238
246
  try {
239
247
  const result = await this.oidc.exchangeCodeForToken({
240
248
  code,
241
249
  redirectUri,
242
250
  codeVerifier,
243
- grantType
251
+ grantType,
252
+ expiresIn
244
253
  });
245
254
  return res.json({
246
255
  access_token: result.accessToken,
@@ -1 +1 @@
1
- {"version":3,"file":"OidcRouter.cjs.js","sources":["../../src/service/OidcRouter.ts"],"sourcesContent":["/*\n * Copyright 2025 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport Router from 'express-promise-router';\nimport { OidcService } from './OidcService';\nimport { AuthenticationError, isError } from '@backstage/errors';\nimport {\n AuthService,\n HttpAuthService,\n LoggerService,\n RootConfigService,\n} from '@backstage/backend-plugin-api';\nimport { TokenIssuer } from '../identity/types';\nimport { UserInfoDatabase } from '../database/UserInfoDatabase';\nimport { OidcDatabase } from '../database/OidcDatabase';\nimport { json } from 'express';\n\nexport class OidcRouter {\n private constructor(\n private readonly oidc: OidcService,\n private readonly logger: LoggerService,\n private readonly auth: AuthService,\n private readonly appUrl: string,\n private readonly httpAuth: HttpAuthService,\n private readonly config: RootConfigService,\n ) {}\n\n static create(options: {\n auth: AuthService;\n tokenIssuer: TokenIssuer;\n baseUrl: string;\n appUrl: string;\n logger: LoggerService;\n userInfo: UserInfoDatabase;\n oidc: OidcDatabase;\n httpAuth: HttpAuthService;\n config: RootConfigService;\n }) {\n return new OidcRouter(\n OidcService.create(options),\n options.logger,\n options.auth,\n options.appUrl,\n options.httpAuth,\n options.config,\n );\n }\n\n public getRouter() {\n const router = Router();\n\n router.use(json());\n\n // OpenID Provider Configuration endpoint\n // https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfig\n // Returns the OpenID Provider Configuration document containing metadata about the provider\n router.get('/.well-known/openid-configuration', (_req, res) => {\n res.json(this.oidc.getConfiguration());\n });\n\n // JSON Web Key Set endpoint\n // https://openid.net/specs/openid-connect-core-1_0.html#rfc.section.10.1.1\n // Returns the public keys used to verify JWTs issued by this provider\n router.get('/.well-known/jwks.json', async (_req, res) => {\n const { keys } = await this.oidc.listPublicKeys();\n res.json({ keys });\n });\n\n // UserInfo endpoint\n // https://openid.net/specs/openid-connect-core-1_0.html#UserInfo\n // Returns claims about the authenticated user using an access token\n router.get('/v1/userinfo', async (req, res) => {\n const matches = req.headers.authorization?.match(/^Bearer[ ]+(\\S+)$/i);\n const token = matches?.[1];\n if (!token) {\n throw new AuthenticationError('No token provided');\n }\n\n const userInfo = await this.oidc.getUserInfo({ token });\n\n if (!userInfo) {\n res.status(404).send('User info not found');\n return;\n }\n\n res.json(userInfo);\n });\n\n if (\n this.config.getOptionalBoolean(\n 'auth.experimentalDynamicClientRegistration.enabled',\n )\n ) {\n // Authorization endpoint\n // https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest\n // Handles the initial authorization request from the client, validates parameters,\n // and redirects to the Authorization Session page for user approval\n router.get('/v1/authorize', async (req, res) => {\n // todo(blam): maybe add zod types for validating input\n const {\n client_id: clientId,\n redirect_uri: redirectUri,\n response_type: responseType,\n scope,\n state,\n nonce,\n code_challenge: codeChallenge,\n code_challenge_method: codeChallengeMethod,\n } = req.query;\n\n if (!clientId || !redirectUri || !responseType) {\n this.logger.error(`Failed to authorize: Missing required parameters`);\n return res.status(400).json({\n error: 'invalid_request',\n error_description:\n 'Missing required parameters: client_id, redirect_uri, response_type',\n });\n }\n\n try {\n const result = await this.oidc.createAuthorizationSession({\n clientId: clientId as string,\n redirectUri: redirectUri as string,\n responseType: responseType as string,\n scope: scope as string | undefined,\n state: state as string | undefined,\n nonce: nonce as string | undefined,\n codeChallenge: codeChallenge as string | undefined,\n codeChallengeMethod: codeChallengeMethod as string | undefined,\n });\n\n // todo(blam): maybe this URL could be overridable by config if\n // the plugin is mounted somewhere else?\n // support slashes in baseUrl?\n const authSessionRedirectUrl = new URL(\n `./oauth2/authorize/${result.id}`,\n ensureTrailingSlash(this.appUrl),\n );\n\n return res.redirect(authSessionRedirectUrl.toString());\n } catch (error) {\n const errorParams = new URLSearchParams();\n errorParams.append(\n 'error',\n isError(error) ? error.name : 'server_error',\n );\n errorParams.append(\n 'error_description',\n isError(error) ? error.message : 'Unknown error',\n );\n if (state) {\n errorParams.append('state', state as string);\n }\n\n const redirectUrl = new URL(redirectUri as string);\n redirectUrl.search = errorParams.toString();\n return res.redirect(redirectUrl.toString());\n }\n });\n\n // Authorization Session request details endpoint\n // Returns Authorization Session request details for the frontend\n router.get('/v1/sessions/:sessionId', async (req, res) => {\n const { sessionId } = req.params;\n\n if (!sessionId) {\n return res.status(400).json({\n error: 'invalid_request',\n error_description: 'Missing Authorization Session ID',\n });\n }\n\n try {\n const session = await this.oidc.getAuthorizationSession({\n sessionId,\n });\n\n return res.json({\n id: session.id,\n clientName: session.clientName,\n scope: session.scope,\n redirectUri: session.redirectUri,\n });\n } catch (error) {\n const description = isError(error) ? error.message : 'Unknown error';\n this.logger.error(\n `Failed to get authorization session: ${description}`,\n error,\n );\n return res.status(404).json({\n error: 'not_found',\n error_description: description,\n });\n }\n });\n\n // Authorization Session approval endpoint\n // Handles user approval of Authorization Session requests and generates authorization codes\n router.post('/v1/sessions/:sessionId/approve', async (req, res) => {\n const { sessionId } = req.params;\n\n if (!sessionId) {\n return res.status(400).json({\n error: 'invalid_request',\n error_description: 'Missing authorization session ID',\n });\n }\n\n try {\n const httpCredentials = await this.httpAuth.credentials(req);\n\n if (!this.auth.isPrincipal(httpCredentials, 'user')) {\n return res.status(401).json({\n error: 'unauthorized',\n error_description: 'Authentication required',\n });\n }\n\n const { userEntityRef } = httpCredentials.principal;\n\n const result = await this.oidc.approveAuthorizationSession({\n sessionId,\n userEntityRef,\n });\n\n return res.json({\n redirectUrl: result.redirectUrl,\n });\n } catch (error) {\n const description = isError(error) ? error.message : 'Unknown error';\n this.logger.error(\n `Failed to approve authorization session: ${description}`,\n error,\n );\n return res.status(400).json({\n error: 'invalid_request',\n error_description: description,\n });\n }\n });\n\n // Authorization Session rejection endpoint\n // Handles user rejection of Authorization Session requests and redirects with error\n router.post('/v1/sessions/:sessionId/reject', async (req, res) => {\n const { sessionId } = req.params;\n\n if (!sessionId) {\n return res.status(400).json({\n error: 'invalid_request',\n error_description: 'Missing authorization session ID',\n });\n }\n\n const httpCredentials = await this.httpAuth.credentials(req);\n\n if (!this.auth.isPrincipal(httpCredentials, 'user')) {\n return res.status(401).json({\n error: 'unauthorized',\n error_description: 'Authentication required',\n });\n }\n\n const { userEntityRef } = httpCredentials.principal;\n try {\n const session = await this.oidc.getAuthorizationSession({\n sessionId,\n });\n\n await this.oidc.rejectAuthorizationSession({\n sessionId,\n userEntityRef,\n });\n\n const errorParams = new URLSearchParams();\n errorParams.append('error', 'access_denied');\n errorParams.append('error_description', 'User denied the request');\n if (session.state) {\n errorParams.append('state', session.state);\n }\n\n const redirectUrl = new URL(session.redirectUri);\n redirectUrl.search = errorParams.toString();\n\n return res.json({\n redirectUrl: redirectUrl.toString(),\n });\n } catch (error) {\n const description = isError(error) ? error.message : 'Unknown error';\n this.logger.error(\n `Failed to reject authorization session: ${description}`,\n error,\n );\n\n return res.status(400).json({\n error: 'invalid_request',\n error_description: description,\n });\n }\n });\n\n // Token endpoint\n // https://openid.net/specs/openid-connect-core-1_0.html#TokenRequest\n // Exchanges authorization codes for access tokens and ID tokens\n router.post('/v1/token', async (req, res) => {\n // todo(blam): maybe add zod types for validating input\n const {\n grant_type: grantType,\n code,\n redirect_uri: redirectUri,\n code_verifier: codeVerifier,\n } = req.body;\n\n if (!grantType || !code || !redirectUri) {\n this.logger.error(\n `Failed to exchange code for token: Missing required parameters`,\n );\n return res.status(400).json({\n error: 'invalid_request',\n error_description: 'Missing required parameters',\n });\n }\n\n try {\n const result = await this.oidc.exchangeCodeForToken({\n code,\n redirectUri,\n codeVerifier,\n grantType,\n });\n\n return res.json({\n access_token: result.accessToken,\n token_type: result.tokenType,\n expires_in: result.expiresIn,\n id_token: result.idToken,\n scope: result.scope,\n });\n } catch (error) {\n const description = isError(error) ? error.message : 'Unknown error';\n this.logger.error(\n `Failed to exchange code for token: ${description}`,\n error,\n );\n\n if (isError(error)) {\n if (error.name === 'AuthenticationError') {\n return res.status(401).json({\n error: 'invalid_client',\n error_description: error.message,\n });\n }\n if (error.name === 'InputError') {\n return res.status(400).json({\n error: 'invalid_request',\n error_description: error.message,\n });\n }\n }\n\n return res.status(500).json({\n error: 'server_error',\n error_description: description,\n });\n }\n });\n\n // Dynamic Client Registration endpoint\n // https://openid.net/specs/openid-connect-registration-1_0.html#ClientRegistration\n // Allows clients to register themselves dynamically with the provider\n router.post('/v1/register', async (req, res) => {\n // todo(blam): maybe add zod types for validating input\n const {\n client_name: clientName,\n redirect_uris: redirectUris,\n response_types: responseTypes,\n grant_types: grantTypes,\n scope,\n } = req.body;\n\n if (!redirectUris?.length) {\n res.status(400).json({\n error: 'invalid_request',\n error_description: 'redirect_uris is required',\n });\n return;\n }\n\n try {\n const client = await this.oidc.registerClient({\n clientName,\n redirectUris,\n responseTypes,\n grantTypes,\n scope,\n });\n\n res.status(201).json({\n client_id: client.clientId,\n redirect_uris: client.redirectUris,\n client_secret: client.clientSecret,\n });\n } catch (e) {\n const description = isError(e) ? e.message : 'Unknown error';\n this.logger.error(`Failed to register client: ${description}`, e);\n\n res.status(500).json({\n error: 'server_error',\n error_description: `Failed to register client: ${description}`,\n });\n }\n });\n }\n\n return router;\n }\n}\nfunction ensureTrailingSlash(appUrl: string): string | URL | undefined {\n if (appUrl.endsWith('/')) {\n return appUrl;\n }\n return `${appUrl}/`;\n}\n"],"names":["OidcService","Router","json","AuthenticationError","isError"],"mappings":"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"}
1
+ {"version":3,"file":"OidcRouter.cjs.js","sources":["../../src/service/OidcRouter.ts"],"sourcesContent":["/*\n * Copyright 2025 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport Router from 'express-promise-router';\nimport { OidcService } from './OidcService';\nimport { AuthenticationError, isError } from '@backstage/errors';\nimport {\n AuthService,\n HttpAuthService,\n LoggerService,\n RootConfigService,\n} from '@backstage/backend-plugin-api';\nimport { TokenIssuer } from '../identity/types';\nimport { UserInfoDatabase } from '../database/UserInfoDatabase';\nimport { OidcDatabase } from '../database/OidcDatabase';\nimport { json } from 'express';\nimport { readDcrTokenExpiration } from './readTokenExpiration.ts';\n\nexport class OidcRouter {\n private readonly oidc: OidcService;\n private readonly logger: LoggerService;\n private readonly auth: AuthService;\n private readonly appUrl: string;\n private readonly httpAuth: HttpAuthService;\n private readonly config: RootConfigService;\n\n private constructor(\n oidc: OidcService,\n logger: LoggerService,\n auth: AuthService,\n appUrl: string,\n httpAuth: HttpAuthService,\n config: RootConfigService,\n ) {\n this.oidc = oidc;\n this.logger = logger;\n this.auth = auth;\n this.appUrl = appUrl;\n this.httpAuth = httpAuth;\n this.config = config;\n }\n\n static create(options: {\n auth: AuthService;\n tokenIssuer: TokenIssuer;\n baseUrl: string;\n appUrl: string;\n logger: LoggerService;\n userInfo: UserInfoDatabase;\n oidc: OidcDatabase;\n httpAuth: HttpAuthService;\n config: RootConfigService;\n }) {\n return new OidcRouter(\n OidcService.create(options),\n options.logger,\n options.auth,\n options.appUrl,\n options.httpAuth,\n options.config,\n );\n }\n\n public getRouter() {\n const router = Router();\n\n router.use(json());\n\n // OpenID Provider Configuration endpoint\n // https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfig\n // Returns the OpenID Provider Configuration document containing metadata about the provider\n router.get('/.well-known/openid-configuration', (_req, res) => {\n res.json(this.oidc.getConfiguration());\n });\n\n // JSON Web Key Set endpoint\n // https://openid.net/specs/openid-connect-core-1_0.html#rfc.section.10.1.1\n // Returns the public keys used to verify JWTs issued by this provider\n router.get('/.well-known/jwks.json', async (_req, res) => {\n const { keys } = await this.oidc.listPublicKeys();\n res.json({ keys });\n });\n\n // UserInfo endpoint\n // https://openid.net/specs/openid-connect-core-1_0.html#UserInfo\n // Returns claims about the authenticated user using an access token\n router.get('/v1/userinfo', async (req, res) => {\n const matches = req.headers.authorization?.match(/^Bearer[ ]+(\\S+)$/i);\n const token = matches?.[1];\n if (!token) {\n throw new AuthenticationError('No token provided');\n }\n\n const userInfo = await this.oidc.getUserInfo({ token });\n\n if (!userInfo) {\n res.status(404).send('User info not found');\n return;\n }\n\n res.json(userInfo);\n });\n\n if (\n this.config.getOptionalBoolean(\n 'auth.experimentalDynamicClientRegistration.enabled',\n )\n ) {\n // Authorization endpoint\n // https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest\n // Handles the initial authorization request from the client, validates parameters,\n // and redirects to the Authorization Session page for user approval\n router.get('/v1/authorize', async (req, res) => {\n // todo(blam): maybe add zod types for validating input\n const {\n client_id: clientId,\n redirect_uri: redirectUri,\n response_type: responseType,\n scope,\n state,\n nonce,\n code_challenge: codeChallenge,\n code_challenge_method: codeChallengeMethod,\n } = req.query;\n\n if (!clientId || !redirectUri || !responseType) {\n this.logger.error(`Failed to authorize: Missing required parameters`);\n return res.status(400).json({\n error: 'invalid_request',\n error_description:\n 'Missing required parameters: client_id, redirect_uri, response_type',\n });\n }\n\n try {\n const result = await this.oidc.createAuthorizationSession({\n clientId: clientId as string,\n redirectUri: redirectUri as string,\n responseType: responseType as string,\n scope: scope as string | undefined,\n state: state as string | undefined,\n nonce: nonce as string | undefined,\n codeChallenge: codeChallenge as string | undefined,\n codeChallengeMethod: codeChallengeMethod as string | undefined,\n });\n\n // todo(blam): maybe this URL could be overridable by config if\n // the plugin is mounted somewhere else?\n // support slashes in baseUrl?\n const authSessionRedirectUrl = new URL(\n `./oauth2/authorize/${result.id}`,\n ensureTrailingSlash(this.appUrl),\n );\n\n return res.redirect(authSessionRedirectUrl.toString());\n } catch (error) {\n const errorParams = new URLSearchParams();\n errorParams.append(\n 'error',\n isError(error) ? error.name : 'server_error',\n );\n errorParams.append(\n 'error_description',\n isError(error) ? error.message : 'Unknown error',\n );\n if (state) {\n errorParams.append('state', state as string);\n }\n\n const redirectUrl = new URL(redirectUri as string);\n redirectUrl.search = errorParams.toString();\n return res.redirect(redirectUrl.toString());\n }\n });\n\n // Authorization Session request details endpoint\n // Returns Authorization Session request details for the frontend\n router.get('/v1/sessions/:sessionId', async (req, res) => {\n const { sessionId } = req.params;\n\n if (!sessionId) {\n return res.status(400).json({\n error: 'invalid_request',\n error_description: 'Missing Authorization Session ID',\n });\n }\n\n try {\n const session = await this.oidc.getAuthorizationSession({\n sessionId,\n });\n\n return res.json({\n id: session.id,\n clientName: session.clientName,\n scope: session.scope,\n redirectUri: session.redirectUri,\n });\n } catch (error) {\n const description = isError(error) ? error.message : 'Unknown error';\n this.logger.error(\n `Failed to get authorization session: ${description}`,\n error,\n );\n return res.status(404).json({\n error: 'not_found',\n error_description: description,\n });\n }\n });\n\n // Authorization Session approval endpoint\n // Handles user approval of Authorization Session requests and generates authorization codes\n router.post('/v1/sessions/:sessionId/approve', async (req, res) => {\n const { sessionId } = req.params;\n\n if (!sessionId) {\n return res.status(400).json({\n error: 'invalid_request',\n error_description: 'Missing authorization session ID',\n });\n }\n\n try {\n const httpCredentials = await this.httpAuth.credentials(req);\n\n if (!this.auth.isPrincipal(httpCredentials, 'user')) {\n return res.status(401).json({\n error: 'unauthorized',\n error_description: 'Authentication required',\n });\n }\n\n const { userEntityRef } = httpCredentials.principal;\n\n const result = await this.oidc.approveAuthorizationSession({\n sessionId,\n userEntityRef,\n });\n\n return res.json({\n redirectUrl: result.redirectUrl,\n });\n } catch (error) {\n const description = isError(error) ? error.message : 'Unknown error';\n this.logger.error(\n `Failed to approve authorization session: ${description}`,\n error,\n );\n return res.status(400).json({\n error: 'invalid_request',\n error_description: description,\n });\n }\n });\n\n // Authorization Session rejection endpoint\n // Handles user rejection of Authorization Session requests and redirects with error\n router.post('/v1/sessions/:sessionId/reject', async (req, res) => {\n const { sessionId } = req.params;\n\n if (!sessionId) {\n return res.status(400).json({\n error: 'invalid_request',\n error_description: 'Missing authorization session ID',\n });\n }\n\n const httpCredentials = await this.httpAuth.credentials(req);\n\n if (!this.auth.isPrincipal(httpCredentials, 'user')) {\n return res.status(401).json({\n error: 'unauthorized',\n error_description: 'Authentication required',\n });\n }\n\n const { userEntityRef } = httpCredentials.principal;\n try {\n const session = await this.oidc.getAuthorizationSession({\n sessionId,\n });\n\n await this.oidc.rejectAuthorizationSession({\n sessionId,\n userEntityRef,\n });\n\n const errorParams = new URLSearchParams();\n errorParams.append('error', 'access_denied');\n errorParams.append('error_description', 'User denied the request');\n if (session.state) {\n errorParams.append('state', session.state);\n }\n\n const redirectUrl = new URL(session.redirectUri);\n redirectUrl.search = errorParams.toString();\n\n return res.json({\n redirectUrl: redirectUrl.toString(),\n });\n } catch (error) {\n const description = isError(error) ? error.message : 'Unknown error';\n this.logger.error(\n `Failed to reject authorization session: ${description}`,\n error,\n );\n\n return res.status(400).json({\n error: 'invalid_request',\n error_description: description,\n });\n }\n });\n\n // Token endpoint\n // https://openid.net/specs/openid-connect-core-1_0.html#TokenRequest\n // Exchanges authorization codes for access tokens and ID tokens\n router.post('/v1/token', async (req, res) => {\n // todo(blam): maybe add zod types for validating input\n const {\n grant_type: grantType,\n code,\n redirect_uri: redirectUri,\n code_verifier: codeVerifier,\n } = req.body;\n\n if (!grantType || !code || !redirectUri) {\n this.logger.error(\n `Failed to exchange code for token: Missing required parameters`,\n );\n return res.status(400).json({\n error: 'invalid_request',\n error_description: 'Missing required parameters',\n });\n }\n\n const expiresIn = readDcrTokenExpiration(this.config);\n\n try {\n const result = await this.oidc.exchangeCodeForToken({\n code,\n redirectUri,\n codeVerifier,\n grantType,\n expiresIn,\n });\n\n return res.json({\n access_token: result.accessToken,\n token_type: result.tokenType,\n expires_in: result.expiresIn,\n id_token: result.idToken,\n scope: result.scope,\n });\n } catch (error) {\n const description = isError(error) ? error.message : 'Unknown error';\n this.logger.error(\n `Failed to exchange code for token: ${description}`,\n error,\n );\n\n if (isError(error)) {\n if (error.name === 'AuthenticationError') {\n return res.status(401).json({\n error: 'invalid_client',\n error_description: error.message,\n });\n }\n if (error.name === 'InputError') {\n return res.status(400).json({\n error: 'invalid_request',\n error_description: error.message,\n });\n }\n }\n\n return res.status(500).json({\n error: 'server_error',\n error_description: description,\n });\n }\n });\n\n // Dynamic Client Registration endpoint\n // https://openid.net/specs/openid-connect-registration-1_0.html#ClientRegistration\n // Allows clients to register themselves dynamically with the provider\n router.post('/v1/register', async (req, res) => {\n // todo(blam): maybe add zod types for validating input\n const {\n client_name: clientName,\n redirect_uris: redirectUris,\n response_types: responseTypes,\n grant_types: grantTypes,\n scope,\n } = req.body;\n\n if (!redirectUris?.length) {\n res.status(400).json({\n error: 'invalid_request',\n error_description: 'redirect_uris is required',\n });\n return;\n }\n\n try {\n const client = await this.oidc.registerClient({\n clientName,\n redirectUris,\n responseTypes,\n grantTypes,\n scope,\n });\n\n res.status(201).json({\n client_id: client.clientId,\n redirect_uris: client.redirectUris,\n client_secret: client.clientSecret,\n });\n } catch (e) {\n const description = isError(e) ? e.message : 'Unknown error';\n this.logger.error(`Failed to register client: ${description}`, e);\n\n res.status(500).json({\n error: 'server_error',\n error_description: `Failed to register client: ${description}`,\n });\n }\n });\n }\n\n return router;\n }\n}\nfunction ensureTrailingSlash(appUrl: string): string | URL | undefined {\n if (appUrl.endsWith('/')) {\n return appUrl;\n }\n return `${appUrl}/`;\n}\n"],"names":["OidcService","Router","json","AuthenticationError","isError","readDcrTokenExpiration"],"mappings":"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"}
@@ -12,6 +12,12 @@ var crypto__default = /*#__PURE__*/_interopDefaultCompat(crypto);
12
12
  var matcher__default = /*#__PURE__*/_interopDefaultCompat(matcher);
13
13
 
14
14
  class OidcService {
15
+ auth;
16
+ tokenIssuer;
17
+ baseUrl;
18
+ userInfo;
19
+ oidc;
20
+ config;
15
21
  constructor(auth, tokenIssuer, baseUrl, userInfo, oidc, config) {
16
22
  this.auth = auth;
17
23
  this.tokenIssuer = tokenIssuer;
@@ -238,7 +244,7 @@ class OidcService {
238
244
  });
239
245
  }
240
246
  async exchangeCodeForToken(params) {
241
- const { code, redirectUri, codeVerifier, grantType } = params;
247
+ const { code, redirectUri, codeVerifier, grantType, expiresIn } = params;
242
248
  if (grantType !== "authorization_code") {
243
249
  throw new errors.InputError("Unsupported grant type");
244
250
  }
@@ -291,7 +297,7 @@ class OidcService {
291
297
  return {
292
298
  accessToken: token,
293
299
  tokenType: "Bearer",
294
- expiresIn: 3600,
300
+ expiresIn,
295
301
  idToken: token,
296
302
  scope: session.scope || "openid"
297
303
  };
@@ -1 +1 @@
1
- {"version":3,"file":"OidcService.cjs.js","sources":["../../src/service/OidcService.ts"],"sourcesContent":["/*\n * Copyright 2025 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { AuthService, RootConfigService } from '@backstage/backend-plugin-api';\nimport { TokenIssuer } from '../identity/types';\nimport { UserInfoDatabase } from '../database/UserInfoDatabase';\nimport {\n InputError,\n AuthenticationError,\n NotFoundError,\n} from '@backstage/errors';\nimport { decodeJwt } from 'jose';\nimport crypto from 'crypto';\nimport { OidcDatabase } from '../database/OidcDatabase';\nimport { DateTime } from 'luxon';\nimport matcher from 'matcher';\n\nexport class OidcService {\n private constructor(\n private readonly auth: AuthService,\n private readonly tokenIssuer: TokenIssuer,\n private readonly baseUrl: string,\n private readonly userInfo: UserInfoDatabase,\n private readonly oidc: OidcDatabase,\n private readonly config: RootConfigService,\n ) {}\n\n static create(options: {\n auth: AuthService;\n tokenIssuer: TokenIssuer;\n baseUrl: string;\n userInfo: UserInfoDatabase;\n oidc: OidcDatabase;\n config: RootConfigService;\n }) {\n return new OidcService(\n options.auth,\n options.tokenIssuer,\n options.baseUrl,\n options.userInfo,\n options.oidc,\n options.config,\n );\n }\n\n public getConfiguration() {\n return {\n issuer: this.baseUrl,\n token_endpoint: `${this.baseUrl}/v1/token`,\n userinfo_endpoint: `${this.baseUrl}/v1/userinfo`,\n jwks_uri: `${this.baseUrl}/.well-known/jwks.json`,\n response_types_supported: ['code', 'id_token'],\n subject_types_supported: ['public'],\n id_token_signing_alg_values_supported: [\n 'RS256',\n 'RS384',\n 'RS512',\n 'ES256',\n 'ES384',\n 'ES512',\n 'PS256',\n 'PS384',\n 'PS512',\n 'EdDSA',\n ],\n scopes_supported: ['openid'],\n token_endpoint_auth_methods_supported: [\n 'client_secret_basic',\n 'client_secret_post',\n ],\n claims_supported: ['sub', 'ent'],\n grant_types_supported: ['authorization_code'],\n authorization_endpoint: `${this.baseUrl}/v1/authorize`,\n registration_endpoint: `${this.baseUrl}/v1/register`,\n code_challenge_methods_supported: ['S256', 'plain'],\n };\n }\n\n public async listPublicKeys() {\n return await this.tokenIssuer.listPublicKeys();\n }\n\n public async getUserInfo({ token }: { token: string }) {\n const credentials = await this.auth.authenticate(token, {\n allowLimitedAccess: true,\n });\n if (!this.auth.isPrincipal(credentials, 'user')) {\n throw new InputError(\n 'Userinfo endpoint must be called with a token that represents a user principal',\n );\n }\n\n const { sub: userEntityRef } = decodeJwt(token);\n\n if (typeof userEntityRef !== 'string') {\n throw new Error('Invalid user token, user entity ref must be a string');\n }\n return await this.userInfo.getUserInfo(userEntityRef);\n }\n\n public async registerClient(opts: {\n responseTypes?: string[];\n grantTypes?: string[];\n clientName: string;\n redirectUris?: string[];\n scope?: string;\n }) {\n const generatedClientId = crypto.randomUUID();\n const generatedClientSecret = crypto.randomUUID();\n\n const allowedRedirectUriPatterns = this.config.getOptionalStringArray(\n 'auth.experimentalDynamicClientRegistration.allowedRedirectUriPatterns',\n ) ?? ['*'];\n\n for (const redirectUri of opts.redirectUris ?? []) {\n if (\n !allowedRedirectUriPatterns.some(pattern =>\n matcher.isMatch(redirectUri, pattern),\n )\n ) {\n throw new InputError('Invalid redirect_uri');\n }\n }\n\n return await this.oidc.createClient({\n clientId: generatedClientId,\n clientName: opts.clientName,\n clientSecret: generatedClientSecret,\n redirectUris: opts.redirectUris ?? [],\n responseTypes: opts.responseTypes ?? ['code'],\n grantTypes: opts.grantTypes ?? ['authorization_code'],\n scope: opts.scope,\n });\n }\n\n public async createAuthorizationSession(opts: {\n clientId: string;\n redirectUri: string;\n responseType: string;\n scope?: string;\n state?: string;\n nonce?: string;\n codeChallenge?: string;\n codeChallengeMethod?: string;\n }) {\n const {\n clientId,\n redirectUri,\n responseType,\n scope,\n state,\n nonce,\n codeChallenge,\n codeChallengeMethod,\n } = opts;\n\n if (responseType !== 'code') {\n throw new InputError('Only authorization code flow is supported');\n }\n\n const client = await this.oidc.getClient({ clientId });\n if (!client) {\n throw new InputError('Invalid client_id');\n }\n\n if (!client.redirectUris.includes(redirectUri)) {\n throw new InputError('Invalid redirect_uri');\n }\n\n if (codeChallenge) {\n if (\n !codeChallengeMethod ||\n !['S256', 'plain'].includes(codeChallengeMethod)\n ) {\n throw new InputError('Invalid code_challenge_method');\n }\n }\n\n const sessionId = crypto.randomUUID();\n const sessionExpiresAt = DateTime.now().plus({ hours: 1 }).toJSDate();\n\n await this.oidc.createAuthorizationSession({\n id: sessionId,\n clientId,\n redirectUri,\n responseType,\n scope,\n state,\n codeChallenge,\n codeChallengeMethod,\n nonce,\n expiresAt: sessionExpiresAt,\n });\n\n return {\n id: sessionId,\n clientName: client.clientName,\n scope,\n redirectUri,\n };\n }\n\n public async approveAuthorizationSession(opts: {\n sessionId: string;\n userEntityRef: string;\n }) {\n const { sessionId, userEntityRef } = opts;\n\n const session = await this.oidc.getAuthorizationSession({\n id: sessionId,\n });\n\n if (!session) {\n throw new NotFoundError('Invalid authorization session');\n }\n\n if (DateTime.fromJSDate(session.expiresAt) < DateTime.now()) {\n throw new InputError('Authorization session expired');\n }\n\n if (session.status !== 'pending') {\n throw new NotFoundError('Authorization session not found or expired');\n }\n\n await this.oidc.updateAuthorizationSession({\n id: session.id,\n userEntityRef,\n status: 'approved',\n });\n\n const authorizationCode = crypto.randomBytes(32).toString('base64url');\n const codeExpiresAt = DateTime.now().plus({ minutes: 10 }).toJSDate();\n\n await this.oidc.createAuthorizationCode({\n code: authorizationCode,\n sessionId: session.id,\n expiresAt: codeExpiresAt,\n });\n\n const redirectUrl = new URL(session.redirectUri);\n\n redirectUrl.searchParams.append('code', authorizationCode);\n if (session.state) {\n redirectUrl.searchParams.append('state', session.state);\n }\n\n return {\n redirectUrl: redirectUrl.toString(),\n };\n }\n\n public async getAuthorizationSession(opts: { sessionId: string }) {\n const session = await this.oidc.getAuthorizationSession({\n id: opts.sessionId,\n });\n\n if (!session) {\n throw new NotFoundError('Invalid authorization session');\n }\n\n if (DateTime.fromJSDate(session.expiresAt) < DateTime.now()) {\n throw new InputError('Authorization session expired');\n }\n\n if (session.status !== 'pending') {\n throw new NotFoundError('Authorization session not found or expired');\n }\n\n const client = await this.oidc.getClient({ clientId: session.clientId });\n if (!client) {\n throw new InputError('Invalid client_id');\n }\n\n return {\n id: session.id,\n clientId: session.clientId,\n clientName: client.clientName,\n redirectUri: session.redirectUri,\n scope: session.scope,\n state: session.state,\n responseType: session.responseType,\n codeChallenge: session.codeChallenge,\n codeChallengeMethod: session.codeChallengeMethod,\n nonce: session.nonce,\n expiresAt: session.expiresAt,\n status: session.status,\n };\n }\n\n public async rejectAuthorizationSession(opts: {\n sessionId: string;\n userEntityRef: string;\n }) {\n const { sessionId, userEntityRef } = opts;\n\n const session = await this.oidc.getAuthorizationSession({\n id: sessionId,\n });\n\n if (!session) {\n throw new NotFoundError('Invalid authorization session');\n }\n\n if (DateTime.fromJSDate(session.expiresAt) < DateTime.now()) {\n throw new InputError('Authorization session expired');\n }\n\n if (session.status !== 'pending') {\n throw new NotFoundError('Authorization session not found or expired');\n }\n\n await this.oidc.updateAuthorizationSession({\n id: session.id,\n status: 'rejected',\n userEntityRef,\n });\n }\n\n public async exchangeCodeForToken(params: {\n code: string;\n redirectUri: string;\n codeVerifier?: string;\n grantType: string;\n }) {\n const { code, redirectUri, codeVerifier, grantType } = params;\n\n if (grantType !== 'authorization_code') {\n throw new InputError('Unsupported grant type');\n }\n\n const authCode = await this.oidc.getAuthorizationCode({ code });\n if (!authCode) {\n throw new AuthenticationError('Invalid authorization code');\n }\n\n if (DateTime.fromJSDate(authCode.expiresAt) < DateTime.now()) {\n throw new AuthenticationError('Authorization code expired');\n }\n\n if (authCode.used) {\n throw new AuthenticationError('Authorization code already used');\n }\n\n const session = await this.oidc.getAuthorizationSession({\n id: authCode.sessionId,\n });\n\n if (!session) {\n throw new NotFoundError('Invalid authorization session');\n }\n\n if (session.redirectUri !== redirectUri) {\n throw new AuthenticationError('Redirect URI mismatch');\n }\n\n if (session.status !== 'approved') {\n throw new AuthenticationError('Authorization not approved');\n }\n\n if (!session.userEntityRef) {\n throw new AuthenticationError('No user associated with authorization');\n }\n\n if (session.codeChallenge) {\n if (!codeVerifier) {\n throw new AuthenticationError('Code verifier required for PKCE');\n }\n\n if (\n !this.verifyPkce(\n session.codeChallenge,\n codeVerifier,\n session.codeChallengeMethod,\n )\n ) {\n throw new AuthenticationError('Invalid code verifier');\n }\n }\n\n await this.oidc.updateAuthorizationCode({\n code,\n used: true,\n });\n\n const { token } = await this.tokenIssuer.issueToken({\n claims: {\n sub: session.userEntityRef,\n },\n });\n\n return {\n accessToken: token,\n tokenType: 'Bearer',\n expiresIn: 3600,\n idToken: token,\n scope: session.scope || 'openid',\n };\n }\n\n private verifyPkce(\n codeChallenge: string,\n codeVerifier: string,\n method?: string,\n ): boolean {\n if (!method || method === 'plain') {\n return codeChallenge === codeVerifier;\n }\n\n if (method === 'S256') {\n const hash = crypto.createHash('sha256').update(codeVerifier).digest();\n const base64urlHash = hash.toString('base64url');\n return codeChallenge === base64urlHash;\n }\n\n return false;\n }\n}\n"],"names":["InputError","decodeJwt","crypto","matcher","DateTime","NotFoundError","AuthenticationError"],"mappings":";;;;;;;;;;;;;AA6BO,MAAM,WAAA,CAAY;AAAA,EACf,YACW,IAAA,EACA,WAAA,EACA,OAAA,EACA,QAAA,EACA,MACA,MAAA,EACjB;AANiB,IAAA,IAAA,CAAA,IAAA,GAAA,IAAA;AACA,IAAA,IAAA,CAAA,WAAA,GAAA,WAAA;AACA,IAAA,IAAA,CAAA,OAAA,GAAA,OAAA;AACA,IAAA,IAAA,CAAA,QAAA,GAAA,QAAA;AACA,IAAA,IAAA,CAAA,IAAA,GAAA,IAAA;AACA,IAAA,IAAA,CAAA,MAAA,GAAA,MAAA;AAAA,EAChB;AAAA,EAEH,OAAO,OAAO,OAAA,EAOX;AACD,IAAA,OAAO,IAAI,WAAA;AAAA,MACT,OAAA,CAAQ,IAAA;AAAA,MACR,OAAA,CAAQ,WAAA;AAAA,MACR,OAAA,CAAQ,OAAA;AAAA,MACR,OAAA,CAAQ,QAAA;AAAA,MACR,OAAA,CAAQ,IAAA;AAAA,MACR,OAAA,CAAQ;AAAA,KACV;AAAA,EACF;AAAA,EAEO,gBAAA,GAAmB;AACxB,IAAA,OAAO;AAAA,MACL,QAAQ,IAAA,CAAK,OAAA;AAAA,MACb,cAAA,EAAgB,CAAA,EAAG,IAAA,CAAK,OAAO,CAAA,SAAA,CAAA;AAAA,MAC/B,iBAAA,EAAmB,CAAA,EAAG,IAAA,CAAK,OAAO,CAAA,YAAA,CAAA;AAAA,MAClC,QAAA,EAAU,CAAA,EAAG,IAAA,CAAK,OAAO,CAAA,sBAAA,CAAA;AAAA,MACzB,wBAAA,EAA0B,CAAC,MAAA,EAAQ,UAAU,CAAA;AAAA,MAC7C,uBAAA,EAAyB,CAAC,QAAQ,CAAA;AAAA,MAClC,qCAAA,EAAuC;AAAA,QACrC,OAAA;AAAA,QACA,OAAA;AAAA,QACA,OAAA;AAAA,QACA,OAAA;AAAA,QACA,OAAA;AAAA,QACA,OAAA;AAAA,QACA,OAAA;AAAA,QACA,OAAA;AAAA,QACA,OAAA;AAAA,QACA;AAAA,OACF;AAAA,MACA,gBAAA,EAAkB,CAAC,QAAQ,CAAA;AAAA,MAC3B,qCAAA,EAAuC;AAAA,QACrC,qBAAA;AAAA,QACA;AAAA,OACF;AAAA,MACA,gBAAA,EAAkB,CAAC,KAAA,EAAO,KAAK,CAAA;AAAA,MAC/B,qBAAA,EAAuB,CAAC,oBAAoB,CAAA;AAAA,MAC5C,sBAAA,EAAwB,CAAA,EAAG,IAAA,CAAK,OAAO,CAAA,aAAA,CAAA;AAAA,MACvC,qBAAA,EAAuB,CAAA,EAAG,IAAA,CAAK,OAAO,CAAA,YAAA,CAAA;AAAA,MACtC,gCAAA,EAAkC,CAAC,MAAA,EAAQ,OAAO;AAAA,KACpD;AAAA,EACF;AAAA,EAEA,MAAa,cAAA,GAAiB;AAC5B,IAAA,OAAO,MAAM,IAAA,CAAK,WAAA,CAAY,cAAA,EAAe;AAAA,EAC/C;AAAA,EAEA,MAAa,WAAA,CAAY,EAAE,KAAA,EAAM,EAAsB;AACrD,IAAA,MAAM,WAAA,GAAc,MAAM,IAAA,CAAK,IAAA,CAAK,aAAa,KAAA,EAAO;AAAA,MACtD,kBAAA,EAAoB;AAAA,KACrB,CAAA;AACD,IAAA,IAAI,CAAC,IAAA,CAAK,IAAA,CAAK,WAAA,CAAY,WAAA,EAAa,MAAM,CAAA,EAAG;AAC/C,MAAA,MAAM,IAAIA,iBAAA;AAAA,QACR;AAAA,OACF;AAAA,IACF;AAEA,IAAA,MAAM,EAAE,GAAA,EAAK,aAAA,EAAc,GAAIC,eAAU,KAAK,CAAA;AAE9C,IAAA,IAAI,OAAO,kBAAkB,QAAA,EAAU;AACrC,MAAA,MAAM,IAAI,MAAM,sDAAsD,CAAA;AAAA,IACxE;AACA,IAAA,OAAO,MAAM,IAAA,CAAK,QAAA,CAAS,WAAA,CAAY,aAAa,CAAA;AAAA,EACtD;AAAA,EAEA,MAAa,eAAe,IAAA,EAMzB;AACD,IAAA,MAAM,iBAAA,GAAoBC,wBAAO,UAAA,EAAW;AAC5C,IAAA,MAAM,qBAAA,GAAwBA,wBAAO,UAAA,EAAW;AAEhD,IAAA,MAAM,0BAAA,GAA6B,KAAK,MAAA,CAAO,sBAAA;AAAA,MAC7C;AAAA,KACF,IAAK,CAAC,GAAG,CAAA;AAET,IAAA,KAAA,MAAW,WAAA,IAAe,IAAA,CAAK,YAAA,IAAgB,EAAC,EAAG;AACjD,MAAA,IACE,CAAC,0BAAA,CAA2B,IAAA;AAAA,QAAK,CAAA,OAAA,KAC/BC,wBAAA,CAAQ,OAAA,CAAQ,WAAA,EAAa,OAAO;AAAA,OACtC,EACA;AACA,QAAA,MAAM,IAAIH,kBAAW,sBAAsB,CAAA;AAAA,MAC7C;AAAA,IACF;AAEA,IAAA,OAAO,MAAM,IAAA,CAAK,IAAA,CAAK,YAAA,CAAa;AAAA,MAClC,QAAA,EAAU,iBAAA;AAAA,MACV,YAAY,IAAA,CAAK,UAAA;AAAA,MACjB,YAAA,EAAc,qBAAA;AAAA,MACd,YAAA,EAAc,IAAA,CAAK,YAAA,IAAgB,EAAC;AAAA,MACpC,aAAA,EAAe,IAAA,CAAK,aAAA,IAAiB,CAAC,MAAM,CAAA;AAAA,MAC5C,UAAA,EAAY,IAAA,CAAK,UAAA,IAAc,CAAC,oBAAoB,CAAA;AAAA,MACpD,OAAO,IAAA,CAAK;AAAA,KACb,CAAA;AAAA,EACH;AAAA,EAEA,MAAa,2BAA2B,IAAA,EASrC;AACD,IAAA,MAAM;AAAA,MACJ,QAAA;AAAA,MACA,WAAA;AAAA,MACA,YAAA;AAAA,MACA,KAAA;AAAA,MACA,KAAA;AAAA,MACA,KAAA;AAAA,MACA,aAAA;AAAA,MACA;AAAA,KACF,GAAI,IAAA;AAEJ,IAAA,IAAI,iBAAiB,MAAA,EAAQ;AAC3B,MAAA,MAAM,IAAIA,kBAAW,2CAA2C,CAAA;AAAA,IAClE;AAEA,IAAA,MAAM,SAAS,MAAM,IAAA,CAAK,KAAK,SAAA,CAAU,EAAE,UAAU,CAAA;AACrD,IAAA,IAAI,CAAC,MAAA,EAAQ;AACX,MAAA,MAAM,IAAIA,kBAAW,mBAAmB,CAAA;AAAA,IAC1C;AAEA,IAAA,IAAI,CAAC,MAAA,CAAO,YAAA,CAAa,QAAA,CAAS,WAAW,CAAA,EAAG;AAC9C,MAAA,MAAM,IAAIA,kBAAW,sBAAsB,CAAA;AAAA,IAC7C;AAEA,IAAA,IAAI,aAAA,EAAe;AACjB,MAAA,IACE,CAAC,uBACD,CAAC,CAAC,QAAQ,OAAO,CAAA,CAAE,QAAA,CAAS,mBAAmB,CAAA,EAC/C;AACA,QAAA,MAAM,IAAIA,kBAAW,+BAA+B,CAAA;AAAA,MACtD;AAAA,IACF;AAEA,IAAA,MAAM,SAAA,GAAYE,wBAAO,UAAA,EAAW;AACpC,IAAA,MAAM,gBAAA,GAAmBE,cAAA,CAAS,GAAA,EAAI,CAAE,IAAA,CAAK,EAAE,KAAA,EAAO,CAAA,EAAG,CAAA,CAAE,QAAA,EAAS;AAEpE,IAAA,MAAM,IAAA,CAAK,KAAK,0BAAA,CAA2B;AAAA,MACzC,EAAA,EAAI,SAAA;AAAA,MACJ,QAAA;AAAA,MACA,WAAA;AAAA,MACA,YAAA;AAAA,MACA,KAAA;AAAA,MACA,KAAA;AAAA,MACA,aAAA;AAAA,MACA,mBAAA;AAAA,MACA,KAAA;AAAA,MACA,SAAA,EAAW;AAAA,KACZ,CAAA;AAED,IAAA,OAAO;AAAA,MACL,EAAA,EAAI,SAAA;AAAA,MACJ,YAAY,MAAA,CAAO,UAAA;AAAA,MACnB,KAAA;AAAA,MACA;AAAA,KACF;AAAA,EACF;AAAA,EAEA,MAAa,4BAA4B,IAAA,EAGtC;AACD,IAAA,MAAM,EAAE,SAAA,EAAW,aAAA,EAAc,GAAI,IAAA;AAErC,IAAA,MAAM,OAAA,GAAU,MAAM,IAAA,CAAK,IAAA,CAAK,uBAAA,CAAwB;AAAA,MACtD,EAAA,EAAI;AAAA,KACL,CAAA;AAED,IAAA,IAAI,CAAC,OAAA,EAAS;AACZ,MAAA,MAAM,IAAIC,qBAAc,+BAA+B,CAAA;AAAA,IACzD;AAEA,IAAA,IAAID,eAAS,UAAA,CAAW,OAAA,CAAQ,SAAS,CAAA,GAAIA,cAAA,CAAS,KAAI,EAAG;AAC3D,MAAA,MAAM,IAAIJ,kBAAW,+BAA+B,CAAA;AAAA,IACtD;AAEA,IAAA,IAAI,OAAA,CAAQ,WAAW,SAAA,EAAW;AAChC,MAAA,MAAM,IAAIK,qBAAc,4CAA4C,CAAA;AAAA,IACtE;AAEA,IAAA,MAAM,IAAA,CAAK,KAAK,0BAAA,CAA2B;AAAA,MACzC,IAAI,OAAA,CAAQ,EAAA;AAAA,MACZ,aAAA;AAAA,MACA,MAAA,EAAQ;AAAA,KACT,CAAA;AAED,IAAA,MAAM,oBAAoBH,uBAAA,CAAO,WAAA,CAAY,EAAE,CAAA,CAAE,SAAS,WAAW,CAAA;AACrE,IAAA,MAAM,aAAA,GAAgBE,cAAA,CAAS,GAAA,EAAI,CAAE,IAAA,CAAK,EAAE,OAAA,EAAS,EAAA,EAAI,CAAA,CAAE,QAAA,EAAS;AAEpE,IAAA,MAAM,IAAA,CAAK,KAAK,uBAAA,CAAwB;AAAA,MACtC,IAAA,EAAM,iBAAA;AAAA,MACN,WAAW,OAAA,CAAQ,EAAA;AAAA,MACnB,SAAA,EAAW;AAAA,KACZ,CAAA;AAED,IAAA,MAAM,WAAA,GAAc,IAAI,GAAA,CAAI,OAAA,CAAQ,WAAW,CAAA;AAE/C,IAAA,WAAA,CAAY,YAAA,CAAa,MAAA,CAAO,MAAA,EAAQ,iBAAiB,CAAA;AACzD,IAAA,IAAI,QAAQ,KAAA,EAAO;AACjB,MAAA,WAAA,CAAY,YAAA,CAAa,MAAA,CAAO,OAAA,EAAS,OAAA,CAAQ,KAAK,CAAA;AAAA,IACxD;AAEA,IAAA,OAAO;AAAA,MACL,WAAA,EAAa,YAAY,QAAA;AAAS,KACpC;AAAA,EACF;AAAA,EAEA,MAAa,wBAAwB,IAAA,EAA6B;AAChE,IAAA,MAAM,OAAA,GAAU,MAAM,IAAA,CAAK,IAAA,CAAK,uBAAA,CAAwB;AAAA,MACtD,IAAI,IAAA,CAAK;AAAA,KACV,CAAA;AAED,IAAA,IAAI,CAAC,OAAA,EAAS;AACZ,MAAA,MAAM,IAAIC,qBAAc,+BAA+B,CAAA;AAAA,IACzD;AAEA,IAAA,IAAID,eAAS,UAAA,CAAW,OAAA,CAAQ,SAAS,CAAA,GAAIA,cAAA,CAAS,KAAI,EAAG;AAC3D,MAAA,MAAM,IAAIJ,kBAAW,+BAA+B,CAAA;AAAA,IACtD;AAEA,IAAA,IAAI,OAAA,CAAQ,WAAW,SAAA,EAAW;AAChC,MAAA,MAAM,IAAIK,qBAAc,4CAA4C,CAAA;AAAA,IACtE;AAEA,IAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,IAAA,CAAK,UAAU,EAAE,QAAA,EAAU,OAAA,CAAQ,QAAA,EAAU,CAAA;AACvE,IAAA,IAAI,CAAC,MAAA,EAAQ;AACX,MAAA,MAAM,IAAIL,kBAAW,mBAAmB,CAAA;AAAA,IAC1C;AAEA,IAAA,OAAO;AAAA,MACL,IAAI,OAAA,CAAQ,EAAA;AAAA,MACZ,UAAU,OAAA,CAAQ,QAAA;AAAA,MAClB,YAAY,MAAA,CAAO,UAAA;AAAA,MACnB,aAAa,OAAA,CAAQ,WAAA;AAAA,MACrB,OAAO,OAAA,CAAQ,KAAA;AAAA,MACf,OAAO,OAAA,CAAQ,KAAA;AAAA,MACf,cAAc,OAAA,CAAQ,YAAA;AAAA,MACtB,eAAe,OAAA,CAAQ,aAAA;AAAA,MACvB,qBAAqB,OAAA,CAAQ,mBAAA;AAAA,MAC7B,OAAO,OAAA,CAAQ,KAAA;AAAA,MACf,WAAW,OAAA,CAAQ,SAAA;AAAA,MACnB,QAAQ,OAAA,CAAQ;AAAA,KAClB;AAAA,EACF;AAAA,EAEA,MAAa,2BAA2B,IAAA,EAGrC;AACD,IAAA,MAAM,EAAE,SAAA,EAAW,aAAA,EAAc,GAAI,IAAA;AAErC,IAAA,MAAM,OAAA,GAAU,MAAM,IAAA,CAAK,IAAA,CAAK,uBAAA,CAAwB;AAAA,MACtD,EAAA,EAAI;AAAA,KACL,CAAA;AAED,IAAA,IAAI,CAAC,OAAA,EAAS;AACZ,MAAA,MAAM,IAAIK,qBAAc,+BAA+B,CAAA;AAAA,IACzD;AAEA,IAAA,IAAID,eAAS,UAAA,CAAW,OAAA,CAAQ,SAAS,CAAA,GAAIA,cAAA,CAAS,KAAI,EAAG;AAC3D,MAAA,MAAM,IAAIJ,kBAAW,+BAA+B,CAAA;AAAA,IACtD;AAEA,IAAA,IAAI,OAAA,CAAQ,WAAW,SAAA,EAAW;AAChC,MAAA,MAAM,IAAIK,qBAAc,4CAA4C,CAAA;AAAA,IACtE;AAEA,IAAA,MAAM,IAAA,CAAK,KAAK,0BAAA,CAA2B;AAAA,MACzC,IAAI,OAAA,CAAQ,EAAA;AAAA,MACZ,MAAA,EAAQ,UAAA;AAAA,MACR;AAAA,KACD,CAAA;AAAA,EACH;AAAA,EAEA,MAAa,qBAAqB,MAAA,EAK/B;AACD,IAAA,MAAM,EAAE,IAAA,EAAM,WAAA,EAAa,YAAA,EAAc,WAAU,GAAI,MAAA;AAEvD,IAAA,IAAI,cAAc,oBAAA,EAAsB;AACtC,MAAA,MAAM,IAAIL,kBAAW,wBAAwB,CAAA;AAAA,IAC/C;AAEA,IAAA,MAAM,WAAW,MAAM,IAAA,CAAK,KAAK,oBAAA,CAAqB,EAAE,MAAM,CAAA;AAC9D,IAAA,IAAI,CAAC,QAAA,EAAU;AACb,MAAA,MAAM,IAAIM,2BAAoB,4BAA4B,CAAA;AAAA,IAC5D;AAEA,IAAA,IAAIF,eAAS,UAAA,CAAW,QAAA,CAAS,SAAS,CAAA,GAAIA,cAAA,CAAS,KAAI,EAAG;AAC5D,MAAA,MAAM,IAAIE,2BAAoB,4BAA4B,CAAA;AAAA,IAC5D;AAEA,IAAA,IAAI,SAAS,IAAA,EAAM;AACjB,MAAA,MAAM,IAAIA,2BAAoB,iCAAiC,CAAA;AAAA,IACjE;AAEA,IAAA,MAAM,OAAA,GAAU,MAAM,IAAA,CAAK,IAAA,CAAK,uBAAA,CAAwB;AAAA,MACtD,IAAI,QAAA,CAAS;AAAA,KACd,CAAA;AAED,IAAA,IAAI,CAAC,OAAA,EAAS;AACZ,MAAA,MAAM,IAAID,qBAAc,+BAA+B,CAAA;AAAA,IACzD;AAEA,IAAA,IAAI,OAAA,CAAQ,gBAAgB,WAAA,EAAa;AACvC,MAAA,MAAM,IAAIC,2BAAoB,uBAAuB,CAAA;AAAA,IACvD;AAEA,IAAA,IAAI,OAAA,CAAQ,WAAW,UAAA,EAAY;AACjC,MAAA,MAAM,IAAIA,2BAAoB,4BAA4B,CAAA;AAAA,IAC5D;AAEA,IAAA,IAAI,CAAC,QAAQ,aAAA,EAAe;AAC1B,MAAA,MAAM,IAAIA,2BAAoB,uCAAuC,CAAA;AAAA,IACvE;AAEA,IAAA,IAAI,QAAQ,aAAA,EAAe;AACzB,MAAA,IAAI,CAAC,YAAA,EAAc;AACjB,QAAA,MAAM,IAAIA,2BAAoB,iCAAiC,CAAA;AAAA,MACjE;AAEA,MAAA,IACE,CAAC,IAAA,CAAK,UAAA;AAAA,QACJ,OAAA,CAAQ,aAAA;AAAA,QACR,YAAA;AAAA,QACA,OAAA,CAAQ;AAAA,OACV,EACA;AACA,QAAA,MAAM,IAAIA,2BAAoB,uBAAuB,CAAA;AAAA,MACvD;AAAA,IACF;AAEA,IAAA,MAAM,IAAA,CAAK,KAAK,uBAAA,CAAwB;AAAA,MACtC,IAAA;AAAA,MACA,IAAA,EAAM;AAAA,KACP,CAAA;AAED,IAAA,MAAM,EAAE,KAAA,EAAM,GAAI,MAAM,IAAA,CAAK,YAAY,UAAA,CAAW;AAAA,MAClD,MAAA,EAAQ;AAAA,QACN,KAAK,OAAA,CAAQ;AAAA;AACf,KACD,CAAA;AAED,IAAA,OAAO;AAAA,MACL,WAAA,EAAa,KAAA;AAAA,MACb,SAAA,EAAW,QAAA;AAAA,MACX,SAAA,EAAW,IAAA;AAAA,MACX,OAAA,EAAS,KAAA;AAAA,MACT,KAAA,EAAO,QAAQ,KAAA,IAAS;AAAA,KAC1B;AAAA,EACF;AAAA,EAEQ,UAAA,CACN,aAAA,EACA,YAAA,EACA,MAAA,EACS;AACT,IAAA,IAAI,CAAC,MAAA,IAAU,MAAA,KAAW,OAAA,EAAS;AACjC,MAAA,OAAO,aAAA,KAAkB,YAAA;AAAA,IAC3B;AAEA,IAAA,IAAI,WAAW,MAAA,EAAQ;AACrB,MAAA,MAAM,IAAA,GAAOJ,wBAAO,UAAA,CAAW,QAAQ,EAAE,MAAA,CAAO,YAAY,EAAE,MAAA,EAAO;AACrE,MAAA,MAAM,aAAA,GAAgB,IAAA,CAAK,QAAA,CAAS,WAAW,CAAA;AAC/C,MAAA,OAAO,aAAA,KAAkB,aAAA;AAAA,IAC3B;AAEA,IAAA,OAAO,KAAA;AAAA,EACT;AACF;;;;"}
1
+ {"version":3,"file":"OidcService.cjs.js","sources":["../../src/service/OidcService.ts"],"sourcesContent":["/*\n * Copyright 2025 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { AuthService, RootConfigService } from '@backstage/backend-plugin-api';\nimport { TokenIssuer } from '../identity/types';\nimport { UserInfoDatabase } from '../database/UserInfoDatabase';\nimport {\n AuthenticationError,\n InputError,\n NotFoundError,\n} from '@backstage/errors';\nimport { decodeJwt } from 'jose';\nimport crypto from 'crypto';\nimport { OidcDatabase } from '../database/OidcDatabase';\nimport { DateTime } from 'luxon';\nimport matcher from 'matcher';\n\nexport class OidcService {\n private readonly auth: AuthService;\n private readonly tokenIssuer: TokenIssuer;\n private readonly baseUrl: string;\n private readonly userInfo: UserInfoDatabase;\n private readonly oidc: OidcDatabase;\n private readonly config: RootConfigService;\n\n private constructor(\n auth: AuthService,\n tokenIssuer: TokenIssuer,\n baseUrl: string,\n userInfo: UserInfoDatabase,\n oidc: OidcDatabase,\n config: RootConfigService,\n ) {\n this.auth = auth;\n this.tokenIssuer = tokenIssuer;\n this.baseUrl = baseUrl;\n this.userInfo = userInfo;\n this.oidc = oidc;\n this.config = config;\n }\n\n static create(options: {\n auth: AuthService;\n tokenIssuer: TokenIssuer;\n baseUrl: string;\n userInfo: UserInfoDatabase;\n oidc: OidcDatabase;\n config: RootConfigService;\n }) {\n return new OidcService(\n options.auth,\n options.tokenIssuer,\n options.baseUrl,\n options.userInfo,\n options.oidc,\n options.config,\n );\n }\n\n public getConfiguration() {\n return {\n issuer: this.baseUrl,\n token_endpoint: `${this.baseUrl}/v1/token`,\n userinfo_endpoint: `${this.baseUrl}/v1/userinfo`,\n jwks_uri: `${this.baseUrl}/.well-known/jwks.json`,\n response_types_supported: ['code', 'id_token'],\n subject_types_supported: ['public'],\n id_token_signing_alg_values_supported: [\n 'RS256',\n 'RS384',\n 'RS512',\n 'ES256',\n 'ES384',\n 'ES512',\n 'PS256',\n 'PS384',\n 'PS512',\n 'EdDSA',\n ],\n scopes_supported: ['openid'],\n token_endpoint_auth_methods_supported: [\n 'client_secret_basic',\n 'client_secret_post',\n ],\n claims_supported: ['sub', 'ent'],\n grant_types_supported: ['authorization_code'],\n authorization_endpoint: `${this.baseUrl}/v1/authorize`,\n registration_endpoint: `${this.baseUrl}/v1/register`,\n code_challenge_methods_supported: ['S256', 'plain'],\n };\n }\n\n public async listPublicKeys() {\n return await this.tokenIssuer.listPublicKeys();\n }\n\n public async getUserInfo({ token }: { token: string }) {\n const credentials = await this.auth.authenticate(token, {\n allowLimitedAccess: true,\n });\n if (!this.auth.isPrincipal(credentials, 'user')) {\n throw new InputError(\n 'Userinfo endpoint must be called with a token that represents a user principal',\n );\n }\n\n const { sub: userEntityRef } = decodeJwt(token);\n\n if (typeof userEntityRef !== 'string') {\n throw new Error('Invalid user token, user entity ref must be a string');\n }\n return await this.userInfo.getUserInfo(userEntityRef);\n }\n\n public async registerClient(opts: {\n responseTypes?: string[];\n grantTypes?: string[];\n clientName: string;\n redirectUris?: string[];\n scope?: string;\n }) {\n const generatedClientId = crypto.randomUUID();\n const generatedClientSecret = crypto.randomUUID();\n\n const allowedRedirectUriPatterns = this.config.getOptionalStringArray(\n 'auth.experimentalDynamicClientRegistration.allowedRedirectUriPatterns',\n ) ?? ['*'];\n\n for (const redirectUri of opts.redirectUris ?? []) {\n if (\n !allowedRedirectUriPatterns.some(pattern =>\n matcher.isMatch(redirectUri, pattern),\n )\n ) {\n throw new InputError('Invalid redirect_uri');\n }\n }\n\n return await this.oidc.createClient({\n clientId: generatedClientId,\n clientName: opts.clientName,\n clientSecret: generatedClientSecret,\n redirectUris: opts.redirectUris ?? [],\n responseTypes: opts.responseTypes ?? ['code'],\n grantTypes: opts.grantTypes ?? ['authorization_code'],\n scope: opts.scope,\n });\n }\n\n public async createAuthorizationSession(opts: {\n clientId: string;\n redirectUri: string;\n responseType: string;\n scope?: string;\n state?: string;\n nonce?: string;\n codeChallenge?: string;\n codeChallengeMethod?: string;\n }) {\n const {\n clientId,\n redirectUri,\n responseType,\n scope,\n state,\n nonce,\n codeChallenge,\n codeChallengeMethod,\n } = opts;\n\n if (responseType !== 'code') {\n throw new InputError('Only authorization code flow is supported');\n }\n\n const client = await this.oidc.getClient({ clientId });\n if (!client) {\n throw new InputError('Invalid client_id');\n }\n\n if (!client.redirectUris.includes(redirectUri)) {\n throw new InputError('Invalid redirect_uri');\n }\n\n if (codeChallenge) {\n if (\n !codeChallengeMethod ||\n !['S256', 'plain'].includes(codeChallengeMethod)\n ) {\n throw new InputError('Invalid code_challenge_method');\n }\n }\n\n const sessionId = crypto.randomUUID();\n const sessionExpiresAt = DateTime.now().plus({ hours: 1 }).toJSDate();\n\n await this.oidc.createAuthorizationSession({\n id: sessionId,\n clientId,\n redirectUri,\n responseType,\n scope,\n state,\n codeChallenge,\n codeChallengeMethod,\n nonce,\n expiresAt: sessionExpiresAt,\n });\n\n return {\n id: sessionId,\n clientName: client.clientName,\n scope,\n redirectUri,\n };\n }\n\n public async approveAuthorizationSession(opts: {\n sessionId: string;\n userEntityRef: string;\n }) {\n const { sessionId, userEntityRef } = opts;\n\n const session = await this.oidc.getAuthorizationSession({\n id: sessionId,\n });\n\n if (!session) {\n throw new NotFoundError('Invalid authorization session');\n }\n\n if (DateTime.fromJSDate(session.expiresAt) < DateTime.now()) {\n throw new InputError('Authorization session expired');\n }\n\n if (session.status !== 'pending') {\n throw new NotFoundError('Authorization session not found or expired');\n }\n\n await this.oidc.updateAuthorizationSession({\n id: session.id,\n userEntityRef,\n status: 'approved',\n });\n\n const authorizationCode = crypto.randomBytes(32).toString('base64url');\n const codeExpiresAt = DateTime.now().plus({ minutes: 10 }).toJSDate();\n\n await this.oidc.createAuthorizationCode({\n code: authorizationCode,\n sessionId: session.id,\n expiresAt: codeExpiresAt,\n });\n\n const redirectUrl = new URL(session.redirectUri);\n\n redirectUrl.searchParams.append('code', authorizationCode);\n if (session.state) {\n redirectUrl.searchParams.append('state', session.state);\n }\n\n return {\n redirectUrl: redirectUrl.toString(),\n };\n }\n\n public async getAuthorizationSession(opts: { sessionId: string }) {\n const session = await this.oidc.getAuthorizationSession({\n id: opts.sessionId,\n });\n\n if (!session) {\n throw new NotFoundError('Invalid authorization session');\n }\n\n if (DateTime.fromJSDate(session.expiresAt) < DateTime.now()) {\n throw new InputError('Authorization session expired');\n }\n\n if (session.status !== 'pending') {\n throw new NotFoundError('Authorization session not found or expired');\n }\n\n const client = await this.oidc.getClient({ clientId: session.clientId });\n if (!client) {\n throw new InputError('Invalid client_id');\n }\n\n return {\n id: session.id,\n clientId: session.clientId,\n clientName: client.clientName,\n redirectUri: session.redirectUri,\n scope: session.scope,\n state: session.state,\n responseType: session.responseType,\n codeChallenge: session.codeChallenge,\n codeChallengeMethod: session.codeChallengeMethod,\n nonce: session.nonce,\n expiresAt: session.expiresAt,\n status: session.status,\n };\n }\n\n public async rejectAuthorizationSession(opts: {\n sessionId: string;\n userEntityRef: string;\n }) {\n const { sessionId, userEntityRef } = opts;\n\n const session = await this.oidc.getAuthorizationSession({\n id: sessionId,\n });\n\n if (!session) {\n throw new NotFoundError('Invalid authorization session');\n }\n\n if (DateTime.fromJSDate(session.expiresAt) < DateTime.now()) {\n throw new InputError('Authorization session expired');\n }\n\n if (session.status !== 'pending') {\n throw new NotFoundError('Authorization session not found or expired');\n }\n\n await this.oidc.updateAuthorizationSession({\n id: session.id,\n status: 'rejected',\n userEntityRef,\n });\n }\n\n public async exchangeCodeForToken(params: {\n code: string;\n redirectUri: string;\n codeVerifier?: string;\n grantType: string;\n expiresIn: number;\n }) {\n const { code, redirectUri, codeVerifier, grantType, expiresIn } = params;\n\n if (grantType !== 'authorization_code') {\n throw new InputError('Unsupported grant type');\n }\n\n const authCode = await this.oidc.getAuthorizationCode({ code });\n if (!authCode) {\n throw new AuthenticationError('Invalid authorization code');\n }\n\n if (DateTime.fromJSDate(authCode.expiresAt) < DateTime.now()) {\n throw new AuthenticationError('Authorization code expired');\n }\n\n if (authCode.used) {\n throw new AuthenticationError('Authorization code already used');\n }\n\n const session = await this.oidc.getAuthorizationSession({\n id: authCode.sessionId,\n });\n\n if (!session) {\n throw new NotFoundError('Invalid authorization session');\n }\n\n if (session.redirectUri !== redirectUri) {\n throw new AuthenticationError('Redirect URI mismatch');\n }\n\n if (session.status !== 'approved') {\n throw new AuthenticationError('Authorization not approved');\n }\n\n if (!session.userEntityRef) {\n throw new AuthenticationError('No user associated with authorization');\n }\n\n if (session.codeChallenge) {\n if (!codeVerifier) {\n throw new AuthenticationError('Code verifier required for PKCE');\n }\n\n if (\n !this.verifyPkce(\n session.codeChallenge,\n codeVerifier,\n session.codeChallengeMethod,\n )\n ) {\n throw new AuthenticationError('Invalid code verifier');\n }\n }\n\n await this.oidc.updateAuthorizationCode({\n code,\n used: true,\n });\n\n const { token } = await this.tokenIssuer.issueToken({\n claims: {\n sub: session.userEntityRef,\n },\n });\n\n return {\n accessToken: token,\n tokenType: 'Bearer',\n expiresIn: expiresIn,\n idToken: token,\n scope: session.scope || 'openid',\n };\n }\n\n private verifyPkce(\n codeChallenge: string,\n codeVerifier: string,\n method?: string,\n ): boolean {\n if (!method || method === 'plain') {\n return codeChallenge === codeVerifier;\n }\n\n if (method === 'S256') {\n const hash = crypto.createHash('sha256').update(codeVerifier).digest();\n const base64urlHash = hash.toString('base64url');\n return codeChallenge === base64urlHash;\n }\n\n return false;\n }\n}\n"],"names":["InputError","decodeJwt","crypto","matcher","DateTime","NotFoundError","AuthenticationError"],"mappings":"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"}
@@ -0,0 +1,44 @@
1
+ 'use strict';
2
+
3
+ var config = require('@backstage/config');
4
+ var types = require('@backstage/types');
5
+
6
+ const TOKEN_EXP_DEFAULT_S = 3600;
7
+ const TOKEN_EXP_MIN_S = 600;
8
+ const TOKEN_EXP_MAX_S = 86400;
9
+ function readBackstageTokenExpiration(config) {
10
+ return readTokenExpiration(config, {
11
+ configKey: "auth.backstageTokenExpiration"
12
+ });
13
+ }
14
+ function readDcrTokenExpiration(config) {
15
+ return readTokenExpiration(config, {
16
+ configKey: "auth.experimentalDynamicClientRegistration.tokenExpiration"
17
+ });
18
+ }
19
+ function readTokenExpiration(config$1, options) {
20
+ const {
21
+ configKey,
22
+ maxExpiration = TOKEN_EXP_MAX_S,
23
+ minExpiration = TOKEN_EXP_MIN_S,
24
+ defaultExpiration = TOKEN_EXP_DEFAULT_S
25
+ } = options ?? {};
26
+ if (!config$1.has(configKey)) {
27
+ return defaultExpiration;
28
+ }
29
+ const duration = config.readDurationFromConfig(config$1, {
30
+ key: configKey
31
+ });
32
+ const durationS = Math.round(types.durationToMilliseconds(duration) / 1e3);
33
+ if (durationS < minExpiration) {
34
+ return minExpiration;
35
+ } else if (durationS > maxExpiration) {
36
+ return maxExpiration;
37
+ }
38
+ return durationS;
39
+ }
40
+
41
+ exports.readBackstageTokenExpiration = readBackstageTokenExpiration;
42
+ exports.readDcrTokenExpiration = readDcrTokenExpiration;
43
+ exports.readTokenExpiration = readTokenExpiration;
44
+ //# sourceMappingURL=readTokenExpiration.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"readTokenExpiration.cjs.js","sources":["../../src/service/readTokenExpiration.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { RootConfigService } from '@backstage/backend-plugin-api';\nimport { readDurationFromConfig } from '@backstage/config';\nimport { durationToMilliseconds } from '@backstage/types';\n\nconst TOKEN_EXP_DEFAULT_S = 3600;\nconst TOKEN_EXP_MIN_S = 600;\nconst TOKEN_EXP_MAX_S = 86400;\n\nexport function readBackstageTokenExpiration(config: RootConfigService) {\n return readTokenExpiration(config, {\n configKey: 'auth.backstageTokenExpiration',\n });\n}\n\nexport function readDcrTokenExpiration(config: RootConfigService) {\n return readTokenExpiration(config, {\n configKey: 'auth.experimentalDynamicClientRegistration.tokenExpiration',\n });\n}\n\nexport function readTokenExpiration(\n config: RootConfigService,\n options: {\n configKey: string;\n maxExpiration?: number;\n minExpiration?: number;\n defaultExpiration?: number;\n },\n): number {\n const {\n configKey,\n maxExpiration = TOKEN_EXP_MAX_S,\n minExpiration = TOKEN_EXP_MIN_S,\n defaultExpiration = TOKEN_EXP_DEFAULT_S,\n } = options ?? {};\n if (!config.has(configKey)) {\n return defaultExpiration;\n }\n\n const duration = readDurationFromConfig(config, {\n key: configKey,\n });\n\n const durationS = Math.round(durationToMilliseconds(duration) / 1000);\n\n if (durationS < minExpiration) {\n return minExpiration;\n } else if (durationS > maxExpiration) {\n return maxExpiration;\n }\n return durationS;\n}\n"],"names":["config","readDurationFromConfig","durationToMilliseconds"],"mappings":";;;;;AAoBA,MAAM,mBAAA,GAAsB,IAAA;AAC5B,MAAM,eAAA,GAAkB,GAAA;AACxB,MAAM,eAAA,GAAkB,KAAA;AAEjB,SAAS,6BAA6B,MAAA,EAA2B;AACtE,EAAA,OAAO,oBAAoB,MAAA,EAAQ;AAAA,IACjC,SAAA,EAAW;AAAA,GACZ,CAAA;AACH;AAEO,SAAS,uBAAuB,MAAA,EAA2B;AAChE,EAAA,OAAO,oBAAoB,MAAA,EAAQ;AAAA,IACjC,SAAA,EAAW;AAAA,GACZ,CAAA;AACH;AAEO,SAAS,mBAAA,CACdA,UACA,OAAA,EAMQ;AACR,EAAA,MAAM;AAAA,IACJ,SAAA;AAAA,IACA,aAAA,GAAgB,eAAA;AAAA,IAChB,aAAA,GAAgB,eAAA;AAAA,IAChB,iBAAA,GAAoB;AAAA,GACtB,GAAI,WAAW,EAAC;AAChB,EAAA,IAAI,CAACA,QAAA,CAAO,GAAA,CAAI,SAAS,CAAA,EAAG;AAC1B,IAAA,OAAO,iBAAA;AAAA,EACT;AAEA,EAAA,MAAM,QAAA,GAAWC,8BAAuBD,QAAA,EAAQ;AAAA,IAC9C,GAAA,EAAK;AAAA,GACN,CAAA;AAED,EAAA,MAAM,YAAY,IAAA,CAAK,KAAA,CAAME,4BAAA,CAAuB,QAAQ,IAAI,GAAI,CAAA;AAEpE,EAAA,IAAI,YAAY,aAAA,EAAe;AAC7B,IAAA,OAAO,aAAA;AAAA,EACT,CAAA,MAAA,IAAW,YAAY,aAAA,EAAe;AACpC,IAAA,OAAO,aAAA;AAAA,EACT;AACA,EAAA,OAAO,SAAA;AACT;;;;;;"}
@@ -11,7 +11,7 @@ var session = require('express-session');
11
11
  var connectSessionKnex = require('connect-session-knex');
12
12
  var passport = require('passport');
13
13
  var AuthDatabase = require('../database/AuthDatabase.cjs.js');
14
- var readBackstageTokenExpiration = require('./readBackstageTokenExpiration.cjs.js');
14
+ var readTokenExpiration = require('./readTokenExpiration.cjs.js');
15
15
  var StaticTokenIssuer = require('../identity/StaticTokenIssuer.cjs.js');
16
16
  var StaticKeyStore = require('../identity/StaticKeyStore.cjs.js');
17
17
  var router = require('../providers/router.cjs.js');
@@ -40,7 +40,7 @@ async function createRouter(options) {
40
40
  const router$1 = Router__default.default();
41
41
  const appUrl = config.getString("app.baseUrl");
42
42
  const authUrl = await discovery.getExternalBaseUrl("auth");
43
- const backstageTokenExpiration = readBackstageTokenExpiration.readBackstageTokenExpiration(config);
43
+ const backstageTokenExpiration = readTokenExpiration.readBackstageTokenExpiration(config);
44
44
  const database = AuthDatabase.AuthDatabase.create(db);
45
45
  const keyStore = await KeyStores.KeyStores.fromConfig(config, {
46
46
  logger,
@@ -52,27 +52,31 @@ async function createRouter(options) {
52
52
  const omitClaimsFromToken = config.getOptionalBoolean(
53
53
  "auth.omitIdentityTokenOwnershipClaim"
54
54
  ) ? ["ent"] : [];
55
- let tokenIssuer;
56
- if (keyStore instanceof StaticKeyStore.StaticKeyStore) {
57
- tokenIssuer = new StaticTokenIssuer.StaticTokenIssuer(
58
- {
59
- logger: logger.child({ component: "token-factory" }),
60
- issuer: authUrl,
61
- sessionExpirationSeconds: backstageTokenExpiration,
62
- omitClaimsFromToken
63
- },
64
- keyStore
65
- );
66
- } else {
67
- tokenIssuer = new TokenFactory.TokenFactory({
55
+ const createTokenIssuer = (opts) => {
56
+ if (keyStore instanceof StaticKeyStore.StaticKeyStore) {
57
+ return new StaticTokenIssuer.StaticTokenIssuer(
58
+ {
59
+ logger: opts.logger,
60
+ issuer: authUrl,
61
+ sessionExpirationSeconds: opts.expirationSeconds,
62
+ omitClaimsFromToken
63
+ },
64
+ keyStore
65
+ );
66
+ }
67
+ return new TokenFactory.TokenFactory({
68
68
  issuer: authUrl,
69
69
  keyStore,
70
- keyDurationSeconds: backstageTokenExpiration,
71
- logger: logger.child({ component: "token-factory" }),
70
+ keyDurationSeconds: opts.expirationSeconds,
71
+ logger: opts.logger,
72
72
  algorithm: tokenFactoryAlgorithm ?? config.getOptionalString("auth.identityTokenAlgorithm"),
73
73
  omitClaimsFromToken
74
74
  });
75
- }
75
+ };
76
+ const tokenIssuer = createTokenIssuer({
77
+ logger: logger.child({ component: "token-factory" }),
78
+ expirationSeconds: backstageTokenExpiration
79
+ });
76
80
  const secret = config.getOptionalString("auth.session.secret");
77
81
  if (secret) {
78
82
  router$1.use(cookieParser__default.default(secret));
@@ -106,10 +110,15 @@ async function createRouter(options) {
106
110
  auth: options.auth,
107
111
  userInfo
108
112
  });
113
+ const dcrTokenExpiration = readTokenExpiration.readDcrTokenExpiration(config);
114
+ const oidcTokenIssuer = createTokenIssuer({
115
+ logger: logger.child({ component: "oidc-token-factory" }),
116
+ expirationSeconds: dcrTokenExpiration
117
+ });
109
118
  const oidc = await OidcDatabase.OidcDatabase.create({ database });
110
119
  const oidcRouter = OidcRouter.OidcRouter.create({
111
120
  auth: options.auth,
112
- tokenIssuer,
121
+ tokenIssuer: oidcTokenIssuer,
113
122
  baseUrl: authUrl,
114
123
  appUrl,
115
124
  userInfo,
@@ -1 +1 @@
1
- {"version":3,"file":"router.cjs.js","sources":["../../src/service/router.ts"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport express from 'express';\nimport Router from 'express-promise-router';\nimport cookieParser from 'cookie-parser';\nimport {\n AuthService,\n DatabaseService,\n DiscoveryService,\n HttpAuthService,\n LoggerService,\n RootConfigService,\n} from '@backstage/backend-plugin-api';\nimport { AuthOwnershipResolver } from '@backstage/plugin-auth-node';\nimport { CatalogService } from '@backstage/plugin-catalog-node';\nimport { NotFoundError } from '@backstage/errors';\nimport { KeyStores } from '../identity/KeyStores';\nimport { TokenFactory } from '../identity/TokenFactory';\nimport { UserInfoDatabase } from '../database/UserInfoDatabase';\nimport session from 'express-session';\nimport connectSessionKnex from 'connect-session-knex';\nimport passport from 'passport';\nimport { AuthDatabase } from '../database/AuthDatabase';\nimport { readBackstageTokenExpiration } from './readBackstageTokenExpiration';\nimport { TokenIssuer } from '../identity/types';\nimport { StaticTokenIssuer } from '../identity/StaticTokenIssuer';\nimport { StaticKeyStore } from '../identity/StaticKeyStore';\nimport { bindProviderRouters, ProviderFactories } from '../providers/router';\nimport { OidcRouter } from './OidcRouter';\nimport { OidcDatabase } from '../database/OidcDatabase';\n\ninterface RouterOptions {\n logger: LoggerService;\n database: DatabaseService;\n config: RootConfigService;\n discovery: DiscoveryService;\n auth: AuthService;\n tokenFactoryAlgorithm?: string;\n providerFactories?: ProviderFactories;\n catalog: CatalogService;\n ownershipResolver?: AuthOwnershipResolver;\n httpAuth: HttpAuthService;\n}\n\nexport async function createRouter(\n options: RouterOptions,\n): Promise<express.Router> {\n const {\n logger,\n config,\n discovery,\n database: db,\n tokenFactoryAlgorithm,\n providerFactories = {},\n httpAuth,\n } = options;\n\n const router = Router();\n\n const appUrl = config.getString('app.baseUrl');\n const authUrl = await discovery.getExternalBaseUrl('auth');\n const backstageTokenExpiration = readBackstageTokenExpiration(config);\n const database = AuthDatabase.create(db);\n\n const keyStore = await KeyStores.fromConfig(config, {\n logger,\n database,\n });\n\n const userInfo = await UserInfoDatabase.create({\n database,\n });\n\n const omitClaimsFromToken = config.getOptionalBoolean(\n 'auth.omitIdentityTokenOwnershipClaim',\n )\n ? ['ent']\n : [];\n\n let tokenIssuer: TokenIssuer;\n if (keyStore instanceof StaticKeyStore) {\n tokenIssuer = new StaticTokenIssuer(\n {\n logger: logger.child({ component: 'token-factory' }),\n issuer: authUrl,\n sessionExpirationSeconds: backstageTokenExpiration,\n omitClaimsFromToken,\n },\n keyStore as StaticKeyStore,\n );\n } else {\n tokenIssuer = new TokenFactory({\n issuer: authUrl,\n keyStore,\n keyDurationSeconds: backstageTokenExpiration,\n logger: logger.child({ component: 'token-factory' }),\n algorithm:\n tokenFactoryAlgorithm ??\n config.getOptionalString('auth.identityTokenAlgorithm'),\n omitClaimsFromToken,\n });\n }\n\n const secret = config.getOptionalString('auth.session.secret');\n if (secret) {\n router.use(cookieParser(secret));\n const enforceCookieSSL = authUrl.startsWith('https');\n const KnexSessionStore = connectSessionKnex(session);\n router.use(\n session({\n secret,\n saveUninitialized: false,\n resave: false,\n cookie: { secure: enforceCookieSSL ? 'auto' : false },\n store: new KnexSessionStore({\n createtable: false,\n knex: await database.get(),\n }),\n }),\n );\n router.use(passport.initialize());\n router.use(passport.session());\n } else {\n router.use(cookieParser());\n }\n\n router.use(express.urlencoded({ extended: false }));\n router.use(express.json());\n\n bindProviderRouters(router, {\n providers: providerFactories,\n appUrl,\n baseUrl: authUrl,\n tokenIssuer,\n ...options,\n auth: options.auth,\n userInfo,\n });\n\n const oidc = await OidcDatabase.create({ database });\n\n const oidcRouter = OidcRouter.create({\n auth: options.auth,\n tokenIssuer,\n baseUrl: authUrl,\n appUrl,\n userInfo,\n oidc,\n logger,\n httpAuth,\n config,\n });\n\n router.use(oidcRouter.getRouter());\n\n // Gives a more helpful error message than a plain 404\n router.use('/:provider/', req => {\n const { provider } = req.params;\n throw new NotFoundError(`Unknown auth provider '${provider}'`);\n });\n\n return router;\n}\n"],"names":["router","Router","readBackstageTokenExpiration","AuthDatabase","KeyStores","UserInfoDatabase","StaticKeyStore","StaticTokenIssuer","TokenFactory","cookieParser","connectSessionKnex","session","passport","express","bindProviderRouters","OidcDatabase","OidcRouter","NotFoundError"],"mappings":"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"}
1
+ {"version":3,"file":"router.cjs.js","sources":["../../src/service/router.ts"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport express from 'express';\nimport Router from 'express-promise-router';\nimport cookieParser from 'cookie-parser';\nimport {\n AuthService,\n DatabaseService,\n DiscoveryService,\n HttpAuthService,\n LoggerService,\n RootConfigService,\n} from '@backstage/backend-plugin-api';\nimport { AuthOwnershipResolver } from '@backstage/plugin-auth-node';\nimport { CatalogService } from '@backstage/plugin-catalog-node';\nimport { NotFoundError } from '@backstage/errors';\nimport { KeyStores } from '../identity/KeyStores';\nimport { TokenFactory } from '../identity/TokenFactory';\nimport { UserInfoDatabase } from '../database/UserInfoDatabase';\nimport session from 'express-session';\nimport connectSessionKnex from 'connect-session-knex';\nimport passport from 'passport';\nimport { AuthDatabase } from '../database/AuthDatabase';\nimport {\n readBackstageTokenExpiration,\n readDcrTokenExpiration,\n} from './readTokenExpiration.ts';\nimport { StaticTokenIssuer } from '../identity/StaticTokenIssuer';\nimport { StaticKeyStore } from '../identity/StaticKeyStore';\nimport { bindProviderRouters, ProviderFactories } from '../providers/router';\nimport { OidcRouter } from './OidcRouter';\nimport { OidcDatabase } from '../database/OidcDatabase';\n\ninterface RouterOptions {\n logger: LoggerService;\n database: DatabaseService;\n config: RootConfigService;\n discovery: DiscoveryService;\n auth: AuthService;\n tokenFactoryAlgorithm?: string;\n providerFactories?: ProviderFactories;\n catalog: CatalogService;\n ownershipResolver?: AuthOwnershipResolver;\n httpAuth: HttpAuthService;\n}\n\nexport async function createRouter(\n options: RouterOptions,\n): Promise<express.Router> {\n const {\n logger,\n config,\n discovery,\n database: db,\n tokenFactoryAlgorithm,\n providerFactories = {},\n httpAuth,\n } = options;\n\n const router = Router();\n\n const appUrl = config.getString('app.baseUrl');\n const authUrl = await discovery.getExternalBaseUrl('auth');\n const backstageTokenExpiration = readBackstageTokenExpiration(config);\n const database = AuthDatabase.create(db);\n\n const keyStore = await KeyStores.fromConfig(config, {\n logger,\n database,\n });\n\n const userInfo = await UserInfoDatabase.create({\n database,\n });\n\n const omitClaimsFromToken = config.getOptionalBoolean(\n 'auth.omitIdentityTokenOwnershipClaim',\n )\n ? ['ent']\n : [];\n\n const createTokenIssuer = (opts: {\n logger: LoggerService;\n expirationSeconds: number;\n }) => {\n if (keyStore instanceof StaticKeyStore) {\n return new StaticTokenIssuer(\n {\n logger: opts.logger,\n issuer: authUrl,\n sessionExpirationSeconds: opts.expirationSeconds,\n omitClaimsFromToken,\n },\n keyStore as StaticKeyStore,\n );\n }\n return new TokenFactory({\n issuer: authUrl,\n keyStore,\n keyDurationSeconds: opts.expirationSeconds,\n logger: opts.logger,\n algorithm:\n tokenFactoryAlgorithm ??\n config.getOptionalString('auth.identityTokenAlgorithm'),\n omitClaimsFromToken,\n });\n };\n\n const tokenIssuer = createTokenIssuer({\n logger: logger.child({ component: 'token-factory' }),\n expirationSeconds: backstageTokenExpiration,\n });\n\n const secret = config.getOptionalString('auth.session.secret');\n if (secret) {\n router.use(cookieParser(secret));\n const enforceCookieSSL = authUrl.startsWith('https');\n const KnexSessionStore = connectSessionKnex(session);\n router.use(\n session({\n secret,\n saveUninitialized: false,\n resave: false,\n cookie: { secure: enforceCookieSSL ? 'auto' : false },\n store: new KnexSessionStore({\n createtable: false,\n knex: await database.get(),\n }),\n }),\n );\n router.use(passport.initialize());\n router.use(passport.session());\n } else {\n router.use(cookieParser());\n }\n\n router.use(express.urlencoded({ extended: false }));\n router.use(express.json());\n\n bindProviderRouters(router, {\n providers: providerFactories,\n appUrl,\n baseUrl: authUrl,\n tokenIssuer,\n ...options,\n auth: options.auth,\n userInfo,\n });\n\n const dcrTokenExpiration = readDcrTokenExpiration(config);\n\n const oidcTokenIssuer = createTokenIssuer({\n logger: logger.child({ component: 'oidc-token-factory' }),\n expirationSeconds: dcrTokenExpiration,\n });\n\n const oidc = await OidcDatabase.create({ database });\n\n const oidcRouter = OidcRouter.create({\n auth: options.auth,\n tokenIssuer: oidcTokenIssuer,\n baseUrl: authUrl,\n appUrl,\n userInfo,\n oidc,\n logger,\n httpAuth,\n config,\n });\n\n router.use(oidcRouter.getRouter());\n\n // Gives a more helpful error message than a plain 404\n router.use('/:provider/', req => {\n const { provider } = req.params;\n throw new NotFoundError(`Unknown auth provider '${provider}'`);\n });\n\n return router;\n}\n"],"names":["router","Router","readBackstageTokenExpiration","AuthDatabase","KeyStores","UserInfoDatabase","StaticKeyStore","StaticTokenIssuer","TokenFactory","cookieParser","connectSessionKnex","session","passport","express","bindProviderRouters","readDcrTokenExpiration","OidcDatabase","OidcRouter","NotFoundError"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@backstage/plugin-auth-backend",
3
- "version": "0.25.5",
3
+ "version": "0.25.6-next.1",
4
4
  "description": "A Backstage backend plugin that handles authentication",
5
5
  "backstage": {
6
6
  "role": "backend-plugin",
@@ -47,13 +47,13 @@
47
47
  "test": "backstage-cli package test"
48
48
  },
49
49
  "dependencies": {
50
- "@backstage/backend-plugin-api": "^1.4.4",
51
- "@backstage/catalog-model": "^1.7.5",
52
- "@backstage/config": "^1.3.5",
53
- "@backstage/errors": "^1.2.7",
54
- "@backstage/plugin-auth-node": "^0.6.8",
55
- "@backstage/plugin-catalog-node": "^1.19.1",
56
- "@backstage/types": "^1.2.2",
50
+ "@backstage/backend-plugin-api": "1.5.0-next.1",
51
+ "@backstage/catalog-model": "1.7.6-next.0",
52
+ "@backstage/config": "1.3.6-next.0",
53
+ "@backstage/errors": "1.2.7",
54
+ "@backstage/plugin-auth-node": "0.6.9-next.1",
55
+ "@backstage/plugin-catalog-node": "1.20.0-next.1",
56
+ "@backstage/types": "1.2.2",
57
57
  "@google-cloud/firestore": "^7.0.0",
58
58
  "connect-session-knex": "^4.0.0",
59
59
  "cookie-parser": "^1.4.5",
@@ -70,11 +70,11 @@
70
70
  "uuid": "^11.0.0"
71
71
  },
72
72
  "devDependencies": {
73
- "@backstage/backend-defaults": "^0.13.0",
74
- "@backstage/backend-test-utils": "^1.9.1",
75
- "@backstage/cli": "^0.34.4",
76
- "@backstage/plugin-auth-backend-module-google-provider": "^0.3.8",
77
- "@backstage/plugin-auth-backend-module-guest-provider": "^0.2.13",
73
+ "@backstage/backend-defaults": "0.13.1-next.1",
74
+ "@backstage/backend-test-utils": "1.10.0-next.1",
75
+ "@backstage/cli": "0.34.5-next.1",
76
+ "@backstage/plugin-auth-backend-module-google-provider": "0.3.9-next.1",
77
+ "@backstage/plugin-auth-backend-module-guest-provider": "0.2.14-next.1",
78
78
  "@types/cookie-parser": "^1.4.2",
79
79
  "@types/express": "^4.17.6",
80
80
  "@types/express-session": "^1.17.2",
@@ -1,27 +0,0 @@
1
- 'use strict';
2
-
3
- var config = require('@backstage/config');
4
- var types = require('@backstage/types');
5
-
6
- const TOKEN_EXP_DEFAULT_S = 3600;
7
- const TOKEN_EXP_MIN_S = 600;
8
- const TOKEN_EXP_MAX_S = 86400;
9
- function readBackstageTokenExpiration(config$1) {
10
- const processingIntervalKey = "auth.backstageTokenExpiration";
11
- if (!config$1.has(processingIntervalKey)) {
12
- return TOKEN_EXP_DEFAULT_S;
13
- }
14
- const duration = config.readDurationFromConfig(config$1, {
15
- key: processingIntervalKey
16
- });
17
- const durationS = Math.round(types.durationToMilliseconds(duration) / 1e3);
18
- if (durationS < TOKEN_EXP_MIN_S) {
19
- return TOKEN_EXP_MIN_S;
20
- } else if (durationS > TOKEN_EXP_MAX_S) {
21
- return TOKEN_EXP_MAX_S;
22
- }
23
- return durationS;
24
- }
25
-
26
- exports.readBackstageTokenExpiration = readBackstageTokenExpiration;
27
- //# sourceMappingURL=readBackstageTokenExpiration.cjs.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"readBackstageTokenExpiration.cjs.js","sources":["../../src/service/readBackstageTokenExpiration.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { RootConfigService } from '@backstage/backend-plugin-api';\nimport { readDurationFromConfig } from '@backstage/config';\nimport { durationToMilliseconds } from '@backstage/types';\n\nconst TOKEN_EXP_DEFAULT_S = 3600;\nconst TOKEN_EXP_MIN_S = 600;\nconst TOKEN_EXP_MAX_S = 86400;\n\nexport function readBackstageTokenExpiration(config: RootConfigService) {\n const processingIntervalKey = 'auth.backstageTokenExpiration';\n\n if (!config.has(processingIntervalKey)) {\n return TOKEN_EXP_DEFAULT_S;\n }\n\n const duration = readDurationFromConfig(config, {\n key: processingIntervalKey,\n });\n\n const durationS = Math.round(durationToMilliseconds(duration) / 1000);\n\n if (durationS < TOKEN_EXP_MIN_S) {\n return TOKEN_EXP_MIN_S;\n } else if (durationS > TOKEN_EXP_MAX_S) {\n return TOKEN_EXP_MAX_S;\n }\n return durationS;\n}\n"],"names":["config","readDurationFromConfig","durationToMilliseconds"],"mappings":";;;;;AAoBA,MAAM,mBAAA,GAAsB,IAAA;AAC5B,MAAM,eAAA,GAAkB,GAAA;AACxB,MAAM,eAAA,GAAkB,KAAA;AAEjB,SAAS,6BAA6BA,QAAA,EAA2B;AACtE,EAAA,MAAM,qBAAA,GAAwB,+BAAA;AAE9B,EAAA,IAAI,CAACA,QAAA,CAAO,GAAA,CAAI,qBAAqB,CAAA,EAAG;AACtC,IAAA,OAAO,mBAAA;AAAA,EACT;AAEA,EAAA,MAAM,QAAA,GAAWC,8BAAuBD,QAAA,EAAQ;AAAA,IAC9C,GAAA,EAAK;AAAA,GACN,CAAA;AAED,EAAA,MAAM,YAAY,IAAA,CAAK,KAAA,CAAME,4BAAA,CAAuB,QAAQ,IAAI,GAAI,CAAA;AAEpE,EAAA,IAAI,YAAY,eAAA,EAAiB;AAC/B,IAAA,OAAO,eAAA;AAAA,EACT,CAAA,MAAA,IAAW,YAAY,eAAA,EAAiB;AACtC,IAAA,OAAO,eAAA;AAAA,EACT;AACA,EAAA,OAAO,SAAA;AACT;;;;"}