@backstage/plugin-auth-backend 0.25.4-next.0 → 0.25.4

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,27 @@
1
1
  # @backstage/plugin-auth-backend
2
2
 
3
+ ## 0.25.4
4
+
5
+ ### Patch Changes
6
+
7
+ - 1d47bf3: Implementing Dynamic Client Registration with the OIDC server. You can enable this by setting `auth.experimentalDynamicClientRegistration.enabled` in `app-config.yaml`. This is highly experimental, but feedback welcome.
8
+ - 54ddfef: Updating plugin metadata
9
+ - Updated dependencies
10
+ - @backstage/plugin-catalog-node@1.19.0
11
+ - @backstage/plugin-auth-node@0.6.7
12
+ - @backstage/types@1.2.2
13
+ - @backstage/backend-plugin-api@1.4.3
14
+
15
+ ## 0.25.4-next.1
16
+
17
+ ### Patch Changes
18
+
19
+ - 1d47bf3: Implementing Dynamic Client Registration with the OIDC server. You can enable this by setting `auth.experimentalDynamicClientRegistration.enabled` in `app-config.yaml`. This is highly experimental, but feedback welcome.
20
+ - 54ddfef: Updating plugin metadata
21
+ - Updated dependencies
22
+ - @backstage/plugin-auth-node@0.6.7-next.1
23
+ - @backstage/plugin-catalog-node@1.19.0-next.1
24
+
3
25
  ## 0.25.4-next.0
4
26
 
5
27
  ### Patch Changes
@@ -36,6 +36,7 @@ const authPlugin = backendPluginApi.createBackendPlugin({
36
36
  database: backendPluginApi.coreServices.database,
37
37
  discovery: backendPluginApi.coreServices.discovery,
38
38
  auth: backendPluginApi.coreServices.auth,
39
+ httpAuth: backendPluginApi.coreServices.httpAuth,
39
40
  catalog: pluginCatalogNode.catalogServiceRef
40
41
  },
41
42
  async init({
@@ -45,6 +46,7 @@ const authPlugin = backendPluginApi.createBackendPlugin({
45
46
  database,
46
47
  discovery,
47
48
  auth,
49
+ httpAuth,
48
50
  catalog
49
51
  }) {
50
52
  const router$1 = await router.createRouter({
@@ -55,7 +57,8 @@ const authPlugin = backendPluginApi.createBackendPlugin({
55
57
  auth,
56
58
  catalog,
57
59
  providerFactories: Object.fromEntries(providers),
58
- ownershipResolver
60
+ ownershipResolver,
61
+ httpAuth
59
62
  });
60
63
  httpRouter.addAuthPolicy({
61
64
  path: "/",
@@ -1 +1 @@
1
- {"version":3,"file":"authPlugin.cjs.js","sources":["../src/authPlugin.ts"],"sourcesContent":["/*\n * Copyright 2023 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 coreServices,\n createBackendPlugin,\n} from '@backstage/backend-plugin-api';\nimport {\n authOwnershipResolutionExtensionPoint,\n AuthOwnershipResolver,\n AuthProviderFactory,\n authProvidersExtensionPoint,\n} from '@backstage/plugin-auth-node';\nimport { catalogServiceRef } from '@backstage/plugin-catalog-node';\nimport { createRouter } from './service/router';\n\n/**\n * Auth plugin\n *\n * @public\n */\nexport const authPlugin = createBackendPlugin({\n pluginId: 'auth',\n register(reg) {\n const providers = new Map<string, AuthProviderFactory>();\n let ownershipResolver: AuthOwnershipResolver | undefined = undefined;\n\n reg.registerExtensionPoint(authProvidersExtensionPoint, {\n registerProvider({ providerId, factory }) {\n if (providers.has(providerId)) {\n throw new Error(\n `Auth provider '${providerId}' was already registered`,\n );\n }\n providers.set(providerId, factory);\n },\n });\n\n reg.registerExtensionPoint(authOwnershipResolutionExtensionPoint, {\n setAuthOwnershipResolver(resolver) {\n if (ownershipResolver) {\n throw new Error('Auth ownership resolver is already set');\n }\n ownershipResolver = resolver;\n },\n });\n\n reg.registerInit({\n deps: {\n httpRouter: coreServices.httpRouter,\n logger: coreServices.logger,\n config: coreServices.rootConfig,\n database: coreServices.database,\n discovery: coreServices.discovery,\n auth: coreServices.auth,\n catalog: catalogServiceRef,\n },\n async init({\n httpRouter,\n logger,\n config,\n database,\n discovery,\n auth,\n catalog,\n }) {\n const router = await createRouter({\n logger,\n config,\n database,\n discovery,\n auth,\n catalog,\n providerFactories: Object.fromEntries(providers),\n ownershipResolver,\n });\n httpRouter.addAuthPolicy({\n path: '/',\n allow: 'unauthenticated',\n });\n httpRouter.use(router);\n },\n });\n },\n});\n"],"names":["createBackendPlugin","authProvidersExtensionPoint","authOwnershipResolutionExtensionPoint","coreServices","catalogServiceRef","router","createRouter"],"mappings":";;;;;;;AAkCO,MAAM,aAAaA,oCAAA,CAAoB;AAAA,EAC5C,QAAA,EAAU,MAAA;AAAA,EACV,SAAS,GAAA,EAAK;AACZ,IAAA,MAAM,SAAA,uBAAgB,GAAA,EAAiC;AACvD,IAAA,IAAI,iBAAA,GAAuD,MAAA;AAE3D,IAAA,GAAA,CAAI,uBAAuBC,0CAAA,EAA6B;AAAA,MACtD,gBAAA,CAAiB,EAAE,UAAA,EAAY,OAAA,EAAQ,EAAG;AACxC,QAAA,IAAI,SAAA,CAAU,GAAA,CAAI,UAAU,CAAA,EAAG;AAC7B,UAAA,MAAM,IAAI,KAAA;AAAA,YACR,kBAAkB,UAAU,CAAA,wBAAA;AAAA,WAC9B;AAAA,QACF;AACA,QAAA,SAAA,CAAU,GAAA,CAAI,YAAY,OAAO,CAAA;AAAA,MACnC;AAAA,KACD,CAAA;AAED,IAAA,GAAA,CAAI,uBAAuBC,oDAAA,EAAuC;AAAA,MAChE,yBAAyB,QAAA,EAAU;AACjC,QAAA,IAAI,iBAAA,EAAmB;AACrB,UAAA,MAAM,IAAI,MAAM,wCAAwC,CAAA;AAAA,QAC1D;AACA,QAAA,iBAAA,GAAoB,QAAA;AAAA,MACtB;AAAA,KACD,CAAA;AAED,IAAA,GAAA,CAAI,YAAA,CAAa;AAAA,MACf,IAAA,EAAM;AAAA,QACJ,YAAYC,6BAAA,CAAa,UAAA;AAAA,QACzB,QAAQA,6BAAA,CAAa,MAAA;AAAA,QACrB,QAAQA,6BAAA,CAAa,UAAA;AAAA,QACrB,UAAUA,6BAAA,CAAa,QAAA;AAAA,QACvB,WAAWA,6BAAA,CAAa,SAAA;AAAA,QACxB,MAAMA,6BAAA,CAAa,IAAA;AAAA,QACnB,OAAA,EAASC;AAAA,OACX;AAAA,MACA,MAAM,IAAA,CAAK;AAAA,QACT,UAAA;AAAA,QACA,MAAA;AAAA,QACA,MAAA;AAAA,QACA,QAAA;AAAA,QACA,SAAA;AAAA,QACA,IAAA;AAAA,QACA;AAAA,OACF,EAAG;AACD,QAAA,MAAMC,QAAA,GAAS,MAAMC,mBAAA,CAAa;AAAA,UAChC,MAAA;AAAA,UACA,MAAA;AAAA,UACA,QAAA;AAAA,UACA,SAAA;AAAA,UACA,IAAA;AAAA,UACA,OAAA;AAAA,UACA,iBAAA,EAAmB,MAAA,CAAO,WAAA,CAAY,SAAS,CAAA;AAAA,UAC/C;AAAA,SACD,CAAA;AACD,QAAA,UAAA,CAAW,aAAA,CAAc;AAAA,UACvB,IAAA,EAAM,GAAA;AAAA,UACN,KAAA,EAAO;AAAA,SACR,CAAA;AACD,QAAA,UAAA,CAAW,IAAID,QAAM,CAAA;AAAA,MACvB;AAAA,KACD,CAAA;AAAA,EACH;AACF,CAAC;;;;"}
1
+ {"version":3,"file":"authPlugin.cjs.js","sources":["../src/authPlugin.ts"],"sourcesContent":["/*\n * Copyright 2023 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 coreServices,\n createBackendPlugin,\n} from '@backstage/backend-plugin-api';\nimport {\n authOwnershipResolutionExtensionPoint,\n AuthOwnershipResolver,\n AuthProviderFactory,\n authProvidersExtensionPoint,\n} from '@backstage/plugin-auth-node';\nimport { catalogServiceRef } from '@backstage/plugin-catalog-node';\nimport { createRouter } from './service/router';\n\n/**\n * Auth plugin\n *\n * @public\n */\nexport const authPlugin = createBackendPlugin({\n pluginId: 'auth',\n register(reg) {\n const providers = new Map<string, AuthProviderFactory>();\n let ownershipResolver: AuthOwnershipResolver | undefined = undefined;\n\n reg.registerExtensionPoint(authProvidersExtensionPoint, {\n registerProvider({ providerId, factory }) {\n if (providers.has(providerId)) {\n throw new Error(\n `Auth provider '${providerId}' was already registered`,\n );\n }\n providers.set(providerId, factory);\n },\n });\n\n reg.registerExtensionPoint(authOwnershipResolutionExtensionPoint, {\n setAuthOwnershipResolver(resolver) {\n if (ownershipResolver) {\n throw new Error('Auth ownership resolver is already set');\n }\n ownershipResolver = resolver;\n },\n });\n\n reg.registerInit({\n deps: {\n httpRouter: coreServices.httpRouter,\n logger: coreServices.logger,\n config: coreServices.rootConfig,\n database: coreServices.database,\n discovery: coreServices.discovery,\n auth: coreServices.auth,\n httpAuth: coreServices.httpAuth,\n catalog: catalogServiceRef,\n },\n async init({\n httpRouter,\n logger,\n config,\n database,\n discovery,\n auth,\n httpAuth,\n catalog,\n }) {\n const router = await createRouter({\n logger,\n config,\n database,\n discovery,\n auth,\n catalog,\n providerFactories: Object.fromEntries(providers),\n ownershipResolver,\n httpAuth,\n });\n httpRouter.addAuthPolicy({\n path: '/',\n allow: 'unauthenticated',\n });\n httpRouter.use(router);\n },\n });\n },\n});\n"],"names":["createBackendPlugin","authProvidersExtensionPoint","authOwnershipResolutionExtensionPoint","coreServices","catalogServiceRef","router","createRouter"],"mappings":";;;;;;;AAkCO,MAAM,aAAaA,oCAAA,CAAoB;AAAA,EAC5C,QAAA,EAAU,MAAA;AAAA,EACV,SAAS,GAAA,EAAK;AACZ,IAAA,MAAM,SAAA,uBAAgB,GAAA,EAAiC;AACvD,IAAA,IAAI,iBAAA,GAAuD,MAAA;AAE3D,IAAA,GAAA,CAAI,uBAAuBC,0CAAA,EAA6B;AAAA,MACtD,gBAAA,CAAiB,EAAE,UAAA,EAAY,OAAA,EAAQ,EAAG;AACxC,QAAA,IAAI,SAAA,CAAU,GAAA,CAAI,UAAU,CAAA,EAAG;AAC7B,UAAA,MAAM,IAAI,KAAA;AAAA,YACR,kBAAkB,UAAU,CAAA,wBAAA;AAAA,WAC9B;AAAA,QACF;AACA,QAAA,SAAA,CAAU,GAAA,CAAI,YAAY,OAAO,CAAA;AAAA,MACnC;AAAA,KACD,CAAA;AAED,IAAA,GAAA,CAAI,uBAAuBC,oDAAA,EAAuC;AAAA,MAChE,yBAAyB,QAAA,EAAU;AACjC,QAAA,IAAI,iBAAA,EAAmB;AACrB,UAAA,MAAM,IAAI,MAAM,wCAAwC,CAAA;AAAA,QAC1D;AACA,QAAA,iBAAA,GAAoB,QAAA;AAAA,MACtB;AAAA,KACD,CAAA;AAED,IAAA,GAAA,CAAI,YAAA,CAAa;AAAA,MACf,IAAA,EAAM;AAAA,QACJ,YAAYC,6BAAA,CAAa,UAAA;AAAA,QACzB,QAAQA,6BAAA,CAAa,MAAA;AAAA,QACrB,QAAQA,6BAAA,CAAa,UAAA;AAAA,QACrB,UAAUA,6BAAA,CAAa,QAAA;AAAA,QACvB,WAAWA,6BAAA,CAAa,SAAA;AAAA,QACxB,MAAMA,6BAAA,CAAa,IAAA;AAAA,QACnB,UAAUA,6BAAA,CAAa,QAAA;AAAA,QACvB,OAAA,EAASC;AAAA,OACX;AAAA,MACA,MAAM,IAAA,CAAK;AAAA,QACT,UAAA;AAAA,QACA,MAAA;AAAA,QACA,MAAA;AAAA,QACA,QAAA;AAAA,QACA,SAAA;AAAA,QACA,IAAA;AAAA,QACA,QAAA;AAAA,QACA;AAAA,OACF,EAAG;AACD,QAAA,MAAMC,QAAA,GAAS,MAAMC,mBAAA,CAAa;AAAA,UAChC,MAAA;AAAA,UACA,MAAA;AAAA,UACA,QAAA;AAAA,UACA,SAAA;AAAA,UACA,IAAA;AAAA,UACA,OAAA;AAAA,UACA,iBAAA,EAAmB,MAAA,CAAO,WAAA,CAAY,SAAS,CAAA;AAAA,UAC/C,iBAAA;AAAA,UACA;AAAA,SACD,CAAA;AACD,QAAA,UAAA,CAAW,aAAA,CAAc;AAAA,UACvB,IAAA,EAAM,GAAA;AAAA,UACN,KAAA,EAAO;AAAA,SACR,CAAA;AACD,QAAA,UAAA,CAAW,IAAID,QAAM,CAAA;AAAA,MACvB;AAAA,KACD,CAAA;AAAA,EACH;AACF,CAAC;;;;"}
@@ -0,0 +1,214 @@
1
+ 'use strict';
2
+
3
+ function toDate(value) {
4
+ if (!value) {
5
+ return void 0;
6
+ }
7
+ return typeof value === "string" || typeof value === "number" ? new Date(value) : value;
8
+ }
9
+ class OidcDatabase {
10
+ constructor(db) {
11
+ this.db = db;
12
+ }
13
+ static async create(options) {
14
+ const client = await options.database.get();
15
+ return new OidcDatabase(client);
16
+ }
17
+ async createClient(client) {
18
+ await this.db("oidc_clients").insert({
19
+ client_id: client.clientId,
20
+ client_secret: client.clientSecret,
21
+ client_name: client.clientName,
22
+ response_types: JSON.stringify(client.responseTypes),
23
+ grant_types: JSON.stringify(client.grantTypes),
24
+ redirect_uris: JSON.stringify(client.redirectUris),
25
+ scope: client.scope,
26
+ metadata: JSON.stringify(client.metadata)
27
+ });
28
+ return client;
29
+ }
30
+ async getClient({ clientId }) {
31
+ const client = await this.db("oidc_clients").where("client_id", clientId).first();
32
+ if (!client) {
33
+ return null;
34
+ }
35
+ return this.rowToClient(client);
36
+ }
37
+ async createAuthorizationSession(session) {
38
+ await this.db(
39
+ "oauth_authorization_sessions"
40
+ ).insert({
41
+ id: session.id,
42
+ client_id: session.clientId,
43
+ user_entity_ref: session.userEntityRef,
44
+ redirect_uri: session.redirectUri,
45
+ scope: session.scope,
46
+ state: session.state,
47
+ response_type: session.responseType,
48
+ code_challenge: session.codeChallenge,
49
+ code_challenge_method: session.codeChallengeMethod,
50
+ nonce: session.nonce,
51
+ status: "pending",
52
+ expires_at: session.expiresAt
53
+ });
54
+ return {
55
+ ...session,
56
+ status: "pending"
57
+ };
58
+ }
59
+ async updateAuthorizationSession(session) {
60
+ const row = this.authorizationSessionToRow(session);
61
+ const updatedFields = Object.fromEntries(
62
+ Object.entries(row).filter(([_, value]) => value !== void 0)
63
+ );
64
+ if (this.db.client.config.client.includes("sqlite3") || this.db.client.config.client.includes("mysql")) {
65
+ return await this.db.transaction(async (trx) => {
66
+ await trx("oauth_authorization_sessions").where("id", session.id).update(updatedFields);
67
+ const updated = await trx(
68
+ "oauth_authorization_sessions"
69
+ ).where("id", session.id).first();
70
+ if (!updated) {
71
+ throw new Error(
72
+ `Failed to retrieve updated authorization session with id ${session.id}`
73
+ );
74
+ }
75
+ return this.rowToAuthorizationSession(updated);
76
+ });
77
+ }
78
+ const returnedRows = await this.db(
79
+ "oauth_authorization_sessions"
80
+ ).where("id", session.id).update(updatedFields).returning("*");
81
+ if (returnedRows.length !== 1) {
82
+ throw new Error(
83
+ `Failed to retrieve updated authorization session with id ${session.id}`
84
+ );
85
+ }
86
+ const [returnedSession] = returnedRows;
87
+ return this.rowToAuthorizationSession(
88
+ returnedSession
89
+ );
90
+ }
91
+ async getAuthorizationSession({ id }) {
92
+ const session = await this.db(
93
+ "oauth_authorization_sessions"
94
+ ).where("id", id).first();
95
+ if (!session) {
96
+ return null;
97
+ }
98
+ return this.rowToAuthorizationSession(session);
99
+ }
100
+ async createAuthorizationCode(authorizationCode) {
101
+ await this.db("oidc_authorization_codes").insert({
102
+ code: authorizationCode.code,
103
+ session_id: authorizationCode.sessionId,
104
+ expires_at: authorizationCode.expiresAt,
105
+ used: false
106
+ });
107
+ return {
108
+ ...authorizationCode,
109
+ used: false
110
+ };
111
+ }
112
+ async getAuthorizationCode({ code }) {
113
+ const authCode = await this.db(
114
+ "oidc_authorization_codes"
115
+ ).where("code", code).first();
116
+ if (!authCode) {
117
+ return null;
118
+ }
119
+ return this.rowToAuthorizationCode(authCode);
120
+ }
121
+ async updateAuthorizationCode(authorizationCode) {
122
+ const row = this.authorizationCodeToRow(authorizationCode);
123
+ const updatedFields = Object.fromEntries(
124
+ Object.entries(row).filter(([_, value]) => value !== void 0)
125
+ );
126
+ if (this.db.client.config.client.includes("sqlite3") || this.db.client.config.client.includes("mysql")) {
127
+ return await this.db.transaction(async (trx) => {
128
+ await trx("oidc_authorization_codes").where("code", authorizationCode.code).update(updatedFields);
129
+ const updated = await trx(
130
+ "oidc_authorization_codes"
131
+ ).where("code", authorizationCode.code).first();
132
+ if (!updated) {
133
+ throw new Error(
134
+ `Failed to retrieve updated authorization code with code ${authorizationCode.code}`
135
+ );
136
+ }
137
+ return this.rowToAuthorizationCode(updated);
138
+ });
139
+ }
140
+ const returnedRows = await this.db(
141
+ "oidc_authorization_codes"
142
+ ).where("code", authorizationCode.code).update(updatedFields).returning("*");
143
+ if (returnedRows.length !== 1) {
144
+ throw new Error(
145
+ `Failed to retrieve updated authorization code with code ${authorizationCode.code}`
146
+ );
147
+ }
148
+ const [returnedCode] = returnedRows;
149
+ return this.rowToAuthorizationCode(returnedCode);
150
+ }
151
+ rowToClient(row) {
152
+ return {
153
+ clientId: row.client_id,
154
+ clientName: row.client_name,
155
+ clientSecret: row.client_secret,
156
+ redirectUris: row.redirect_uris ? JSON.parse(row.redirect_uris) : void 0,
157
+ responseTypes: row.response_types ? JSON.parse(row.response_types) : void 0,
158
+ grantTypes: row.grant_types ? JSON.parse(row.grant_types) : void 0,
159
+ scope: row.scope ?? void 0,
160
+ metadata: row.metadata ? JSON.parse(row.metadata) : void 0
161
+ };
162
+ }
163
+ authorizationSessionToRow(session) {
164
+ return {
165
+ id: session.id,
166
+ client_id: session.clientId,
167
+ user_entity_ref: session.userEntityRef,
168
+ redirect_uri: session.redirectUri,
169
+ scope: session.scope,
170
+ state: session.state,
171
+ response_type: session.responseType,
172
+ code_challenge: session.codeChallenge,
173
+ code_challenge_method: session.codeChallengeMethod,
174
+ nonce: session.nonce,
175
+ status: session.status,
176
+ expires_at: toDate(session.expiresAt)
177
+ };
178
+ }
179
+ rowToAuthorizationSession(row) {
180
+ return {
181
+ id: row.id,
182
+ clientId: row.client_id,
183
+ userEntityRef: row.user_entity_ref ?? void 0,
184
+ redirectUri: row.redirect_uri,
185
+ scope: row.scope ?? void 0,
186
+ state: row.state ?? void 0,
187
+ responseType: row.response_type,
188
+ codeChallenge: row.code_challenge ?? void 0,
189
+ codeChallengeMethod: row.code_challenge_method ?? void 0,
190
+ nonce: row.nonce ?? void 0,
191
+ status: row.status,
192
+ expiresAt: toDate(row.expires_at)
193
+ };
194
+ }
195
+ authorizationCodeToRow(authorizationCode) {
196
+ return {
197
+ code: authorizationCode.code,
198
+ session_id: authorizationCode.sessionId,
199
+ expires_at: toDate(authorizationCode.expiresAt),
200
+ used: authorizationCode.used
201
+ };
202
+ }
203
+ rowToAuthorizationCode(row) {
204
+ return {
205
+ code: row.code,
206
+ sessionId: row.session_id,
207
+ expiresAt: toDate(row.expires_at),
208
+ used: Boolean(row.used)
209
+ };
210
+ }
211
+ }
212
+
213
+ exports.OidcDatabase = OidcDatabase;
214
+ //# sourceMappingURL=OidcDatabase.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"OidcDatabase.cjs.js","sources":["../../src/database/OidcDatabase.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 { Knex } from 'knex';\nimport { AuthDatabase } from './AuthDatabase';\n\nfunction toDate(value?: Date | string | number): Date | undefined {\n if (!value) {\n return undefined;\n }\n\n return typeof value === 'string' || typeof value === 'number'\n ? new Date(value)\n : value;\n}\ntype OidcClientRow = {\n client_id: string;\n client_secret: string;\n client_name: string;\n response_types: string;\n grant_types: string;\n redirect_uris: string;\n scope: string | null;\n metadata: string | null;\n};\n\ntype OAuthAuthorizationSessionRow = {\n id: string;\n client_id: string;\n user_entity_ref: string | null;\n redirect_uri: string;\n scope: string | null;\n state: string | null;\n response_type: string;\n code_challenge: string | null;\n code_challenge_method: string | null;\n nonce: string | null;\n status: 'pending' | 'approved' | 'rejected' | 'expired';\n expires_at: Date | string;\n};\n\ntype OidcAuthorizationCodeRow = {\n code: string;\n session_id: string;\n expires_at: Date | string;\n used: boolean;\n};\n\nexport type Client = {\n clientId: string;\n clientName: string;\n clientSecret: string;\n redirectUris: string[];\n responseTypes: string[];\n grantTypes: string[];\n scope?: string;\n metadata?: Record<string, unknown>;\n};\n\nexport type AuthorizationSession = {\n id: string;\n clientId: string;\n userEntityRef?: string;\n redirectUri: string;\n scope?: string;\n state?: string;\n responseType: string;\n codeChallenge?: string;\n codeChallengeMethod?: string;\n nonce?: string;\n status: 'pending' | 'approved' | 'rejected' | 'expired';\n expiresAt: Date;\n};\n\nexport type ConsentRequest = {\n id: string;\n sessionId: string;\n expiresAt: Date;\n};\n\nexport type AuthorizationCode = {\n code: string;\n sessionId: string;\n expiresAt: Date;\n used: boolean;\n};\n\nexport type AccessToken = {\n tokenId: string;\n sessionId: string;\n expiresAt: Date;\n};\n\n/**\n * This class provides database operations for OpenID Connect (OIDC) authentication flows.\n * It manages OIDC clients, authorization codes, and access tokens in the database.\n */\nexport class OidcDatabase {\n private constructor(private readonly db: Knex) {}\n\n static async create(options: { database: AuthDatabase }) {\n const client = await options.database.get();\n return new OidcDatabase(client);\n }\n\n async createClient(client: Client) {\n await this.db<OidcClientRow>('oidc_clients').insert({\n client_id: client.clientId,\n client_secret: client.clientSecret,\n client_name: client.clientName,\n response_types: JSON.stringify(client.responseTypes),\n grant_types: JSON.stringify(client.grantTypes),\n redirect_uris: JSON.stringify(client.redirectUris),\n scope: client.scope,\n metadata: JSON.stringify(client.metadata),\n });\n\n return client;\n }\n\n async getClient({ clientId }: { clientId: string }) {\n const client = await this.db<OidcClientRow>('oidc_clients')\n .where('client_id', clientId)\n .first();\n\n if (!client) {\n return null;\n }\n\n return this.rowToClient(client) as Client;\n }\n\n async createAuthorizationSession(\n session: Omit<AuthorizationSession, 'status'>,\n ) {\n await this.db<OAuthAuthorizationSessionRow>(\n 'oauth_authorization_sessions',\n ).insert({\n id: session.id,\n client_id: session.clientId,\n user_entity_ref: session.userEntityRef,\n redirect_uri: session.redirectUri,\n scope: session.scope,\n state: session.state,\n response_type: session.responseType,\n code_challenge: session.codeChallenge,\n code_challenge_method: session.codeChallengeMethod,\n nonce: session.nonce,\n status: 'pending',\n expires_at: session.expiresAt,\n });\n\n return {\n ...session,\n status: 'pending',\n };\n }\n\n async updateAuthorizationSession(\n session: Partial<AuthorizationSession> & { id: string },\n ) {\n const row = this.authorizationSessionToRow(session);\n const updatedFields = Object.fromEntries(\n Object.entries(row).filter(([_, value]) => value !== undefined),\n );\n\n // MySQL and SQLite3 don't support RETURNING\n if (\n this.db.client.config.client.includes('sqlite3') ||\n this.db.client.config.client.includes('mysql')\n ) {\n return await this.db.transaction(async trx => {\n await trx<OAuthAuthorizationSessionRow>('oauth_authorization_sessions')\n .where('id', session.id)\n .update(updatedFields);\n\n const updated = await trx<OAuthAuthorizationSessionRow>(\n 'oauth_authorization_sessions',\n )\n .where('id', session.id)\n .first();\n\n if (!updated) {\n throw new Error(\n `Failed to retrieve updated authorization session with id ${session.id}`,\n );\n }\n\n return this.rowToAuthorizationSession(updated) as AuthorizationSession;\n });\n }\n\n const returnedRows = await this.db<OAuthAuthorizationSessionRow>(\n 'oauth_authorization_sessions',\n )\n .where('id', session.id)\n .update(updatedFields)\n .returning('*');\n\n if (returnedRows.length !== 1) {\n throw new Error(\n `Failed to retrieve updated authorization session with id ${session.id}`,\n );\n }\n\n const [returnedSession] = returnedRows;\n\n return this.rowToAuthorizationSession(\n returnedSession,\n ) as AuthorizationSession;\n }\n\n async getAuthorizationSession({ id }: { id: string }) {\n const session = await this.db<OAuthAuthorizationSessionRow>(\n 'oauth_authorization_sessions',\n )\n .where('id', id)\n .first();\n\n if (!session) {\n return null;\n }\n\n return this.rowToAuthorizationSession(session) as AuthorizationSession;\n }\n\n async createAuthorizationCode(\n authorizationCode: Omit<AuthorizationCode, 'used'>,\n ) {\n await this.db<OidcAuthorizationCodeRow>('oidc_authorization_codes').insert({\n code: authorizationCode.code,\n session_id: authorizationCode.sessionId,\n expires_at: authorizationCode.expiresAt,\n used: false,\n });\n\n return {\n ...authorizationCode,\n used: false,\n };\n }\n\n async getAuthorizationCode({ code }: { code: string }) {\n const authCode = await this.db<OidcAuthorizationCodeRow>(\n 'oidc_authorization_codes',\n )\n .where('code', code)\n .first();\n\n if (!authCode) {\n return null;\n }\n\n return this.rowToAuthorizationCode(authCode) as AuthorizationCode;\n }\n\n async updateAuthorizationCode(\n authorizationCode: Partial<AuthorizationCode> & { code: string },\n ) {\n const row = this.authorizationCodeToRow(authorizationCode);\n const updatedFields = Object.fromEntries(\n Object.entries(row).filter(([_, value]) => value !== undefined),\n );\n\n // MySQL and SQLite3 don't support RETURNING\n if (\n this.db.client.config.client.includes('sqlite3') ||\n this.db.client.config.client.includes('mysql')\n ) {\n return await this.db.transaction(async trx => {\n await trx<OidcAuthorizationCodeRow>('oidc_authorization_codes')\n .where('code', authorizationCode.code)\n .update(updatedFields);\n\n const updated = await trx<OidcAuthorizationCodeRow>(\n 'oidc_authorization_codes',\n )\n .where('code', authorizationCode.code)\n .first();\n\n if (!updated) {\n throw new Error(\n `Failed to retrieve updated authorization code with code ${authorizationCode.code}`,\n );\n }\n\n return this.rowToAuthorizationCode(updated) as AuthorizationCode;\n });\n }\n\n const returnedRows = await this.db<OidcAuthorizationCodeRow>(\n 'oidc_authorization_codes',\n )\n .where('code', authorizationCode.code)\n .update(updatedFields)\n .returning('*');\n\n if (returnedRows.length !== 1) {\n throw new Error(\n `Failed to retrieve updated authorization code with code ${authorizationCode.code}`,\n );\n }\n\n const [returnedCode] = returnedRows;\n\n return this.rowToAuthorizationCode(returnedCode) as AuthorizationCode;\n }\n\n private rowToClient(row: OidcClientRow): Client {\n return {\n clientId: row.client_id,\n clientName: row.client_name,\n clientSecret: row.client_secret,\n redirectUris: row.redirect_uris\n ? JSON.parse(row.redirect_uris)\n : undefined,\n responseTypes: row.response_types\n ? JSON.parse(row.response_types)\n : undefined,\n grantTypes: row.grant_types ? JSON.parse(row.grant_types) : undefined,\n scope: row.scope ?? undefined,\n metadata: row.metadata ? JSON.parse(row.metadata) : undefined,\n };\n }\n\n private authorizationSessionToRow(\n session: Partial<AuthorizationSession>,\n ): Partial<OAuthAuthorizationSessionRow> {\n return {\n id: session.id,\n client_id: session.clientId,\n user_entity_ref: session.userEntityRef,\n redirect_uri: session.redirectUri,\n scope: session.scope,\n state: session.state,\n response_type: session.responseType,\n code_challenge: session.codeChallenge,\n code_challenge_method: session.codeChallengeMethod,\n nonce: session.nonce,\n status: session.status,\n expires_at: toDate(session.expiresAt),\n };\n }\n\n private rowToAuthorizationSession(\n row: OAuthAuthorizationSessionRow,\n ): Partial<AuthorizationSession> {\n return {\n id: row.id,\n clientId: row.client_id,\n userEntityRef: row.user_entity_ref ?? undefined,\n redirectUri: row.redirect_uri,\n scope: row.scope ?? undefined,\n state: row.state ?? undefined,\n responseType: row.response_type,\n codeChallenge: row.code_challenge ?? undefined,\n codeChallengeMethod: row.code_challenge_method ?? undefined,\n nonce: row.nonce ?? undefined,\n status: row.status,\n expiresAt: toDate(row.expires_at),\n };\n }\n\n private authorizationCodeToRow(\n authorizationCode: Partial<AuthorizationCode>,\n ): Partial<OidcAuthorizationCodeRow> {\n return {\n code: authorizationCode.code,\n session_id: authorizationCode.sessionId,\n expires_at: toDate(authorizationCode.expiresAt),\n used: authorizationCode.used,\n };\n }\n\n private rowToAuthorizationCode(\n row: OidcAuthorizationCodeRow,\n ): Partial<AuthorizationCode> {\n return {\n code: row.code,\n sessionId: row.session_id,\n expiresAt: toDate(row.expires_at),\n used: Boolean(row.used),\n };\n }\n}\n"],"names":[],"mappings":";;AAkBA,SAAS,OAAO,KAAA,EAAkD;AAChE,EAAA,IAAI,CAAC,KAAA,EAAO;AACV,IAAA,OAAO,MAAA;AAAA,EACT;AAEA,EAAA,OAAO,OAAO,UAAU,QAAA,IAAY,OAAO,UAAU,QAAA,GACjD,IAAI,IAAA,CAAK,KAAK,CAAA,GACd,KAAA;AACN;AAmFO,MAAM,YAAA,CAAa;AAAA,EAChB,YAA6B,EAAA,EAAU;AAAV,IAAA,IAAA,CAAA,EAAA,GAAA,EAAA;AAAA,EAAW;AAAA,EAEhD,aAAa,OAAO,OAAA,EAAqC;AACvD,IAAA,MAAM,MAAA,GAAS,MAAM,OAAA,CAAQ,QAAA,CAAS,GAAA,EAAI;AAC1C,IAAA,OAAO,IAAI,aAAa,MAAM,CAAA;AAAA,EAChC;AAAA,EAEA,MAAM,aAAa,MAAA,EAAgB;AACjC,IAAA,MAAM,IAAA,CAAK,EAAA,CAAkB,cAAc,CAAA,CAAE,MAAA,CAAO;AAAA,MAClD,WAAW,MAAA,CAAO,QAAA;AAAA,MAClB,eAAe,MAAA,CAAO,YAAA;AAAA,MACtB,aAAa,MAAA,CAAO,UAAA;AAAA,MACpB,cAAA,EAAgB,IAAA,CAAK,SAAA,CAAU,MAAA,CAAO,aAAa,CAAA;AAAA,MACnD,WAAA,EAAa,IAAA,CAAK,SAAA,CAAU,MAAA,CAAO,UAAU,CAAA;AAAA,MAC7C,aAAA,EAAe,IAAA,CAAK,SAAA,CAAU,MAAA,CAAO,YAAY,CAAA;AAAA,MACjD,OAAO,MAAA,CAAO,KAAA;AAAA,MACd,QAAA,EAAU,IAAA,CAAK,SAAA,CAAU,MAAA,CAAO,QAAQ;AAAA,KACzC,CAAA;AAED,IAAA,OAAO,MAAA;AAAA,EACT;AAAA,EAEA,MAAM,SAAA,CAAU,EAAE,QAAA,EAAS,EAAyB;AAClD,IAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,EAAA,CAAkB,cAAc,EACvD,KAAA,CAAM,WAAA,EAAa,QAAQ,CAAA,CAC3B,KAAA,EAAM;AAET,IAAA,IAAI,CAAC,MAAA,EAAQ;AACX,MAAA,OAAO,IAAA;AAAA,IACT;AAEA,IAAA,OAAO,IAAA,CAAK,YAAY,MAAM,CAAA;AAAA,EAChC;AAAA,EAEA,MAAM,2BACJ,OAAA,EACA;AACA,IAAA,MAAM,IAAA,CAAK,EAAA;AAAA,MACT;AAAA,MACA,MAAA,CAAO;AAAA,MACP,IAAI,OAAA,CAAQ,EAAA;AAAA,MACZ,WAAW,OAAA,CAAQ,QAAA;AAAA,MACnB,iBAAiB,OAAA,CAAQ,aAAA;AAAA,MACzB,cAAc,OAAA,CAAQ,WAAA;AAAA,MACtB,OAAO,OAAA,CAAQ,KAAA;AAAA,MACf,OAAO,OAAA,CAAQ,KAAA;AAAA,MACf,eAAe,OAAA,CAAQ,YAAA;AAAA,MACvB,gBAAgB,OAAA,CAAQ,aAAA;AAAA,MACxB,uBAAuB,OAAA,CAAQ,mBAAA;AAAA,MAC/B,OAAO,OAAA,CAAQ,KAAA;AAAA,MACf,MAAA,EAAQ,SAAA;AAAA,MACR,YAAY,OAAA,CAAQ;AAAA,KACrB,CAAA;AAED,IAAA,OAAO;AAAA,MACL,GAAG,OAAA;AAAA,MACH,MAAA,EAAQ;AAAA,KACV;AAAA,EACF;AAAA,EAEA,MAAM,2BACJ,OAAA,EACA;AACA,IAAA,MAAM,GAAA,GAAM,IAAA,CAAK,yBAAA,CAA0B,OAAO,CAAA;AAClD,IAAA,MAAM,gBAAgB,MAAA,CAAO,WAAA;AAAA,MAC3B,MAAA,CAAO,OAAA,CAAQ,GAAG,CAAA,CAAE,MAAA,CAAO,CAAC,CAAC,CAAA,EAAG,KAAK,CAAA,KAAM,KAAA,KAAU,MAAS;AAAA,KAChE;AAGA,IAAA,IACE,IAAA,CAAK,EAAA,CAAG,MAAA,CAAO,MAAA,CAAO,OAAO,QAAA,CAAS,SAAS,CAAA,IAC/C,IAAA,CAAK,GAAG,MAAA,CAAO,MAAA,CAAO,MAAA,CAAO,QAAA,CAAS,OAAO,CAAA,EAC7C;AACA,MAAA,OAAO,MAAM,IAAA,CAAK,EAAA,CAAG,WAAA,CAAY,OAAM,GAAA,KAAO;AAC5C,QAAA,MAAM,GAAA,CAAkC,8BAA8B,CAAA,CACnE,KAAA,CAAM,MAAM,OAAA,CAAQ,EAAE,CAAA,CACtB,MAAA,CAAO,aAAa,CAAA;AAEvB,QAAA,MAAM,UAAU,MAAM,GAAA;AAAA,UACpB;AAAA,UAEC,KAAA,CAAM,IAAA,EAAM,OAAA,CAAQ,EAAE,EACtB,KAAA,EAAM;AAET,QAAA,IAAI,CAAC,OAAA,EAAS;AACZ,UAAA,MAAM,IAAI,KAAA;AAAA,YACR,CAAA,yDAAA,EAA4D,QAAQ,EAAE,CAAA;AAAA,WACxE;AAAA,QACF;AAEA,QAAA,OAAO,IAAA,CAAK,0BAA0B,OAAO,CAAA;AAAA,MAC/C,CAAC,CAAA;AAAA,IACH;AAEA,IAAA,MAAM,YAAA,GAAe,MAAM,IAAA,CAAK,EAAA;AAAA,MAC9B;AAAA,KACF,CACG,KAAA,CAAM,IAAA,EAAM,OAAA,CAAQ,EAAE,EACtB,MAAA,CAAO,aAAa,CAAA,CACpB,SAAA,CAAU,GAAG,CAAA;AAEhB,IAAA,IAAI,YAAA,CAAa,WAAW,CAAA,EAAG;AAC7B,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,CAAA,yDAAA,EAA4D,QAAQ,EAAE,CAAA;AAAA,OACxE;AAAA,IACF;AAEA,IAAA,MAAM,CAAC,eAAe,CAAA,GAAI,YAAA;AAE1B,IAAA,OAAO,IAAA,CAAK,yBAAA;AAAA,MACV;AAAA,KACF;AAAA,EACF;AAAA,EAEA,MAAM,uBAAA,CAAwB,EAAE,EAAA,EAAG,EAAmB;AACpD,IAAA,MAAM,OAAA,GAAU,MAAM,IAAA,CAAK,EAAA;AAAA,MACzB;AAAA,KACF,CACG,KAAA,CAAM,IAAA,EAAM,EAAE,EACd,KAAA,EAAM;AAET,IAAA,IAAI,CAAC,OAAA,EAAS;AACZ,MAAA,OAAO,IAAA;AAAA,IACT;AAEA,IAAA,OAAO,IAAA,CAAK,0BAA0B,OAAO,CAAA;AAAA,EAC/C;AAAA,EAEA,MAAM,wBACJ,iBAAA,EACA;AACA,IAAA,MAAM,IAAA,CAAK,EAAA,CAA6B,0BAA0B,CAAA,CAAE,MAAA,CAAO;AAAA,MACzE,MAAM,iBAAA,CAAkB,IAAA;AAAA,MACxB,YAAY,iBAAA,CAAkB,SAAA;AAAA,MAC9B,YAAY,iBAAA,CAAkB,SAAA;AAAA,MAC9B,IAAA,EAAM;AAAA,KACP,CAAA;AAED,IAAA,OAAO;AAAA,MACL,GAAG,iBAAA;AAAA,MACH,IAAA,EAAM;AAAA,KACR;AAAA,EACF;AAAA,EAEA,MAAM,oBAAA,CAAqB,EAAE,IAAA,EAAK,EAAqB;AACrD,IAAA,MAAM,QAAA,GAAW,MAAM,IAAA,CAAK,EAAA;AAAA,MAC1B;AAAA,KACF,CACG,KAAA,CAAM,MAAA,EAAQ,IAAI,EAClB,KAAA,EAAM;AAET,IAAA,IAAI,CAAC,QAAA,EAAU;AACb,MAAA,OAAO,IAAA;AAAA,IACT;AAEA,IAAA,OAAO,IAAA,CAAK,uBAAuB,QAAQ,CAAA;AAAA,EAC7C;AAAA,EAEA,MAAM,wBACJ,iBAAA,EACA;AACA,IAAA,MAAM,GAAA,GAAM,IAAA,CAAK,sBAAA,CAAuB,iBAAiB,CAAA;AACzD,IAAA,MAAM,gBAAgB,MAAA,CAAO,WAAA;AAAA,MAC3B,MAAA,CAAO,OAAA,CAAQ,GAAG,CAAA,CAAE,MAAA,CAAO,CAAC,CAAC,CAAA,EAAG,KAAK,CAAA,KAAM,KAAA,KAAU,MAAS;AAAA,KAChE;AAGA,IAAA,IACE,IAAA,CAAK,EAAA,CAAG,MAAA,CAAO,MAAA,CAAO,OAAO,QAAA,CAAS,SAAS,CAAA,IAC/C,IAAA,CAAK,GAAG,MAAA,CAAO,MAAA,CAAO,MAAA,CAAO,QAAA,CAAS,OAAO,CAAA,EAC7C;AACA,MAAA,OAAO,MAAM,IAAA,CAAK,EAAA,CAAG,WAAA,CAAY,OAAM,GAAA,KAAO;AAC5C,QAAA,MAAM,GAAA,CAA8B,0BAA0B,CAAA,CAC3D,KAAA,CAAM,QAAQ,iBAAA,CAAkB,IAAI,CAAA,CACpC,MAAA,CAAO,aAAa,CAAA;AAEvB,QAAA,MAAM,UAAU,MAAM,GAAA;AAAA,UACpB;AAAA,UAEC,KAAA,CAAM,MAAA,EAAQ,iBAAA,CAAkB,IAAI,EACpC,KAAA,EAAM;AAET,QAAA,IAAI,CAAC,OAAA,EAAS;AACZ,UAAA,MAAM,IAAI,KAAA;AAAA,YACR,CAAA,wDAAA,EAA2D,kBAAkB,IAAI,CAAA;AAAA,WACnF;AAAA,QACF;AAEA,QAAA,OAAO,IAAA,CAAK,uBAAuB,OAAO,CAAA;AAAA,MAC5C,CAAC,CAAA;AAAA,IACH;AAEA,IAAA,MAAM,YAAA,GAAe,MAAM,IAAA,CAAK,EAAA;AAAA,MAC9B;AAAA,KACF,CACG,KAAA,CAAM,MAAA,EAAQ,iBAAA,CAAkB,IAAI,EACpC,MAAA,CAAO,aAAa,CAAA,CACpB,SAAA,CAAU,GAAG,CAAA;AAEhB,IAAA,IAAI,YAAA,CAAa,WAAW,CAAA,EAAG;AAC7B,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,CAAA,wDAAA,EAA2D,kBAAkB,IAAI,CAAA;AAAA,OACnF;AAAA,IACF;AAEA,IAAA,MAAM,CAAC,YAAY,CAAA,GAAI,YAAA;AAEvB,IAAA,OAAO,IAAA,CAAK,uBAAuB,YAAY,CAAA;AAAA,EACjD;AAAA,EAEQ,YAAY,GAAA,EAA4B;AAC9C,IAAA,OAAO;AAAA,MACL,UAAU,GAAA,CAAI,SAAA;AAAA,MACd,YAAY,GAAA,CAAI,WAAA;AAAA,MAChB,cAAc,GAAA,CAAI,aAAA;AAAA,MAClB,cAAc,GAAA,CAAI,aAAA,GACd,KAAK,KAAA,CAAM,GAAA,CAAI,aAAa,CAAA,GAC5B,MAAA;AAAA,MACJ,eAAe,GAAA,CAAI,cAAA,GACf,KAAK,KAAA,CAAM,GAAA,CAAI,cAAc,CAAA,GAC7B,MAAA;AAAA,MACJ,YAAY,GAAA,CAAI,WAAA,GAAc,KAAK,KAAA,CAAM,GAAA,CAAI,WAAW,CAAA,GAAI,MAAA;AAAA,MAC5D,KAAA,EAAO,IAAI,KAAA,IAAS,MAAA;AAAA,MACpB,UAAU,GAAA,CAAI,QAAA,GAAW,KAAK,KAAA,CAAM,GAAA,CAAI,QAAQ,CAAA,GAAI;AAAA,KACtD;AAAA,EACF;AAAA,EAEQ,0BACN,OAAA,EACuC;AACvC,IAAA,OAAO;AAAA,MACL,IAAI,OAAA,CAAQ,EAAA;AAAA,MACZ,WAAW,OAAA,CAAQ,QAAA;AAAA,MACnB,iBAAiB,OAAA,CAAQ,aAAA;AAAA,MACzB,cAAc,OAAA,CAAQ,WAAA;AAAA,MACtB,OAAO,OAAA,CAAQ,KAAA;AAAA,MACf,OAAO,OAAA,CAAQ,KAAA;AAAA,MACf,eAAe,OAAA,CAAQ,YAAA;AAAA,MACvB,gBAAgB,OAAA,CAAQ,aAAA;AAAA,MACxB,uBAAuB,OAAA,CAAQ,mBAAA;AAAA,MAC/B,OAAO,OAAA,CAAQ,KAAA;AAAA,MACf,QAAQ,OAAA,CAAQ,MAAA;AAAA,MAChB,UAAA,EAAY,MAAA,CAAO,OAAA,CAAQ,SAAS;AAAA,KACtC;AAAA,EACF;AAAA,EAEQ,0BACN,GAAA,EAC+B;AAC/B,IAAA,OAAO;AAAA,MACL,IAAI,GAAA,CAAI,EAAA;AAAA,MACR,UAAU,GAAA,CAAI,SAAA;AAAA,MACd,aAAA,EAAe,IAAI,eAAA,IAAmB,MAAA;AAAA,MACtC,aAAa,GAAA,CAAI,YAAA;AAAA,MACjB,KAAA,EAAO,IAAI,KAAA,IAAS,MAAA;AAAA,MACpB,KAAA,EAAO,IAAI,KAAA,IAAS,MAAA;AAAA,MACpB,cAAc,GAAA,CAAI,aAAA;AAAA,MAClB,aAAA,EAAe,IAAI,cAAA,IAAkB,MAAA;AAAA,MACrC,mBAAA,EAAqB,IAAI,qBAAA,IAAyB,MAAA;AAAA,MAClD,KAAA,EAAO,IAAI,KAAA,IAAS,MAAA;AAAA,MACpB,QAAQ,GAAA,CAAI,MAAA;AAAA,MACZ,SAAA,EAAW,MAAA,CAAO,GAAA,CAAI,UAAU;AAAA,KAClC;AAAA,EACF;AAAA,EAEQ,uBACN,iBAAA,EACmC;AACnC,IAAA,OAAO;AAAA,MACL,MAAM,iBAAA,CAAkB,IAAA;AAAA,MACxB,YAAY,iBAAA,CAAkB,SAAA;AAAA,MAC9B,UAAA,EAAY,MAAA,CAAO,iBAAA,CAAkB,SAAS,CAAA;AAAA,MAC9C,MAAM,iBAAA,CAAkB;AAAA,KAC1B;AAAA,EACF;AAAA,EAEQ,uBACN,GAAA,EAC4B;AAC5B,IAAA,OAAO;AAAA,MACL,MAAM,GAAA,CAAI,IAAA;AAAA,MACV,WAAW,GAAA,CAAI,UAAA;AAAA,MACf,SAAA,EAAW,MAAA,CAAO,GAAA,CAAI,UAAU,CAAA;AAAA,MAChC,IAAA,EAAM,OAAA,CAAQ,GAAA,CAAI,IAAI;AAAA,KACxB;AAAA,EACF;AACF;;;;"}
@@ -3,20 +3,34 @@
3
3
  var Router = require('express-promise-router');
4
4
  var OidcService = require('./OidcService.cjs.js');
5
5
  var errors = require('@backstage/errors');
6
+ var express = require('express');
6
7
 
7
8
  function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; }
8
9
 
9
10
  var Router__default = /*#__PURE__*/_interopDefaultCompat(Router);
10
11
 
11
12
  class OidcRouter {
12
- constructor(oidc) {
13
+ constructor(oidc, logger, auth, appUrl, httpAuth, config) {
13
14
  this.oidc = oidc;
15
+ this.logger = logger;
16
+ this.auth = auth;
17
+ this.appUrl = appUrl;
18
+ this.httpAuth = httpAuth;
19
+ this.config = config;
14
20
  }
15
21
  static create(options) {
16
- return new OidcRouter(OidcService.OidcService.create(options));
22
+ return new OidcRouter(
23
+ OidcService.OidcService.create(options),
24
+ options.logger,
25
+ options.auth,
26
+ options.appUrl,
27
+ options.httpAuth,
28
+ options.config
29
+ );
17
30
  }
18
31
  getRouter() {
19
32
  const router = Router__default.default();
33
+ router.use(express.json());
20
34
  router.get("/.well-known/openid-configuration", (_req, res) => {
21
35
  res.json(this.oidc.getConfiguration());
22
36
  });
@@ -24,9 +38,6 @@ class OidcRouter {
24
38
  const { keys } = await this.oidc.listPublicKeys();
25
39
  res.json({ keys });
26
40
  });
27
- router.get("/v1/token", (_req, res) => {
28
- res.status(501).send("Not Implemented");
29
- });
30
41
  router.get("/v1/userinfo", async (req, res) => {
31
42
  const matches = req.headers.authorization?.match(/^Bearer[ ]+(\S+)$/i);
32
43
  const token = matches?.[1];
@@ -40,9 +51,277 @@ class OidcRouter {
40
51
  }
41
52
  res.json(userInfo);
42
53
  });
54
+ if (this.config.getOptionalBoolean(
55
+ "auth.experimentalDynamicClientRegistration.enabled"
56
+ )) {
57
+ router.get("/v1/authorize", async (req, res) => {
58
+ const {
59
+ client_id: clientId,
60
+ redirect_uri: redirectUri,
61
+ response_type: responseType,
62
+ scope,
63
+ state,
64
+ nonce,
65
+ code_challenge: codeChallenge,
66
+ code_challenge_method: codeChallengeMethod
67
+ } = req.query;
68
+ if (!clientId || !redirectUri || !responseType) {
69
+ this.logger.error(`Failed to authorize: Missing required parameters`);
70
+ return res.status(400).json({
71
+ error: "invalid_request",
72
+ error_description: "Missing required parameters: client_id, redirect_uri, response_type"
73
+ });
74
+ }
75
+ try {
76
+ const result = await this.oidc.createAuthorizationSession({
77
+ clientId,
78
+ redirectUri,
79
+ responseType,
80
+ scope,
81
+ state,
82
+ nonce,
83
+ codeChallenge,
84
+ codeChallengeMethod
85
+ });
86
+ const authSessionRedirectUrl = new URL(
87
+ `./oauth2/authorize/${result.id}`,
88
+ ensureTrailingSlash(this.appUrl)
89
+ );
90
+ return res.redirect(authSessionRedirectUrl.toString());
91
+ } catch (error) {
92
+ const errorParams = new URLSearchParams();
93
+ errorParams.append(
94
+ "error",
95
+ errors.isError(error) ? error.name : "server_error"
96
+ );
97
+ errorParams.append(
98
+ "error_description",
99
+ errors.isError(error) ? error.message : "Unknown error"
100
+ );
101
+ if (state) {
102
+ errorParams.append("state", state);
103
+ }
104
+ const redirectUrl = new URL(redirectUri);
105
+ redirectUrl.search = errorParams.toString();
106
+ return res.redirect(redirectUrl.toString());
107
+ }
108
+ });
109
+ router.get("/v1/sessions/:sessionId", async (req, res) => {
110
+ const { sessionId } = req.params;
111
+ if (!sessionId) {
112
+ return res.status(400).json({
113
+ error: "invalid_request",
114
+ error_description: "Missing Authorization Session ID"
115
+ });
116
+ }
117
+ try {
118
+ const session = await this.oidc.getAuthorizationSession({
119
+ sessionId
120
+ });
121
+ return res.json({
122
+ id: session.id,
123
+ clientName: session.clientName,
124
+ scope: session.scope,
125
+ redirectUri: session.redirectUri
126
+ });
127
+ } catch (error) {
128
+ const description = errors.isError(error) ? error.message : "Unknown error";
129
+ this.logger.error(
130
+ `Failed to get authorization session: ${description}`,
131
+ error
132
+ );
133
+ return res.status(404).json({
134
+ error: "not_found",
135
+ error_description: description
136
+ });
137
+ }
138
+ });
139
+ router.post("/v1/sessions/:sessionId/approve", async (req, res) => {
140
+ const { sessionId } = req.params;
141
+ if (!sessionId) {
142
+ return res.status(400).json({
143
+ error: "invalid_request",
144
+ error_description: "Missing authorization session ID"
145
+ });
146
+ }
147
+ try {
148
+ const httpCredentials = await this.httpAuth.credentials(req);
149
+ if (!this.auth.isPrincipal(httpCredentials, "user")) {
150
+ return res.status(401).json({
151
+ error: "unauthorized",
152
+ error_description: "Authentication required"
153
+ });
154
+ }
155
+ const { userEntityRef } = httpCredentials.principal;
156
+ const result = await this.oidc.approveAuthorizationSession({
157
+ sessionId,
158
+ userEntityRef
159
+ });
160
+ return res.json({
161
+ redirectUrl: result.redirectUrl
162
+ });
163
+ } catch (error) {
164
+ const description = errors.isError(error) ? error.message : "Unknown error";
165
+ this.logger.error(
166
+ `Failed to approve authorization session: ${description}`,
167
+ error
168
+ );
169
+ return res.status(400).json({
170
+ error: "invalid_request",
171
+ error_description: description
172
+ });
173
+ }
174
+ });
175
+ router.post("/v1/sessions/:sessionId/reject", async (req, res) => {
176
+ const { sessionId } = req.params;
177
+ if (!sessionId) {
178
+ return res.status(400).json({
179
+ error: "invalid_request",
180
+ error_description: "Missing authorization session ID"
181
+ });
182
+ }
183
+ const httpCredentials = await this.httpAuth.credentials(req);
184
+ if (!this.auth.isPrincipal(httpCredentials, "user")) {
185
+ return res.status(401).json({
186
+ error: "unauthorized",
187
+ error_description: "Authentication required"
188
+ });
189
+ }
190
+ const { userEntityRef } = httpCredentials.principal;
191
+ try {
192
+ const session = await this.oidc.getAuthorizationSession({
193
+ sessionId
194
+ });
195
+ await this.oidc.rejectAuthorizationSession({
196
+ sessionId,
197
+ userEntityRef
198
+ });
199
+ const errorParams = new URLSearchParams();
200
+ errorParams.append("error", "access_denied");
201
+ errorParams.append("error_description", "User denied the request");
202
+ if (session.state) {
203
+ errorParams.append("state", session.state);
204
+ }
205
+ const redirectUrl = new URL(session.redirectUri);
206
+ redirectUrl.search = errorParams.toString();
207
+ return res.json({
208
+ redirectUrl: redirectUrl.toString()
209
+ });
210
+ } catch (error) {
211
+ const description = errors.isError(error) ? error.message : "Unknown error";
212
+ this.logger.error(
213
+ `Failed to reject authorization session: ${description}`,
214
+ error
215
+ );
216
+ return res.status(400).json({
217
+ error: "invalid_request",
218
+ error_description: description
219
+ });
220
+ }
221
+ });
222
+ router.post("/v1/token", async (req, res) => {
223
+ const {
224
+ grant_type: grantType,
225
+ code,
226
+ redirect_uri: redirectUri,
227
+ code_verifier: codeVerifier
228
+ } = req.body;
229
+ if (!grantType || !code || !redirectUri) {
230
+ this.logger.error(
231
+ `Failed to exchange code for token: Missing required parameters`
232
+ );
233
+ return res.status(400).json({
234
+ error: "invalid_request",
235
+ error_description: "Missing required parameters"
236
+ });
237
+ }
238
+ try {
239
+ const result = await this.oidc.exchangeCodeForToken({
240
+ code,
241
+ redirectUri,
242
+ codeVerifier,
243
+ grantType
244
+ });
245
+ return res.json({
246
+ access_token: result.accessToken,
247
+ token_type: result.tokenType,
248
+ expires_in: result.expiresIn,
249
+ id_token: result.idToken,
250
+ scope: result.scope
251
+ });
252
+ } catch (error) {
253
+ const description = errors.isError(error) ? error.message : "Unknown error";
254
+ this.logger.error(
255
+ `Failed to exchange code for token: ${description}`,
256
+ error
257
+ );
258
+ if (errors.isError(error)) {
259
+ if (error.name === "AuthenticationError") {
260
+ return res.status(401).json({
261
+ error: "invalid_client",
262
+ error_description: error.message
263
+ });
264
+ }
265
+ if (error.name === "InputError") {
266
+ return res.status(400).json({
267
+ error: "invalid_request",
268
+ error_description: error.message
269
+ });
270
+ }
271
+ }
272
+ return res.status(500).json({
273
+ error: "server_error",
274
+ error_description: description
275
+ });
276
+ }
277
+ });
278
+ router.post("/v1/register", async (req, res) => {
279
+ const {
280
+ client_name: clientName,
281
+ redirect_uris: redirectUris,
282
+ response_types: responseTypes,
283
+ grant_types: grantTypes,
284
+ scope
285
+ } = req.body;
286
+ if (!redirectUris?.length) {
287
+ res.status(400).json({
288
+ error: "invalid_request",
289
+ error_description: "redirect_uris is required"
290
+ });
291
+ return;
292
+ }
293
+ try {
294
+ const client = await this.oidc.registerClient({
295
+ clientName,
296
+ redirectUris,
297
+ responseTypes,
298
+ grantTypes,
299
+ scope
300
+ });
301
+ res.status(201).json({
302
+ client_id: client.clientId,
303
+ redirect_uris: client.redirectUris,
304
+ client_secret: client.clientSecret
305
+ });
306
+ } catch (e) {
307
+ const description = errors.isError(e) ? e.message : "Unknown error";
308
+ this.logger.error(`Failed to register client: ${description}`, e);
309
+ res.status(500).json({
310
+ error: "server_error",
311
+ error_description: `Failed to register client: ${description}`
312
+ });
313
+ }
314
+ });
315
+ }
43
316
  return router;
44
317
  }
45
318
  }
319
+ function ensureTrailingSlash(appUrl) {
320
+ if (appUrl.endsWith("/")) {
321
+ return appUrl;
322
+ }
323
+ return `${appUrl}/`;
324
+ }
46
325
 
47
326
  exports.OidcRouter = OidcRouter;
48
327
  //# sourceMappingURL=OidcRouter.cjs.js.map