@backstage/plugin-auth-backend 0.6.2 → 0.9.0-next.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +82 -0
- package/dist/index.cjs.js +994 -862
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +94 -42
- package/package.json +10 -10
package/dist/index.cjs.js
CHANGED
|
@@ -20,17 +20,17 @@ var passportGithub2 = require('passport-github2');
|
|
|
20
20
|
var passportGitlab2 = require('passport-gitlab2');
|
|
21
21
|
var passportGoogleOauth20 = require('passport-google-oauth20');
|
|
22
22
|
var passportMicrosoft = require('passport-microsoft');
|
|
23
|
+
var uuid = require('uuid');
|
|
24
|
+
var luxon = require('luxon');
|
|
25
|
+
var backendCommon = require('@backstage/backend-common');
|
|
26
|
+
var firestore = require('@google-cloud/firestore');
|
|
27
|
+
var lodash = require('lodash');
|
|
23
28
|
var openidClient = require('openid-client');
|
|
24
29
|
var passportOktaOauth = require('passport-okta-oauth');
|
|
25
30
|
var passportOneloginOauth = require('passport-onelogin-oauth');
|
|
26
31
|
var passportSaml = require('passport-saml');
|
|
27
32
|
var googleAuthLibrary = require('google-auth-library');
|
|
28
33
|
var catalogClient = require('@backstage/catalog-client');
|
|
29
|
-
var uuid = require('uuid');
|
|
30
|
-
var luxon = require('luxon');
|
|
31
|
-
var backendCommon = require('@backstage/backend-common');
|
|
32
|
-
var firestore = require('@google-cloud/firestore');
|
|
33
|
-
var lodash = require('lodash');
|
|
34
34
|
var session = require('express-session');
|
|
35
35
|
var passport = require('passport');
|
|
36
36
|
var minimatch = require('minimatch');
|
|
@@ -149,6 +149,16 @@ const verifyNonce = (req, providerId) => {
|
|
|
149
149
|
throw new Error("Invalid nonce");
|
|
150
150
|
}
|
|
151
151
|
};
|
|
152
|
+
const getCookieConfig = (authUrl, providerId) => {
|
|
153
|
+
const { hostname: cookieDomain, pathname, protocol } = authUrl;
|
|
154
|
+
const secure = protocol === "https:";
|
|
155
|
+
const cookiePath = pathname.endsWith(`${providerId}/handler/frame`) ? pathname.slice(0, -"/handler/frame".length) : `${pathname}/${providerId}`;
|
|
156
|
+
return {
|
|
157
|
+
cookieDomain,
|
|
158
|
+
cookiePath,
|
|
159
|
+
secure
|
|
160
|
+
};
|
|
161
|
+
};
|
|
152
162
|
|
|
153
163
|
class OAuthEnvironmentHandler {
|
|
154
164
|
constructor(handlers) {
|
|
@@ -245,6 +255,10 @@ function parseJwtPayload(token) {
|
|
|
245
255
|
}
|
|
246
256
|
function prepareBackstageIdentityResponse(result) {
|
|
247
257
|
const { sub, ent } = parseJwtPayload(result.token);
|
|
258
|
+
const userEntityRef = catalogModel.stringifyEntityRef(catalogModel.parseEntityRef(sub, {
|
|
259
|
+
defaultKind: "user",
|
|
260
|
+
defaultNamespace: catalogModel.ENTITY_DEFAULT_NAMESPACE
|
|
261
|
+
}));
|
|
248
262
|
return {
|
|
249
263
|
...{
|
|
250
264
|
idToken: result.token,
|
|
@@ -252,7 +266,7 @@ function prepareBackstageIdentityResponse(result) {
|
|
|
252
266
|
},
|
|
253
267
|
identity: {
|
|
254
268
|
type: "user",
|
|
255
|
-
userEntityRef
|
|
269
|
+
userEntityRef,
|
|
256
270
|
ownershipEntityRefs: ent != null ? ent : []
|
|
257
271
|
}
|
|
258
272
|
};
|
|
@@ -309,14 +323,14 @@ class OAuthAdapter {
|
|
|
309
323
|
};
|
|
310
324
|
}
|
|
311
325
|
static fromConfig(config, handlers, options) {
|
|
326
|
+
var _a;
|
|
312
327
|
const { origin: appOrigin } = new url.URL(config.appUrl);
|
|
313
|
-
const
|
|
314
|
-
const
|
|
315
|
-
const cookiePath = `${url$1.pathname}/${options.providerId}`;
|
|
328
|
+
const authUrl = new url.URL((_a = options.callbackUrl) != null ? _a : config.baseUrl);
|
|
329
|
+
const { cookieDomain, cookiePath, secure } = getCookieConfig(authUrl, options.providerId);
|
|
316
330
|
return new OAuthAdapter(handlers, {
|
|
317
331
|
...options,
|
|
318
332
|
appOrigin,
|
|
319
|
-
cookieDomain
|
|
333
|
+
cookieDomain,
|
|
320
334
|
cookiePath,
|
|
321
335
|
secure,
|
|
322
336
|
isOriginAllowed: config.isOriginAllowed
|
|
@@ -546,7 +560,7 @@ const executeFetchUserProfileStrategy = async (providerStrategy, accessToken) =>
|
|
|
546
560
|
class CatalogIdentityClient {
|
|
547
561
|
constructor(options) {
|
|
548
562
|
this.catalogApi = options.catalogApi;
|
|
549
|
-
this.
|
|
563
|
+
this.tokenManager = options.tokenManager;
|
|
550
564
|
}
|
|
551
565
|
async findUser(query) {
|
|
552
566
|
const filter = {
|
|
@@ -555,9 +569,7 @@ class CatalogIdentityClient {
|
|
|
555
569
|
for (const [key, value] of Object.entries(query.annotations)) {
|
|
556
570
|
filter[`metadata.annotations.${key}`] = value;
|
|
557
571
|
}
|
|
558
|
-
const token = await this.
|
|
559
|
-
claims: { sub: "backstage.io/auth-backend" }
|
|
560
|
-
});
|
|
572
|
+
const { token } = await this.tokenManager.getToken();
|
|
561
573
|
const { items } = await this.catalogApi.getEntities({ filter }, { token });
|
|
562
574
|
if (items.length !== 1) {
|
|
563
575
|
if (items.length > 1) {
|
|
@@ -587,7 +599,8 @@ class CatalogIdentityClient {
|
|
|
587
599
|
"metadata.namespace": ref.namespace,
|
|
588
600
|
"metadata.name": ref.name
|
|
589
601
|
}));
|
|
590
|
-
const
|
|
602
|
+
const { token } = await this.tokenManager.getToken();
|
|
603
|
+
const entities = await this.catalogApi.getEntities({ filter }, { token }).then((r) => r.items);
|
|
591
604
|
if (entityRefs.length !== entities.length) {
|
|
592
605
|
const foundEntityNames = entities.map(catalogModel.stringifyEntityRef);
|
|
593
606
|
const missingEntityNames = resolvedEntityRefs.map(catalogModel.stringifyEntityRef).filter((s) => !foundEntityNames.includes(s));
|
|
@@ -655,7 +668,12 @@ class AtlassianAuthProvider {
|
|
|
655
668
|
};
|
|
656
669
|
}
|
|
657
670
|
async handleResult(result) {
|
|
658
|
-
const
|
|
671
|
+
const context = {
|
|
672
|
+
logger: this.logger,
|
|
673
|
+
catalogIdentityClient: this.catalogIdentityClient,
|
|
674
|
+
tokenIssuer: this.tokenIssuer
|
|
675
|
+
};
|
|
676
|
+
const { profile } = await this.authHandler(result, context);
|
|
659
677
|
const response = {
|
|
660
678
|
providerInfo: {
|
|
661
679
|
idToken: result.params.id_token,
|
|
@@ -669,11 +687,7 @@ class AtlassianAuthProvider {
|
|
|
669
687
|
response.backstageIdentity = await this.signInResolver({
|
|
670
688
|
result,
|
|
671
689
|
profile
|
|
672
|
-
},
|
|
673
|
-
tokenIssuer: this.tokenIssuer,
|
|
674
|
-
catalogIdentityClient: this.catalogIdentityClient,
|
|
675
|
-
logger: this.logger
|
|
676
|
-
});
|
|
690
|
+
}, context);
|
|
677
691
|
}
|
|
678
692
|
return response;
|
|
679
693
|
}
|
|
@@ -696,6 +710,7 @@ const createAtlassianProvider = (options) => {
|
|
|
696
710
|
globalConfig,
|
|
697
711
|
config,
|
|
698
712
|
tokenIssuer,
|
|
713
|
+
tokenManager,
|
|
699
714
|
catalogApi,
|
|
700
715
|
logger
|
|
701
716
|
}) => OAuthEnvironmentHandler.mapConfig(config, (envConfig) => {
|
|
@@ -706,7 +721,7 @@ const createAtlassianProvider = (options) => {
|
|
|
706
721
|
const callbackUrl = `${globalConfig.baseUrl}/${providerId}/handler/frame`;
|
|
707
722
|
const catalogIdentityClient = new CatalogIdentityClient({
|
|
708
723
|
catalogApi,
|
|
709
|
-
|
|
724
|
+
tokenManager
|
|
710
725
|
});
|
|
711
726
|
const authHandler = (_a = options == null ? void 0 : options.authHandler) != null ? _a : atlassianDefaultAuthHandler;
|
|
712
727
|
const provider = new AtlassianAuthProvider({
|
|
@@ -793,7 +808,12 @@ class Auth0AuthProvider {
|
|
|
793
808
|
};
|
|
794
809
|
}
|
|
795
810
|
async handleResult(result) {
|
|
796
|
-
const
|
|
811
|
+
const context = {
|
|
812
|
+
logger: this.logger,
|
|
813
|
+
catalogIdentityClient: this.catalogIdentityClient,
|
|
814
|
+
tokenIssuer: this.tokenIssuer
|
|
815
|
+
};
|
|
816
|
+
const { profile } = await this.authHandler(result, context);
|
|
797
817
|
const response = {
|
|
798
818
|
providerInfo: {
|
|
799
819
|
idToken: result.params.id_token,
|
|
@@ -807,11 +827,7 @@ class Auth0AuthProvider {
|
|
|
807
827
|
response.backstageIdentity = await this.signInResolver({
|
|
808
828
|
result,
|
|
809
829
|
profile
|
|
810
|
-
},
|
|
811
|
-
tokenIssuer: this.tokenIssuer,
|
|
812
|
-
catalogIdentityClient: this.catalogIdentityClient,
|
|
813
|
-
logger: this.logger
|
|
814
|
-
});
|
|
830
|
+
}, context);
|
|
815
831
|
}
|
|
816
832
|
return response;
|
|
817
833
|
}
|
|
@@ -830,6 +846,7 @@ const createAuth0Provider = (options) => {
|
|
|
830
846
|
globalConfig,
|
|
831
847
|
config,
|
|
832
848
|
tokenIssuer,
|
|
849
|
+
tokenManager,
|
|
833
850
|
catalogApi,
|
|
834
851
|
logger
|
|
835
852
|
}) => OAuthEnvironmentHandler.mapConfig(config, (envConfig) => {
|
|
@@ -840,7 +857,7 @@ const createAuth0Provider = (options) => {
|
|
|
840
857
|
const callbackUrl = `${globalConfig.baseUrl}/${providerId}/handler/frame`;
|
|
841
858
|
const catalogIdentityClient = new CatalogIdentityClient({
|
|
842
859
|
catalogApi,
|
|
843
|
-
|
|
860
|
+
tokenManager
|
|
844
861
|
});
|
|
845
862
|
const authHandler = (options == null ? void 0 : options.authHandler) ? options.authHandler : async ({ fullProfile, params }) => ({
|
|
846
863
|
profile: makeProfileInfo(fullProfile, params.id_token)
|
|
@@ -937,15 +954,16 @@ class AwsAlbAuthProvider {
|
|
|
937
954
|
}
|
|
938
955
|
}
|
|
939
956
|
async handleResult(result) {
|
|
940
|
-
const
|
|
941
|
-
const backstageIdentity = await this.signInResolver({
|
|
942
|
-
result,
|
|
943
|
-
profile
|
|
944
|
-
}, {
|
|
957
|
+
const context = {
|
|
945
958
|
tokenIssuer: this.tokenIssuer,
|
|
946
959
|
catalogIdentityClient: this.catalogIdentityClient,
|
|
947
960
|
logger: this.logger
|
|
948
|
-
}
|
|
961
|
+
};
|
|
962
|
+
const { profile } = await this.authHandler(result, context);
|
|
963
|
+
const backstageIdentity = await this.signInResolver({
|
|
964
|
+
result,
|
|
965
|
+
profile
|
|
966
|
+
}, context);
|
|
949
967
|
return {
|
|
950
968
|
providerInfo: {
|
|
951
969
|
accessToken: result.accessToken,
|
|
@@ -967,7 +985,7 @@ class AwsAlbAuthProvider {
|
|
|
967
985
|
}
|
|
968
986
|
}
|
|
969
987
|
const createAwsAlbProvider = (options) => {
|
|
970
|
-
return ({ config, tokenIssuer, catalogApi, logger }) => {
|
|
988
|
+
return ({ config, tokenIssuer, catalogApi, logger, tokenManager }) => {
|
|
971
989
|
const region = config.getString("region");
|
|
972
990
|
const issuer = config.getOptionalString("iss");
|
|
973
991
|
if ((options == null ? void 0 : options.signIn.resolver) === void 0) {
|
|
@@ -975,7 +993,7 @@ const createAwsAlbProvider = (options) => {
|
|
|
975
993
|
}
|
|
976
994
|
const catalogIdentityClient = new CatalogIdentityClient({
|
|
977
995
|
catalogApi,
|
|
978
|
-
|
|
996
|
+
tokenManager
|
|
979
997
|
});
|
|
980
998
|
const authHandler = (options == null ? void 0 : options.authHandler) ? options.authHandler : async ({ fullProfile }) => ({
|
|
981
999
|
profile: makeProfileInfo(fullProfile)
|
|
@@ -1045,7 +1063,12 @@ class BitbucketAuthProvider {
|
|
|
1045
1063
|
}
|
|
1046
1064
|
async handleResult(result) {
|
|
1047
1065
|
result.fullProfile.avatarUrl = result.fullProfile._json.links.avatar.href;
|
|
1048
|
-
const
|
|
1066
|
+
const context = {
|
|
1067
|
+
logger: this.logger,
|
|
1068
|
+
catalogIdentityClient: this.catalogIdentityClient,
|
|
1069
|
+
tokenIssuer: this.tokenIssuer
|
|
1070
|
+
};
|
|
1071
|
+
const { profile } = await this.authHandler(result, context);
|
|
1049
1072
|
const response = {
|
|
1050
1073
|
providerInfo: {
|
|
1051
1074
|
idToken: result.params.id_token,
|
|
@@ -1059,11 +1082,7 @@ class BitbucketAuthProvider {
|
|
|
1059
1082
|
response.backstageIdentity = await this.signInResolver({
|
|
1060
1083
|
result,
|
|
1061
1084
|
profile
|
|
1062
|
-
},
|
|
1063
|
-
tokenIssuer: this.tokenIssuer,
|
|
1064
|
-
catalogIdentityClient: this.catalogIdentityClient,
|
|
1065
|
-
logger: this.logger
|
|
1066
|
-
});
|
|
1085
|
+
}, context);
|
|
1067
1086
|
}
|
|
1068
1087
|
return response;
|
|
1069
1088
|
}
|
|
@@ -1102,6 +1121,7 @@ const createBitbucketProvider = (options) => {
|
|
|
1102
1121
|
globalConfig,
|
|
1103
1122
|
config,
|
|
1104
1123
|
tokenIssuer,
|
|
1124
|
+
tokenManager,
|
|
1105
1125
|
catalogApi,
|
|
1106
1126
|
logger
|
|
1107
1127
|
}) => OAuthEnvironmentHandler.mapConfig(config, (envConfig) => {
|
|
@@ -1111,7 +1131,7 @@ const createBitbucketProvider = (options) => {
|
|
|
1111
1131
|
const callbackUrl = `${globalConfig.baseUrl}/${providerId}/handler/frame`;
|
|
1112
1132
|
const catalogIdentityClient = new CatalogIdentityClient({
|
|
1113
1133
|
catalogApi,
|
|
1114
|
-
|
|
1134
|
+
tokenManager
|
|
1115
1135
|
});
|
|
1116
1136
|
const authHandler = (options == null ? void 0 : options.authHandler) ? options.authHandler : async ({ fullProfile, params }) => ({
|
|
1117
1137
|
profile: makeProfileInfo(fullProfile, params.id_token)
|
|
@@ -1179,7 +1199,12 @@ class GithubAuthProvider {
|
|
|
1179
1199
|
};
|
|
1180
1200
|
}
|
|
1181
1201
|
async handleResult(result) {
|
|
1182
|
-
const
|
|
1202
|
+
const context = {
|
|
1203
|
+
logger: this.logger,
|
|
1204
|
+
catalogIdentityClient: this.catalogIdentityClient,
|
|
1205
|
+
tokenIssuer: this.tokenIssuer
|
|
1206
|
+
};
|
|
1207
|
+
const { profile } = await this.authHandler(result, context);
|
|
1183
1208
|
const expiresInStr = result.params.expires_in;
|
|
1184
1209
|
const response = {
|
|
1185
1210
|
providerInfo: {
|
|
@@ -1193,11 +1218,7 @@ class GithubAuthProvider {
|
|
|
1193
1218
|
response.backstageIdentity = await this.signInResolver({
|
|
1194
1219
|
result,
|
|
1195
1220
|
profile
|
|
1196
|
-
},
|
|
1197
|
-
tokenIssuer: this.tokenIssuer,
|
|
1198
|
-
catalogIdentityClient: this.catalogIdentityClient,
|
|
1199
|
-
logger: this.logger
|
|
1200
|
-
});
|
|
1221
|
+
}, context);
|
|
1201
1222
|
}
|
|
1202
1223
|
return response;
|
|
1203
1224
|
}
|
|
@@ -1206,7 +1227,10 @@ const githubDefaultSignInResolver = async (info, ctx) => {
|
|
|
1206
1227
|
const { fullProfile } = info.result;
|
|
1207
1228
|
const userId = fullProfile.username || fullProfile.id;
|
|
1208
1229
|
const token = await ctx.tokenIssuer.issueToken({
|
|
1209
|
-
claims: {
|
|
1230
|
+
claims: {
|
|
1231
|
+
sub: `user:default/${userId}`,
|
|
1232
|
+
ent: [`user:default/${userId}`]
|
|
1233
|
+
}
|
|
1210
1234
|
});
|
|
1211
1235
|
return { id: userId, token };
|
|
1212
1236
|
};
|
|
@@ -1216,6 +1240,7 @@ const createGithubProvider = (options) => {
|
|
|
1216
1240
|
globalConfig,
|
|
1217
1241
|
config,
|
|
1218
1242
|
tokenIssuer,
|
|
1243
|
+
tokenManager,
|
|
1219
1244
|
catalogApi,
|
|
1220
1245
|
logger
|
|
1221
1246
|
}) => OAuthEnvironmentHandler.mapConfig(config, (envConfig) => {
|
|
@@ -1230,7 +1255,7 @@ const createGithubProvider = (options) => {
|
|
|
1230
1255
|
const callbackUrl = customCallbackUrl || `${globalConfig.baseUrl}/${providerId}/handler/frame`;
|
|
1231
1256
|
const catalogIdentityClient = new CatalogIdentityClient({
|
|
1232
1257
|
catalogApi,
|
|
1233
|
-
|
|
1258
|
+
tokenManager
|
|
1234
1259
|
});
|
|
1235
1260
|
const authHandler = (options == null ? void 0 : options.authHandler) ? options.authHandler : async ({ fullProfile }) => ({
|
|
1236
1261
|
profile: makeProfileInfo(fullProfile)
|
|
@@ -1261,7 +1286,8 @@ const createGithubProvider = (options) => {
|
|
|
1261
1286
|
return OAuthAdapter.fromConfig(globalConfig, provider, {
|
|
1262
1287
|
persistScopes: true,
|
|
1263
1288
|
providerId,
|
|
1264
|
-
tokenIssuer
|
|
1289
|
+
tokenIssuer,
|
|
1290
|
+
callbackUrl
|
|
1265
1291
|
});
|
|
1266
1292
|
});
|
|
1267
1293
|
};
|
|
@@ -1273,7 +1299,7 @@ const gitlabDefaultSignInResolver = async (info, ctx) => {
|
|
|
1273
1299
|
id = profile.email.split("@")[0];
|
|
1274
1300
|
}
|
|
1275
1301
|
const token = await ctx.tokenIssuer.issueToken({
|
|
1276
|
-
claims: { sub: id
|
|
1302
|
+
claims: { sub: `user:default/${id}`, ent: [`user:default/${id}`] }
|
|
1277
1303
|
});
|
|
1278
1304
|
return { id, token };
|
|
1279
1305
|
};
|
|
@@ -1327,7 +1353,12 @@ class GitlabAuthProvider {
|
|
|
1327
1353
|
};
|
|
1328
1354
|
}
|
|
1329
1355
|
async handleResult(result) {
|
|
1330
|
-
const
|
|
1356
|
+
const context = {
|
|
1357
|
+
logger: this.logger,
|
|
1358
|
+
catalogIdentityClient: this.catalogIdentityClient,
|
|
1359
|
+
tokenIssuer: this.tokenIssuer
|
|
1360
|
+
};
|
|
1361
|
+
const { profile } = await this.authHandler(result, context);
|
|
1331
1362
|
const response = {
|
|
1332
1363
|
providerInfo: {
|
|
1333
1364
|
idToken: result.params.id_token,
|
|
@@ -1341,11 +1372,7 @@ class GitlabAuthProvider {
|
|
|
1341
1372
|
response.backstageIdentity = await this.signInResolver({
|
|
1342
1373
|
result,
|
|
1343
1374
|
profile
|
|
1344
|
-
},
|
|
1345
|
-
tokenIssuer: this.tokenIssuer,
|
|
1346
|
-
catalogIdentityClient: this.catalogIdentityClient,
|
|
1347
|
-
logger: this.logger
|
|
1348
|
-
});
|
|
1375
|
+
}, context);
|
|
1349
1376
|
}
|
|
1350
1377
|
return response;
|
|
1351
1378
|
}
|
|
@@ -1356,6 +1383,7 @@ const createGitlabProvider = (options) => {
|
|
|
1356
1383
|
globalConfig,
|
|
1357
1384
|
config,
|
|
1358
1385
|
tokenIssuer,
|
|
1386
|
+
tokenManager,
|
|
1359
1387
|
catalogApi,
|
|
1360
1388
|
logger
|
|
1361
1389
|
}) => OAuthEnvironmentHandler.mapConfig(config, (envConfig) => {
|
|
@@ -1367,7 +1395,7 @@ const createGitlabProvider = (options) => {
|
|
|
1367
1395
|
const callbackUrl = `${globalConfig.baseUrl}/${providerId}/handler/frame`;
|
|
1368
1396
|
const catalogIdentityClient = new CatalogIdentityClient({
|
|
1369
1397
|
catalogApi,
|
|
1370
|
-
|
|
1398
|
+
tokenManager
|
|
1371
1399
|
});
|
|
1372
1400
|
const authHandler = (_a = options == null ? void 0 : options.authHandler) != null ? _a : gitlabDefaultAuthHandler;
|
|
1373
1401
|
const signInResolverFn = (_c = (_b = options == null ? void 0 : options.signIn) == null ? void 0 : _b.resolver) != null ? _c : gitlabDefaultSignInResolver;
|
|
@@ -1446,7 +1474,12 @@ class GoogleAuthProvider {
|
|
|
1446
1474
|
};
|
|
1447
1475
|
}
|
|
1448
1476
|
async handleResult(result) {
|
|
1449
|
-
const
|
|
1477
|
+
const context = {
|
|
1478
|
+
logger: this.logger,
|
|
1479
|
+
catalogIdentityClient: this.catalogIdentityClient,
|
|
1480
|
+
tokenIssuer: this.tokenIssuer
|
|
1481
|
+
};
|
|
1482
|
+
const { profile } = await this.authHandler(result, context);
|
|
1450
1483
|
const response = {
|
|
1451
1484
|
providerInfo: {
|
|
1452
1485
|
idToken: result.params.id_token,
|
|
@@ -1460,11 +1493,7 @@ class GoogleAuthProvider {
|
|
|
1460
1493
|
response.backstageIdentity = await this.signInResolver({
|
|
1461
1494
|
result,
|
|
1462
1495
|
profile
|
|
1463
|
-
},
|
|
1464
|
-
tokenIssuer: this.tokenIssuer,
|
|
1465
|
-
catalogIdentityClient: this.catalogIdentityClient,
|
|
1466
|
-
logger: this.logger
|
|
1467
|
-
});
|
|
1496
|
+
}, context);
|
|
1468
1497
|
}
|
|
1469
1498
|
return response;
|
|
1470
1499
|
}
|
|
@@ -1501,7 +1530,7 @@ const googleDefaultSignInResolver = async (info, ctx) => {
|
|
|
1501
1530
|
userId = profile.email.split("@")[0];
|
|
1502
1531
|
}
|
|
1503
1532
|
const token = await ctx.tokenIssuer.issueToken({
|
|
1504
|
-
claims: { sub: userId
|
|
1533
|
+
claims: { sub: `user:default/${userId}`, ent: [`user:default/${userId}`] }
|
|
1505
1534
|
});
|
|
1506
1535
|
return { id: userId, token };
|
|
1507
1536
|
};
|
|
@@ -1511,6 +1540,7 @@ const createGoogleProvider = (options) => {
|
|
|
1511
1540
|
globalConfig,
|
|
1512
1541
|
config,
|
|
1513
1542
|
tokenIssuer,
|
|
1543
|
+
tokenManager,
|
|
1514
1544
|
catalogApi,
|
|
1515
1545
|
logger
|
|
1516
1546
|
}) => OAuthEnvironmentHandler.mapConfig(config, (envConfig) => {
|
|
@@ -1520,7 +1550,7 @@ const createGoogleProvider = (options) => {
|
|
|
1520
1550
|
const callbackUrl = `${globalConfig.baseUrl}/${providerId}/handler/frame`;
|
|
1521
1551
|
const catalogIdentityClient = new CatalogIdentityClient({
|
|
1522
1552
|
catalogApi,
|
|
1523
|
-
|
|
1553
|
+
tokenManager
|
|
1524
1554
|
});
|
|
1525
1555
|
const authHandler = (options == null ? void 0 : options.authHandler) ? options.authHandler : async ({ fullProfile, params }) => ({
|
|
1526
1556
|
profile: makeProfileInfo(fullProfile, params.id_token)
|
|
@@ -1595,7 +1625,12 @@ class MicrosoftAuthProvider {
|
|
|
1595
1625
|
async handleResult(result) {
|
|
1596
1626
|
const photo = await this.getUserPhoto(result.accessToken);
|
|
1597
1627
|
result.fullProfile.photos = photo ? [{ value: photo }] : void 0;
|
|
1598
|
-
const
|
|
1628
|
+
const context = {
|
|
1629
|
+
logger: this.logger,
|
|
1630
|
+
catalogIdentityClient: this.catalogIdentityClient,
|
|
1631
|
+
tokenIssuer: this.tokenIssuer
|
|
1632
|
+
};
|
|
1633
|
+
const { profile } = await this.authHandler(result, context);
|
|
1599
1634
|
const response = {
|
|
1600
1635
|
providerInfo: {
|
|
1601
1636
|
idToken: result.params.id_token,
|
|
@@ -1609,11 +1644,7 @@ class MicrosoftAuthProvider {
|
|
|
1609
1644
|
response.backstageIdentity = await this.signInResolver({
|
|
1610
1645
|
result,
|
|
1611
1646
|
profile
|
|
1612
|
-
},
|
|
1613
|
-
tokenIssuer: this.tokenIssuer,
|
|
1614
|
-
catalogIdentityClient: this.catalogIdentityClient,
|
|
1615
|
-
logger: this.logger
|
|
1616
|
-
});
|
|
1647
|
+
}, context);
|
|
1617
1648
|
}
|
|
1618
1649
|
return response;
|
|
1619
1650
|
}
|
|
@@ -1654,7 +1685,10 @@ const microsoftDefaultSignInResolver = async (info, ctx) => {
|
|
|
1654
1685
|
}
|
|
1655
1686
|
const userId = profile.email.split("@")[0];
|
|
1656
1687
|
const token = await ctx.tokenIssuer.issueToken({
|
|
1657
|
-
claims: {
|
|
1688
|
+
claims: {
|
|
1689
|
+
sub: `user:default/${userId}`,
|
|
1690
|
+
ent: [`user:default/${userId}`]
|
|
1691
|
+
}
|
|
1658
1692
|
});
|
|
1659
1693
|
return { id: userId, token };
|
|
1660
1694
|
};
|
|
@@ -1664,6 +1698,7 @@ const createMicrosoftProvider = (options) => {
|
|
|
1664
1698
|
globalConfig,
|
|
1665
1699
|
config,
|
|
1666
1700
|
tokenIssuer,
|
|
1701
|
+
tokenManager,
|
|
1667
1702
|
catalogApi,
|
|
1668
1703
|
logger
|
|
1669
1704
|
}) => OAuthEnvironmentHandler.mapConfig(config, (envConfig) => {
|
|
@@ -1676,7 +1711,7 @@ const createMicrosoftProvider = (options) => {
|
|
|
1676
1711
|
const tokenUrl = `https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/token`;
|
|
1677
1712
|
const catalogIdentityClient = new CatalogIdentityClient({
|
|
1678
1713
|
catalogApi,
|
|
1679
|
-
|
|
1714
|
+
tokenManager
|
|
1680
1715
|
});
|
|
1681
1716
|
const authHandler = (options == null ? void 0 : options.authHandler) ? options.authHandler : async ({ fullProfile, params }) => ({
|
|
1682
1717
|
profile: makeProfileInfo(fullProfile, params.id_token)
|
|
@@ -1765,7 +1800,12 @@ class OAuth2AuthProvider {
|
|
|
1765
1800
|
};
|
|
1766
1801
|
}
|
|
1767
1802
|
async handleResult(result) {
|
|
1768
|
-
const
|
|
1803
|
+
const context = {
|
|
1804
|
+
logger: this.logger,
|
|
1805
|
+
catalogIdentityClient: this.catalogIdentityClient,
|
|
1806
|
+
tokenIssuer: this.tokenIssuer
|
|
1807
|
+
};
|
|
1808
|
+
const { profile } = await this.authHandler(result, context);
|
|
1769
1809
|
const response = {
|
|
1770
1810
|
providerInfo: {
|
|
1771
1811
|
idToken: result.params.id_token,
|
|
@@ -1779,11 +1819,7 @@ class OAuth2AuthProvider {
|
|
|
1779
1819
|
response.backstageIdentity = await this.signInResolver({
|
|
1780
1820
|
result,
|
|
1781
1821
|
profile
|
|
1782
|
-
},
|
|
1783
|
-
tokenIssuer: this.tokenIssuer,
|
|
1784
|
-
catalogIdentityClient: this.catalogIdentityClient,
|
|
1785
|
-
logger: this.logger
|
|
1786
|
-
});
|
|
1822
|
+
}, context);
|
|
1787
1823
|
}
|
|
1788
1824
|
return response;
|
|
1789
1825
|
}
|
|
@@ -1798,7 +1834,7 @@ const oAuth2DefaultSignInResolver$1 = async (info, ctx) => {
|
|
|
1798
1834
|
}
|
|
1799
1835
|
const userId = profile.email.split("@")[0];
|
|
1800
1836
|
const token = await ctx.tokenIssuer.issueToken({
|
|
1801
|
-
claims: { sub: userId
|
|
1837
|
+
claims: { sub: `user:default/${userId}`, ent: [`user:default/${userId}`] }
|
|
1802
1838
|
});
|
|
1803
1839
|
return { id: userId, token };
|
|
1804
1840
|
};
|
|
@@ -1808,6 +1844,7 @@ const createOAuth2Provider = (options) => {
|
|
|
1808
1844
|
globalConfig,
|
|
1809
1845
|
config,
|
|
1810
1846
|
tokenIssuer,
|
|
1847
|
+
tokenManager,
|
|
1811
1848
|
catalogApi,
|
|
1812
1849
|
logger
|
|
1813
1850
|
}) => OAuthEnvironmentHandler.mapConfig(config, (envConfig) => {
|
|
@@ -1822,7 +1859,7 @@ const createOAuth2Provider = (options) => {
|
|
|
1822
1859
|
const disableRefresh = (_a = envConfig.getOptionalBoolean("disableRefresh")) != null ? _a : false;
|
|
1823
1860
|
const catalogIdentityClient = new CatalogIdentityClient({
|
|
1824
1861
|
catalogApi,
|
|
1825
|
-
|
|
1862
|
+
tokenManager
|
|
1826
1863
|
});
|
|
1827
1864
|
const authHandler = (options == null ? void 0 : options.authHandler) ? options.authHandler : async ({ fullProfile, params }) => ({
|
|
1828
1865
|
profile: makeProfileInfo(fullProfile, params.id_token)
|
|
@@ -1855,107 +1892,525 @@ const createOAuth2Provider = (options) => {
|
|
|
1855
1892
|
});
|
|
1856
1893
|
};
|
|
1857
1894
|
|
|
1858
|
-
|
|
1895
|
+
function createOidcRouter(options) {
|
|
1896
|
+
const { baseUrl, tokenIssuer } = options;
|
|
1897
|
+
const router = Router__default["default"]();
|
|
1898
|
+
const config = {
|
|
1899
|
+
issuer: baseUrl,
|
|
1900
|
+
token_endpoint: `${baseUrl}/v1/token`,
|
|
1901
|
+
userinfo_endpoint: `${baseUrl}/v1/userinfo`,
|
|
1902
|
+
jwks_uri: `${baseUrl}/.well-known/jwks.json`,
|
|
1903
|
+
response_types_supported: ["id_token"],
|
|
1904
|
+
subject_types_supported: ["public"],
|
|
1905
|
+
id_token_signing_alg_values_supported: ["RS256"],
|
|
1906
|
+
scopes_supported: ["openid"],
|
|
1907
|
+
token_endpoint_auth_methods_supported: [],
|
|
1908
|
+
claims_supported: ["sub"],
|
|
1909
|
+
grant_types_supported: []
|
|
1910
|
+
};
|
|
1911
|
+
router.get("/.well-known/openid-configuration", (_req, res) => {
|
|
1912
|
+
res.json(config);
|
|
1913
|
+
});
|
|
1914
|
+
router.get("/.well-known/jwks.json", async (_req, res) => {
|
|
1915
|
+
const { keys } = await tokenIssuer.listPublicKeys();
|
|
1916
|
+
res.json({ keys });
|
|
1917
|
+
});
|
|
1918
|
+
router.get("/v1/token", (_req, res) => {
|
|
1919
|
+
res.status(501).send("Not Implemented");
|
|
1920
|
+
});
|
|
1921
|
+
router.get("/v1/userinfo", (_req, res) => {
|
|
1922
|
+
res.status(501).send("Not Implemented");
|
|
1923
|
+
});
|
|
1924
|
+
return router;
|
|
1925
|
+
}
|
|
1926
|
+
|
|
1927
|
+
const CLOCK_MARGIN_S = 10;
|
|
1928
|
+
class IdentityClient {
|
|
1859
1929
|
constructor(options) {
|
|
1860
|
-
this.
|
|
1861
|
-
this.
|
|
1862
|
-
this.
|
|
1863
|
-
this.
|
|
1864
|
-
this.authHandler = options.authHandler;
|
|
1865
|
-
this.tokenIssuer = options.tokenIssuer;
|
|
1866
|
-
this.catalogIdentityClient = options.catalogIdentityClient;
|
|
1867
|
-
this.logger = options.logger;
|
|
1930
|
+
this.discovery = options.discovery;
|
|
1931
|
+
this.issuer = options.issuer;
|
|
1932
|
+
this.keyStore = new jose.JWKS.KeyStore();
|
|
1933
|
+
this.keyStoreUpdated = 0;
|
|
1868
1934
|
}
|
|
1869
|
-
async
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
state: encodeState(req.state)
|
|
1874
|
-
};
|
|
1875
|
-
const prompt = this.prompt || "none";
|
|
1876
|
-
if (prompt !== "auto") {
|
|
1877
|
-
options.prompt = prompt;
|
|
1935
|
+
async authenticate(token) {
|
|
1936
|
+
var _a;
|
|
1937
|
+
if (!token) {
|
|
1938
|
+
throw new errors.AuthenticationError("No token specified");
|
|
1878
1939
|
}
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
const
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1940
|
+
const key = await this.getKey(token);
|
|
1941
|
+
if (!key) {
|
|
1942
|
+
throw new errors.AuthenticationError("No signing key matching token found");
|
|
1943
|
+
}
|
|
1944
|
+
const decoded = jose.JWT.IdToken.verify(token, key, {
|
|
1945
|
+
algorithms: ["ES256"],
|
|
1946
|
+
audience: "backstage",
|
|
1947
|
+
issuer: this.issuer
|
|
1948
|
+
});
|
|
1949
|
+
if (!decoded.sub) {
|
|
1950
|
+
throw new errors.AuthenticationError("No user sub found in token");
|
|
1951
|
+
}
|
|
1952
|
+
const user = {
|
|
1953
|
+
id: decoded.sub,
|
|
1954
|
+
token,
|
|
1955
|
+
identity: {
|
|
1956
|
+
type: "user",
|
|
1957
|
+
userEntityRef: decoded.sub,
|
|
1958
|
+
ownershipEntityRefs: (_a = decoded.ent) != null ? _a : []
|
|
1959
|
+
}
|
|
1887
1960
|
};
|
|
1961
|
+
return user;
|
|
1888
1962
|
}
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
if (!tokenset.access_token) {
|
|
1893
|
-
throw new Error("Refresh failed");
|
|
1963
|
+
static getBearerToken(authorizationHeader) {
|
|
1964
|
+
if (typeof authorizationHeader !== "string") {
|
|
1965
|
+
return void 0;
|
|
1894
1966
|
}
|
|
1895
|
-
const
|
|
1896
|
-
return
|
|
1897
|
-
response: await this.handleResult({ tokenset, userinfo }),
|
|
1898
|
-
refreshToken: tokenset.refresh_token
|
|
1899
|
-
};
|
|
1967
|
+
const matches = authorizationHeader.match(/Bearer\s+(\S+)/i);
|
|
1968
|
+
return matches == null ? void 0 : matches[1];
|
|
1900
1969
|
}
|
|
1901
|
-
async
|
|
1902
|
-
const
|
|
1903
|
-
|
|
1904
|
-
access_type: "offline",
|
|
1905
|
-
client_id: options.clientId,
|
|
1906
|
-
client_secret: options.clientSecret,
|
|
1907
|
-
redirect_uris: [options.callbackUrl],
|
|
1908
|
-
response_types: ["code"],
|
|
1909
|
-
id_token_signed_response_alg: options.tokenSignedResponseAlg || "RS256",
|
|
1910
|
-
scope: options.scope || ""
|
|
1911
|
-
});
|
|
1912
|
-
const strategy = new openidClient.Strategy({
|
|
1913
|
-
client,
|
|
1914
|
-
passReqToCallback: false
|
|
1915
|
-
}, (tokenset, userinfo, done) => {
|
|
1916
|
-
if (typeof done !== "function") {
|
|
1917
|
-
throw new Error("OIDC IdP must provide a userinfo_endpoint in the metadata response");
|
|
1918
|
-
}
|
|
1919
|
-
done(void 0, { tokenset, userinfo }, {
|
|
1920
|
-
refreshToken: tokenset.refresh_token
|
|
1921
|
-
});
|
|
1970
|
+
async getKey(rawJwtToken) {
|
|
1971
|
+
const { header, payload } = jose.JWT.decode(rawJwtToken, {
|
|
1972
|
+
complete: true
|
|
1922
1973
|
});
|
|
1923
|
-
|
|
1924
|
-
|
|
1974
|
+
const keyStoreHasKey = !!this.keyStore.get({ kid: header.kid });
|
|
1975
|
+
const issuedAfterLastRefresh = (payload == null ? void 0 : payload.iat) && payload.iat > this.keyStoreUpdated - CLOCK_MARGIN_S;
|
|
1976
|
+
if (!keyStoreHasKey && issuedAfterLastRefresh) {
|
|
1977
|
+
await this.refreshKeyStore();
|
|
1978
|
+
}
|
|
1979
|
+
return this.keyStore.get({ kid: header.kid });
|
|
1925
1980
|
}
|
|
1926
|
-
async
|
|
1927
|
-
const
|
|
1928
|
-
const response =
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
expiresInSeconds: result.tokenset.expires_in
|
|
1934
|
-
},
|
|
1935
|
-
profile
|
|
1936
|
-
};
|
|
1937
|
-
if (this.signInResolver) {
|
|
1938
|
-
response.backstageIdentity = await this.signInResolver({
|
|
1939
|
-
result,
|
|
1940
|
-
profile
|
|
1941
|
-
}, {
|
|
1942
|
-
tokenIssuer: this.tokenIssuer,
|
|
1943
|
-
catalogIdentityClient: this.catalogIdentityClient,
|
|
1944
|
-
logger: this.logger
|
|
1945
|
-
});
|
|
1981
|
+
async listPublicKeys() {
|
|
1982
|
+
const url = `${await this.discovery.getBaseUrl("auth")}/.well-known/jwks.json`;
|
|
1983
|
+
const response = await fetch__default["default"](url);
|
|
1984
|
+
if (!response.ok) {
|
|
1985
|
+
const payload = await response.text();
|
|
1986
|
+
const message = `Request failed with ${response.status} ${response.statusText}, ${payload}`;
|
|
1987
|
+
throw new Error(message);
|
|
1946
1988
|
}
|
|
1947
|
-
|
|
1989
|
+
const publicKeys = await response.json();
|
|
1990
|
+
return publicKeys;
|
|
1948
1991
|
}
|
|
1949
|
-
|
|
1950
|
-
const
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1992
|
+
async refreshKeyStore() {
|
|
1993
|
+
const now = Date.now() / 1e3;
|
|
1994
|
+
const publicKeys = await this.listPublicKeys();
|
|
1995
|
+
this.keyStore = jose.JWKS.asKeyStore({
|
|
1996
|
+
keys: publicKeys.keys.map((key) => key)
|
|
1997
|
+
});
|
|
1998
|
+
this.keyStoreUpdated = now;
|
|
1954
1999
|
}
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
2000
|
+
}
|
|
2001
|
+
|
|
2002
|
+
const MS_IN_S = 1e3;
|
|
2003
|
+
class TokenFactory {
|
|
2004
|
+
constructor(options) {
|
|
2005
|
+
this.issuer = options.issuer;
|
|
2006
|
+
this.logger = options.logger;
|
|
2007
|
+
this.keyStore = options.keyStore;
|
|
2008
|
+
this.keyDurationSeconds = options.keyDurationSeconds;
|
|
2009
|
+
}
|
|
2010
|
+
async issueToken(params) {
|
|
2011
|
+
const key = await this.getKey();
|
|
2012
|
+
const iss = this.issuer;
|
|
2013
|
+
const sub = params.claims.sub;
|
|
2014
|
+
const ent = params.claims.ent;
|
|
2015
|
+
const aud = "backstage";
|
|
2016
|
+
const iat = Math.floor(Date.now() / MS_IN_S);
|
|
2017
|
+
const exp = iat + this.keyDurationSeconds;
|
|
2018
|
+
this.logger.info(`Issuing token for ${sub}, with entities ${ent != null ? ent : []}`);
|
|
2019
|
+
return jose.JWS.sign({ iss, sub, aud, iat, exp, ent }, key, {
|
|
2020
|
+
alg: key.alg,
|
|
2021
|
+
kid: key.kid
|
|
2022
|
+
});
|
|
2023
|
+
}
|
|
2024
|
+
async listPublicKeys() {
|
|
2025
|
+
const { items: keys } = await this.keyStore.listKeys();
|
|
2026
|
+
const validKeys = [];
|
|
2027
|
+
const expiredKeys = [];
|
|
2028
|
+
for (const key of keys) {
|
|
2029
|
+
const expireAt = luxon.DateTime.fromJSDate(key.createdAt).plus({
|
|
2030
|
+
seconds: 3 * this.keyDurationSeconds
|
|
2031
|
+
});
|
|
2032
|
+
if (expireAt < luxon.DateTime.local()) {
|
|
2033
|
+
expiredKeys.push(key);
|
|
2034
|
+
} else {
|
|
2035
|
+
validKeys.push(key);
|
|
2036
|
+
}
|
|
2037
|
+
}
|
|
2038
|
+
if (expiredKeys.length > 0) {
|
|
2039
|
+
const kids = expiredKeys.map(({ key }) => key.kid);
|
|
2040
|
+
this.logger.info(`Removing expired signing keys, '${kids.join("', '")}'`);
|
|
2041
|
+
this.keyStore.removeKeys(kids).catch((error) => {
|
|
2042
|
+
this.logger.error(`Failed to remove expired keys, ${error}`);
|
|
2043
|
+
});
|
|
2044
|
+
}
|
|
2045
|
+
return { keys: validKeys.map(({ key }) => key) };
|
|
2046
|
+
}
|
|
2047
|
+
async getKey() {
|
|
2048
|
+
if (this.privateKeyPromise) {
|
|
2049
|
+
if (this.keyExpiry && luxon.DateTime.fromJSDate(this.keyExpiry) > luxon.DateTime.local()) {
|
|
2050
|
+
return this.privateKeyPromise;
|
|
2051
|
+
}
|
|
2052
|
+
this.logger.info(`Signing key has expired, generating new key`);
|
|
2053
|
+
delete this.privateKeyPromise;
|
|
2054
|
+
}
|
|
2055
|
+
this.keyExpiry = luxon.DateTime.utc().plus({
|
|
2056
|
+
seconds: this.keyDurationSeconds
|
|
2057
|
+
}).toJSDate();
|
|
2058
|
+
const promise = (async () => {
|
|
2059
|
+
const key = await jose.JWK.generate("EC", "P-256", {
|
|
2060
|
+
use: "sig",
|
|
2061
|
+
kid: uuid.v4(),
|
|
2062
|
+
alg: "ES256"
|
|
2063
|
+
});
|
|
2064
|
+
this.logger.info(`Created new signing key ${key.kid}`);
|
|
2065
|
+
await this.keyStore.addKey(key.toJWK(false));
|
|
2066
|
+
return key;
|
|
2067
|
+
})();
|
|
2068
|
+
this.privateKeyPromise = promise;
|
|
2069
|
+
try {
|
|
2070
|
+
await promise;
|
|
2071
|
+
} catch (error) {
|
|
2072
|
+
this.logger.error(`Failed to generate new signing key, ${error}`);
|
|
2073
|
+
delete this.keyExpiry;
|
|
2074
|
+
delete this.privateKeyPromise;
|
|
2075
|
+
}
|
|
2076
|
+
return promise;
|
|
2077
|
+
}
|
|
2078
|
+
}
|
|
2079
|
+
|
|
2080
|
+
const migrationsDir = backendCommon.resolvePackagePath("@backstage/plugin-auth-backend", "migrations");
|
|
2081
|
+
const TABLE = "signing_keys";
|
|
2082
|
+
const parseDate = (date) => {
|
|
2083
|
+
const parsedDate = typeof date === "string" ? luxon.DateTime.fromSQL(date, { zone: "UTC" }) : luxon.DateTime.fromJSDate(date);
|
|
2084
|
+
if (!parsedDate.isValid) {
|
|
2085
|
+
throw new Error(`Failed to parse date, reason: ${parsedDate.invalidReason}, explanation: ${parsedDate.invalidExplanation}`);
|
|
2086
|
+
}
|
|
2087
|
+
return parsedDate.toJSDate();
|
|
2088
|
+
};
|
|
2089
|
+
class DatabaseKeyStore {
|
|
2090
|
+
static async create(options) {
|
|
2091
|
+
const { database } = options;
|
|
2092
|
+
await database.migrate.latest({
|
|
2093
|
+
directory: migrationsDir
|
|
2094
|
+
});
|
|
2095
|
+
return new DatabaseKeyStore(options);
|
|
2096
|
+
}
|
|
2097
|
+
constructor(options) {
|
|
2098
|
+
this.database = options.database;
|
|
2099
|
+
}
|
|
2100
|
+
async addKey(key) {
|
|
2101
|
+
await this.database(TABLE).insert({
|
|
2102
|
+
kid: key.kid,
|
|
2103
|
+
key: JSON.stringify(key)
|
|
2104
|
+
});
|
|
2105
|
+
}
|
|
2106
|
+
async listKeys() {
|
|
2107
|
+
const rows = await this.database(TABLE).select();
|
|
2108
|
+
return {
|
|
2109
|
+
items: rows.map((row) => ({
|
|
2110
|
+
key: JSON.parse(row.key),
|
|
2111
|
+
createdAt: parseDate(row.created_at)
|
|
2112
|
+
}))
|
|
2113
|
+
};
|
|
2114
|
+
}
|
|
2115
|
+
async removeKeys(kids) {
|
|
2116
|
+
await this.database(TABLE).delete().whereIn("kid", kids);
|
|
2117
|
+
}
|
|
2118
|
+
}
|
|
2119
|
+
|
|
2120
|
+
class MemoryKeyStore {
|
|
2121
|
+
constructor() {
|
|
2122
|
+
this.keys = /* @__PURE__ */ new Map();
|
|
2123
|
+
}
|
|
2124
|
+
async addKey(key) {
|
|
2125
|
+
this.keys.set(key.kid, {
|
|
2126
|
+
createdAt: luxon.DateTime.utc().toJSDate(),
|
|
2127
|
+
key: JSON.stringify(key)
|
|
2128
|
+
});
|
|
2129
|
+
}
|
|
2130
|
+
async removeKeys(kids) {
|
|
2131
|
+
for (const kid of kids) {
|
|
2132
|
+
this.keys.delete(kid);
|
|
2133
|
+
}
|
|
2134
|
+
}
|
|
2135
|
+
async listKeys() {
|
|
2136
|
+
return {
|
|
2137
|
+
items: Array.from(this.keys).map(([, { createdAt, key: keyStr }]) => ({
|
|
2138
|
+
createdAt,
|
|
2139
|
+
key: JSON.parse(keyStr)
|
|
2140
|
+
}))
|
|
2141
|
+
};
|
|
2142
|
+
}
|
|
2143
|
+
}
|
|
2144
|
+
|
|
2145
|
+
const DEFAULT_TIMEOUT_MS = 1e4;
|
|
2146
|
+
const DEFAULT_DOCUMENT_PATH = "sessions";
|
|
2147
|
+
class FirestoreKeyStore {
|
|
2148
|
+
constructor(database, path, timeout) {
|
|
2149
|
+
this.database = database;
|
|
2150
|
+
this.path = path;
|
|
2151
|
+
this.timeout = timeout;
|
|
2152
|
+
}
|
|
2153
|
+
static async create(settings) {
|
|
2154
|
+
const { path, timeout, ...firestoreSettings } = settings != null ? settings : {};
|
|
2155
|
+
const database = new firestore.Firestore(firestoreSettings);
|
|
2156
|
+
return new FirestoreKeyStore(database, path != null ? path : DEFAULT_DOCUMENT_PATH, timeout != null ? timeout : DEFAULT_TIMEOUT_MS);
|
|
2157
|
+
}
|
|
2158
|
+
static async verifyConnection(keyStore, logger) {
|
|
2159
|
+
try {
|
|
2160
|
+
await keyStore.verify();
|
|
2161
|
+
} catch (error) {
|
|
2162
|
+
if (process.env.NODE_ENV !== "development") {
|
|
2163
|
+
throw new Error(`Failed to connect to database: ${error.message}`);
|
|
2164
|
+
}
|
|
2165
|
+
logger == null ? void 0 : logger.warn(`Failed to connect to database: ${error.message}`);
|
|
2166
|
+
}
|
|
2167
|
+
}
|
|
2168
|
+
async addKey(key) {
|
|
2169
|
+
await this.withTimeout(this.database.collection(this.path).doc(key.kid).set({
|
|
2170
|
+
kid: key.kid,
|
|
2171
|
+
key: JSON.stringify(key)
|
|
2172
|
+
}));
|
|
2173
|
+
}
|
|
2174
|
+
async listKeys() {
|
|
2175
|
+
const keys = await this.withTimeout(this.database.collection(this.path).get());
|
|
2176
|
+
return {
|
|
2177
|
+
items: keys.docs.map((key) => ({
|
|
2178
|
+
key: key.data(),
|
|
2179
|
+
createdAt: key.createTime.toDate()
|
|
2180
|
+
}))
|
|
2181
|
+
};
|
|
2182
|
+
}
|
|
2183
|
+
async removeKeys(kids) {
|
|
2184
|
+
for (const kid of kids) {
|
|
2185
|
+
await this.withTimeout(this.database.collection(this.path).doc(kid).delete());
|
|
2186
|
+
}
|
|
2187
|
+
}
|
|
2188
|
+
async withTimeout(operation) {
|
|
2189
|
+
const timer = new Promise((_, reject) => setTimeout(() => {
|
|
2190
|
+
reject(new Error(`Operation timed out after ${this.timeout}ms`));
|
|
2191
|
+
}, this.timeout));
|
|
2192
|
+
return Promise.race([operation, timer]);
|
|
2193
|
+
}
|
|
2194
|
+
async verify() {
|
|
2195
|
+
await this.withTimeout(this.database.collection(this.path).limit(1).get());
|
|
2196
|
+
}
|
|
2197
|
+
}
|
|
2198
|
+
|
|
2199
|
+
class KeyStores {
|
|
2200
|
+
static async fromConfig(config, options) {
|
|
2201
|
+
var _a;
|
|
2202
|
+
const { logger, database } = options != null ? options : {};
|
|
2203
|
+
const ks = config.getOptionalConfig("auth.keyStore");
|
|
2204
|
+
const provider = (_a = ks == null ? void 0 : ks.getOptionalString("provider")) != null ? _a : "database";
|
|
2205
|
+
logger == null ? void 0 : logger.info(`Configuring "${provider}" as KeyStore provider`);
|
|
2206
|
+
if (provider === "database") {
|
|
2207
|
+
if (!database) {
|
|
2208
|
+
throw new Error("This KeyStore provider requires a database");
|
|
2209
|
+
}
|
|
2210
|
+
return await DatabaseKeyStore.create({
|
|
2211
|
+
database: await database.getClient()
|
|
2212
|
+
});
|
|
2213
|
+
}
|
|
2214
|
+
if (provider === "memory") {
|
|
2215
|
+
return new MemoryKeyStore();
|
|
2216
|
+
}
|
|
2217
|
+
if (provider === "firestore") {
|
|
2218
|
+
const settings = ks == null ? void 0 : ks.getConfig(provider);
|
|
2219
|
+
const keyStore = await FirestoreKeyStore.create(lodash.pickBy({
|
|
2220
|
+
projectId: settings == null ? void 0 : settings.getOptionalString("projectId"),
|
|
2221
|
+
keyFilename: settings == null ? void 0 : settings.getOptionalString("keyFilename"),
|
|
2222
|
+
host: settings == null ? void 0 : settings.getOptionalString("host"),
|
|
2223
|
+
port: settings == null ? void 0 : settings.getOptionalNumber("port"),
|
|
2224
|
+
ssl: settings == null ? void 0 : settings.getOptionalBoolean("ssl"),
|
|
2225
|
+
path: settings == null ? void 0 : settings.getOptionalString("path"),
|
|
2226
|
+
timeout: settings == null ? void 0 : settings.getOptionalNumber("timeout")
|
|
2227
|
+
}, (value) => value !== void 0));
|
|
2228
|
+
await FirestoreKeyStore.verifyConnection(keyStore, logger);
|
|
2229
|
+
return keyStore;
|
|
2230
|
+
}
|
|
2231
|
+
throw new Error(`Unknown KeyStore provider: ${provider}`);
|
|
2232
|
+
}
|
|
2233
|
+
}
|
|
2234
|
+
|
|
2235
|
+
const OAUTH2_PROXY_JWT_HEADER = "X-OAUTH2-PROXY-ID-TOKEN";
|
|
2236
|
+
class Oauth2ProxyAuthProvider {
|
|
2237
|
+
constructor(options) {
|
|
2238
|
+
this.catalogIdentityClient = options.catalogIdentityClient;
|
|
2239
|
+
this.logger = options.logger;
|
|
2240
|
+
this.tokenIssuer = options.tokenIssuer;
|
|
2241
|
+
this.signInResolver = options.signInResolver;
|
|
2242
|
+
this.authHandler = options.authHandler;
|
|
2243
|
+
}
|
|
2244
|
+
frameHandler() {
|
|
2245
|
+
return Promise.resolve(void 0);
|
|
2246
|
+
}
|
|
2247
|
+
async refresh(req, res) {
|
|
2248
|
+
try {
|
|
2249
|
+
const result = this.getResult(req);
|
|
2250
|
+
const response = await this.handleResult(result);
|
|
2251
|
+
res.json(response);
|
|
2252
|
+
} catch (e) {
|
|
2253
|
+
this.logger.error(`Exception occurred during ${OAUTH2_PROXY_JWT_HEADER} refresh`, e);
|
|
2254
|
+
res.status(401);
|
|
2255
|
+
res.end();
|
|
2256
|
+
}
|
|
2257
|
+
}
|
|
2258
|
+
start() {
|
|
2259
|
+
return Promise.resolve(void 0);
|
|
2260
|
+
}
|
|
2261
|
+
async handleResult(result) {
|
|
2262
|
+
const ctx = {
|
|
2263
|
+
logger: this.logger,
|
|
2264
|
+
tokenIssuer: this.tokenIssuer,
|
|
2265
|
+
catalogIdentityClient: this.catalogIdentityClient
|
|
2266
|
+
};
|
|
2267
|
+
const { profile } = await this.authHandler(result, ctx);
|
|
2268
|
+
const backstageSignInResult = await this.signInResolver({
|
|
2269
|
+
result,
|
|
2270
|
+
profile
|
|
2271
|
+
}, ctx);
|
|
2272
|
+
return {
|
|
2273
|
+
providerInfo: {
|
|
2274
|
+
accessToken: result.accessToken
|
|
2275
|
+
},
|
|
2276
|
+
backstageIdentity: prepareBackstageIdentityResponse(backstageSignInResult),
|
|
2277
|
+
profile
|
|
2278
|
+
};
|
|
2279
|
+
}
|
|
2280
|
+
getResult(req) {
|
|
2281
|
+
const authHeader = req.header(OAUTH2_PROXY_JWT_HEADER);
|
|
2282
|
+
const jwt = IdentityClient.getBearerToken(authHeader);
|
|
2283
|
+
if (!jwt) {
|
|
2284
|
+
throw new errors.AuthenticationError(`Missing or in incorrect format - Oauth2Proxy OIDC header: ${OAUTH2_PROXY_JWT_HEADER}`);
|
|
2285
|
+
}
|
|
2286
|
+
const decodedJWT = jose.JWT.decode(jwt);
|
|
2287
|
+
return {
|
|
2288
|
+
fullProfile: decodedJWT,
|
|
2289
|
+
accessToken: jwt
|
|
2290
|
+
};
|
|
2291
|
+
}
|
|
2292
|
+
}
|
|
2293
|
+
const createOauth2ProxyProvider = (options) => ({ catalogApi, logger, tokenIssuer, tokenManager }) => {
|
|
2294
|
+
const signInResolver = options.signIn.resolver;
|
|
2295
|
+
const authHandler = options.authHandler;
|
|
2296
|
+
const catalogIdentityClient = new CatalogIdentityClient({
|
|
2297
|
+
catalogApi,
|
|
2298
|
+
tokenManager
|
|
2299
|
+
});
|
|
2300
|
+
return new Oauth2ProxyAuthProvider({
|
|
2301
|
+
logger,
|
|
2302
|
+
signInResolver,
|
|
2303
|
+
authHandler,
|
|
2304
|
+
tokenIssuer,
|
|
2305
|
+
catalogIdentityClient
|
|
2306
|
+
});
|
|
2307
|
+
};
|
|
2308
|
+
|
|
2309
|
+
class OidcAuthProvider {
|
|
2310
|
+
constructor(options) {
|
|
2311
|
+
this.implementation = this.setupStrategy(options);
|
|
2312
|
+
this.scope = options.scope;
|
|
2313
|
+
this.prompt = options.prompt;
|
|
2314
|
+
this.signInResolver = options.signInResolver;
|
|
2315
|
+
this.authHandler = options.authHandler;
|
|
2316
|
+
this.tokenIssuer = options.tokenIssuer;
|
|
2317
|
+
this.catalogIdentityClient = options.catalogIdentityClient;
|
|
2318
|
+
this.logger = options.logger;
|
|
2319
|
+
}
|
|
2320
|
+
async start(req) {
|
|
2321
|
+
const { strategy } = await this.implementation;
|
|
2322
|
+
const options = {
|
|
2323
|
+
scope: req.scope || this.scope || "openid profile email",
|
|
2324
|
+
state: encodeState(req.state)
|
|
2325
|
+
};
|
|
2326
|
+
const prompt = this.prompt || "none";
|
|
2327
|
+
if (prompt !== "auto") {
|
|
2328
|
+
options.prompt = prompt;
|
|
2329
|
+
}
|
|
2330
|
+
return await executeRedirectStrategy(req, strategy, options);
|
|
2331
|
+
}
|
|
2332
|
+
async handler(req) {
|
|
2333
|
+
const { strategy } = await this.implementation;
|
|
2334
|
+
const { result, privateInfo } = await executeFrameHandlerStrategy(req, strategy);
|
|
2335
|
+
return {
|
|
2336
|
+
response: await this.handleResult(result),
|
|
2337
|
+
refreshToken: privateInfo.refreshToken
|
|
2338
|
+
};
|
|
2339
|
+
}
|
|
2340
|
+
async refresh(req) {
|
|
2341
|
+
const { client } = await this.implementation;
|
|
2342
|
+
const tokenset = await client.refresh(req.refreshToken);
|
|
2343
|
+
if (!tokenset.access_token) {
|
|
2344
|
+
throw new Error("Refresh failed");
|
|
2345
|
+
}
|
|
2346
|
+
const userinfo = await client.userinfo(tokenset.access_token);
|
|
2347
|
+
return {
|
|
2348
|
+
response: await this.handleResult({ tokenset, userinfo }),
|
|
2349
|
+
refreshToken: tokenset.refresh_token
|
|
2350
|
+
};
|
|
2351
|
+
}
|
|
2352
|
+
async setupStrategy(options) {
|
|
2353
|
+
const issuer = await openidClient.Issuer.discover(options.metadataUrl);
|
|
2354
|
+
const client = new issuer.Client({
|
|
2355
|
+
access_type: "offline",
|
|
2356
|
+
client_id: options.clientId,
|
|
2357
|
+
client_secret: options.clientSecret,
|
|
2358
|
+
redirect_uris: [options.callbackUrl],
|
|
2359
|
+
response_types: ["code"],
|
|
2360
|
+
id_token_signed_response_alg: options.tokenSignedResponseAlg || "RS256",
|
|
2361
|
+
scope: options.scope || ""
|
|
2362
|
+
});
|
|
2363
|
+
const strategy = new openidClient.Strategy({
|
|
2364
|
+
client,
|
|
2365
|
+
passReqToCallback: false
|
|
2366
|
+
}, (tokenset, userinfo, done) => {
|
|
2367
|
+
if (typeof done !== "function") {
|
|
2368
|
+
throw new Error("OIDC IdP must provide a userinfo_endpoint in the metadata response");
|
|
2369
|
+
}
|
|
2370
|
+
done(void 0, { tokenset, userinfo }, {
|
|
2371
|
+
refreshToken: tokenset.refresh_token
|
|
2372
|
+
});
|
|
2373
|
+
});
|
|
2374
|
+
strategy.error = console.error;
|
|
2375
|
+
return { strategy, client };
|
|
2376
|
+
}
|
|
2377
|
+
async handleResult(result) {
|
|
2378
|
+
const context = {
|
|
2379
|
+
logger: this.logger,
|
|
2380
|
+
catalogIdentityClient: this.catalogIdentityClient,
|
|
2381
|
+
tokenIssuer: this.tokenIssuer
|
|
2382
|
+
};
|
|
2383
|
+
const { profile } = await this.authHandler(result, context);
|
|
2384
|
+
const response = {
|
|
2385
|
+
providerInfo: {
|
|
2386
|
+
idToken: result.tokenset.id_token,
|
|
2387
|
+
accessToken: result.tokenset.access_token,
|
|
2388
|
+
scope: result.tokenset.scope,
|
|
2389
|
+
expiresInSeconds: result.tokenset.expires_in
|
|
2390
|
+
},
|
|
2391
|
+
profile
|
|
2392
|
+
};
|
|
2393
|
+
if (this.signInResolver) {
|
|
2394
|
+
response.backstageIdentity = await this.signInResolver({
|
|
2395
|
+
result,
|
|
2396
|
+
profile
|
|
2397
|
+
}, context);
|
|
2398
|
+
}
|
|
2399
|
+
return response;
|
|
2400
|
+
}
|
|
2401
|
+
}
|
|
2402
|
+
const oAuth2DefaultSignInResolver = async (info, ctx) => {
|
|
2403
|
+
const { profile } = info;
|
|
2404
|
+
if (!profile.email) {
|
|
2405
|
+
throw new Error("Profile contained no email");
|
|
2406
|
+
}
|
|
2407
|
+
const userId = profile.email.split("@")[0];
|
|
2408
|
+
const token = await ctx.tokenIssuer.issueToken({
|
|
2409
|
+
claims: {
|
|
2410
|
+
sub: `user:default/${userId}`,
|
|
2411
|
+
ent: [`user:default/${userId}`]
|
|
2412
|
+
}
|
|
2413
|
+
});
|
|
1959
2414
|
return { id: userId, token };
|
|
1960
2415
|
};
|
|
1961
2416
|
const createOidcProvider = (options) => {
|
|
@@ -1964,6 +2419,7 @@ const createOidcProvider = (options) => {
|
|
|
1964
2419
|
globalConfig,
|
|
1965
2420
|
config,
|
|
1966
2421
|
tokenIssuer,
|
|
2422
|
+
tokenManager,
|
|
1967
2423
|
catalogApi,
|
|
1968
2424
|
logger
|
|
1969
2425
|
}) => OAuthEnvironmentHandler.mapConfig(config, (envConfig) => {
|
|
@@ -1977,7 +2433,7 @@ const createOidcProvider = (options) => {
|
|
|
1977
2433
|
const prompt = envConfig.getOptionalString("prompt");
|
|
1978
2434
|
const catalogIdentityClient = new CatalogIdentityClient({
|
|
1979
2435
|
catalogApi,
|
|
1980
|
-
|
|
2436
|
+
tokenManager
|
|
1981
2437
|
});
|
|
1982
2438
|
const authHandler = (options == null ? void 0 : options.authHandler) ? options.authHandler : async ({ userinfo }) => ({
|
|
1983
2439
|
profile: {
|
|
@@ -2076,7 +2532,12 @@ class OktaAuthProvider {
|
|
|
2076
2532
|
};
|
|
2077
2533
|
}
|
|
2078
2534
|
async handleResult(result) {
|
|
2079
|
-
const
|
|
2535
|
+
const context = {
|
|
2536
|
+
logger: this._logger,
|
|
2537
|
+
catalogIdentityClient: this._catalogIdentityClient,
|
|
2538
|
+
tokenIssuer: this._tokenIssuer
|
|
2539
|
+
};
|
|
2540
|
+
const { profile } = await this._authHandler(result, context);
|
|
2080
2541
|
const response = {
|
|
2081
2542
|
providerInfo: {
|
|
2082
2543
|
idToken: result.params.id_token,
|
|
@@ -2090,11 +2551,7 @@ class OktaAuthProvider {
|
|
|
2090
2551
|
response.backstageIdentity = await this._signInResolver({
|
|
2091
2552
|
result,
|
|
2092
2553
|
profile
|
|
2093
|
-
},
|
|
2094
|
-
tokenIssuer: this._tokenIssuer,
|
|
2095
|
-
catalogIdentityClient: this._catalogIdentityClient,
|
|
2096
|
-
logger: this._logger
|
|
2097
|
-
});
|
|
2554
|
+
}, context);
|
|
2098
2555
|
}
|
|
2099
2556
|
return response;
|
|
2100
2557
|
}
|
|
@@ -2118,741 +2575,414 @@ const oktaDefaultSignInResolver = async (info, ctx) => {
|
|
|
2118
2575
|
if (!profile.email) {
|
|
2119
2576
|
throw new Error("Okta profile contained no email");
|
|
2120
2577
|
}
|
|
2121
|
-
const userId = profile.email.split("@")[0];
|
|
2122
|
-
const token = await ctx.tokenIssuer.issueToken({
|
|
2123
|
-
claims: { sub: userId, ent: [`user:default/${userId}`] }
|
|
2124
|
-
});
|
|
2125
|
-
return { id: userId, token };
|
|
2126
|
-
};
|
|
2127
|
-
const createOktaProvider = (_options) => {
|
|
2128
|
-
return ({
|
|
2129
|
-
providerId,
|
|
2130
|
-
globalConfig,
|
|
2131
|
-
config,
|
|
2132
|
-
tokenIssuer,
|
|
2133
|
-
catalogApi,
|
|
2134
|
-
logger
|
|
2135
|
-
}) => OAuthEnvironmentHandler.mapConfig(config, (envConfig) => {
|
|
2136
|
-
var _a, _b;
|
|
2137
|
-
const clientId = envConfig.getString("clientId");
|
|
2138
|
-
const clientSecret = envConfig.getString("clientSecret");
|
|
2139
|
-
const audience = envConfig.getString("audience");
|
|
2140
|
-
const callbackUrl = `${globalConfig.baseUrl}/${providerId}/handler/frame`;
|
|
2141
|
-
if (!audience.startsWith("https://")) {
|
|
2142
|
-
throw new Error("URL for 'audience' must start with 'https://'.");
|
|
2143
|
-
}
|
|
2144
|
-
const catalogIdentityClient = new CatalogIdentityClient({
|
|
2145
|
-
catalogApi,
|
|
2146
|
-
tokenIssuer
|
|
2147
|
-
});
|
|
2148
|
-
const authHandler = (_options == null ? void 0 : _options.authHandler) ? _options.authHandler : async ({ fullProfile, params }) => ({
|
|
2149
|
-
profile: makeProfileInfo(fullProfile, params.id_token)
|
|
2150
|
-
});
|
|
2151
|
-
const signInResolverFn = (_b = (_a = _options == null ? void 0 : _options.signIn) == null ? void 0 : _a.resolver) != null ? _b : oktaDefaultSignInResolver;
|
|
2152
|
-
const signInResolver = (info) => signInResolverFn(info, {
|
|
2153
|
-
catalogIdentityClient,
|
|
2154
|
-
tokenIssuer,
|
|
2155
|
-
logger
|
|
2156
|
-
});
|
|
2157
|
-
const provider = new OktaAuthProvider({
|
|
2158
|
-
audience,
|
|
2159
|
-
clientId,
|
|
2160
|
-
clientSecret,
|
|
2161
|
-
callbackUrl,
|
|
2162
|
-
authHandler,
|
|
2163
|
-
signInResolver,
|
|
2164
|
-
tokenIssuer,
|
|
2165
|
-
catalogIdentityClient,
|
|
2166
|
-
logger
|
|
2167
|
-
});
|
|
2168
|
-
return OAuthAdapter.fromConfig(globalConfig, provider, {
|
|
2169
|
-
disableRefresh: false,
|
|
2170
|
-
providerId,
|
|
2171
|
-
tokenIssuer
|
|
2172
|
-
});
|
|
2173
|
-
});
|
|
2174
|
-
};
|
|
2175
|
-
|
|
2176
|
-
class OneLoginProvider {
|
|
2177
|
-
constructor(options) {
|
|
2178
|
-
this.signInResolver = options.signInResolver;
|
|
2179
|
-
this.authHandler = options.authHandler;
|
|
2180
|
-
this.tokenIssuer = options.tokenIssuer;
|
|
2181
|
-
this.catalogIdentityClient = options.catalogIdentityClient;
|
|
2182
|
-
this.logger = options.logger;
|
|
2183
|
-
this._strategy = new passportOneloginOauth.Strategy({
|
|
2184
|
-
issuer: options.issuer,
|
|
2185
|
-
clientID: options.clientId,
|
|
2186
|
-
clientSecret: options.clientSecret,
|
|
2187
|
-
callbackURL: options.callbackUrl,
|
|
2188
|
-
passReqToCallback: false
|
|
2189
|
-
}, (accessToken, refreshToken, params, fullProfile, done) => {
|
|
2190
|
-
done(void 0, {
|
|
2191
|
-
accessToken,
|
|
2192
|
-
refreshToken,
|
|
2193
|
-
params,
|
|
2194
|
-
fullProfile
|
|
2195
|
-
}, {
|
|
2196
|
-
refreshToken
|
|
2197
|
-
});
|
|
2198
|
-
});
|
|
2199
|
-
}
|
|
2200
|
-
async start(req) {
|
|
2201
|
-
return await executeRedirectStrategy(req, this._strategy, {
|
|
2202
|
-
accessType: "offline",
|
|
2203
|
-
prompt: "consent",
|
|
2204
|
-
scope: "openid",
|
|
2205
|
-
state: encodeState(req.state)
|
|
2206
|
-
});
|
|
2207
|
-
}
|
|
2208
|
-
async handler(req) {
|
|
2209
|
-
const { result, privateInfo } = await executeFrameHandlerStrategy(req, this._strategy);
|
|
2210
|
-
return {
|
|
2211
|
-
response: await this.handleResult(result),
|
|
2212
|
-
refreshToken: privateInfo.refreshToken
|
|
2213
|
-
};
|
|
2214
|
-
}
|
|
2215
|
-
async refresh(req) {
|
|
2216
|
-
const { accessToken, refreshToken, params } = await executeRefreshTokenStrategy(this._strategy, req.refreshToken, req.scope);
|
|
2217
|
-
const fullProfile = await executeFetchUserProfileStrategy(this._strategy, accessToken);
|
|
2218
|
-
return {
|
|
2219
|
-
response: await this.handleResult({
|
|
2220
|
-
fullProfile,
|
|
2221
|
-
params,
|
|
2222
|
-
accessToken
|
|
2223
|
-
}),
|
|
2224
|
-
refreshToken
|
|
2225
|
-
};
|
|
2226
|
-
}
|
|
2227
|
-
async handleResult(result) {
|
|
2228
|
-
const { profile } = await this.authHandler(result);
|
|
2229
|
-
const response = {
|
|
2230
|
-
providerInfo: {
|
|
2231
|
-
idToken: result.params.id_token,
|
|
2232
|
-
accessToken: result.accessToken,
|
|
2233
|
-
scope: result.params.scope,
|
|
2234
|
-
expiresInSeconds: result.params.expires_in
|
|
2235
|
-
},
|
|
2236
|
-
profile
|
|
2237
|
-
};
|
|
2238
|
-
if (this.signInResolver) {
|
|
2239
|
-
response.backstageIdentity = await this.signInResolver({
|
|
2240
|
-
result,
|
|
2241
|
-
profile
|
|
2242
|
-
}, {
|
|
2243
|
-
tokenIssuer: this.tokenIssuer,
|
|
2244
|
-
catalogIdentityClient: this.catalogIdentityClient,
|
|
2245
|
-
logger: this.logger
|
|
2246
|
-
});
|
|
2247
|
-
}
|
|
2248
|
-
return response;
|
|
2249
|
-
}
|
|
2250
|
-
}
|
|
2251
|
-
const defaultSignInResolver = async (info) => {
|
|
2252
|
-
const { profile } = info;
|
|
2253
|
-
if (!profile.email) {
|
|
2254
|
-
throw new Error("OIDC profile contained no email");
|
|
2255
|
-
}
|
|
2256
|
-
const id = profile.email.split("@")[0];
|
|
2257
|
-
return { id, token: "" };
|
|
2258
|
-
};
|
|
2259
|
-
const createOneLoginProvider = (options) => {
|
|
2260
|
-
return ({
|
|
2261
|
-
providerId,
|
|
2262
|
-
globalConfig,
|
|
2263
|
-
config,
|
|
2264
|
-
tokenIssuer,
|
|
2265
|
-
catalogApi,
|
|
2266
|
-
logger
|
|
2267
|
-
}) => OAuthEnvironmentHandler.mapConfig(config, (envConfig) => {
|
|
2268
|
-
var _a, _b;
|
|
2269
|
-
const clientId = envConfig.getString("clientId");
|
|
2270
|
-
const clientSecret = envConfig.getString("clientSecret");
|
|
2271
|
-
const issuer = envConfig.getString("issuer");
|
|
2272
|
-
const callbackUrl = `${globalConfig.baseUrl}/${providerId}/handler/frame`;
|
|
2273
|
-
const catalogIdentityClient = new CatalogIdentityClient({
|
|
2274
|
-
catalogApi,
|
|
2275
|
-
tokenIssuer
|
|
2276
|
-
});
|
|
2277
|
-
const authHandler = (options == null ? void 0 : options.authHandler) ? options.authHandler : async ({ fullProfile, params }) => ({
|
|
2278
|
-
profile: makeProfileInfo(fullProfile, params.id_token)
|
|
2279
|
-
});
|
|
2280
|
-
const signInResolver = (_b = (_a = options == null ? void 0 : options.signIn) == null ? void 0 : _a.resolver) != null ? _b : defaultSignInResolver;
|
|
2281
|
-
const provider = new OneLoginProvider({
|
|
2282
|
-
clientId,
|
|
2283
|
-
clientSecret,
|
|
2284
|
-
callbackUrl,
|
|
2285
|
-
issuer,
|
|
2286
|
-
authHandler,
|
|
2287
|
-
signInResolver,
|
|
2288
|
-
tokenIssuer,
|
|
2289
|
-
catalogIdentityClient,
|
|
2290
|
-
logger
|
|
2291
|
-
});
|
|
2292
|
-
return OAuthAdapter.fromConfig(globalConfig, provider, {
|
|
2293
|
-
disableRefresh: false,
|
|
2294
|
-
providerId,
|
|
2295
|
-
tokenIssuer
|
|
2296
|
-
});
|
|
2297
|
-
});
|
|
2298
|
-
};
|
|
2299
|
-
|
|
2300
|
-
class SamlAuthProvider {
|
|
2301
|
-
constructor(options) {
|
|
2302
|
-
this.appUrl = options.appUrl;
|
|
2303
|
-
this.signInResolver = options.signInResolver;
|
|
2304
|
-
this.authHandler = options.authHandler;
|
|
2305
|
-
this.tokenIssuer = options.tokenIssuer;
|
|
2306
|
-
this.catalogIdentityClient = options.catalogIdentityClient;
|
|
2307
|
-
this.logger = options.logger;
|
|
2308
|
-
this.strategy = new passportSaml.Strategy({ ...options }, (fullProfile, done) => {
|
|
2309
|
-
done(void 0, { fullProfile });
|
|
2310
|
-
});
|
|
2311
|
-
}
|
|
2312
|
-
async start(req, res) {
|
|
2313
|
-
const { url } = await executeRedirectStrategy(req, this.strategy, {});
|
|
2314
|
-
res.redirect(url);
|
|
2315
|
-
}
|
|
2316
|
-
async frameHandler(req, res) {
|
|
2317
|
-
try {
|
|
2318
|
-
const { result } = await executeFrameHandlerStrategy(req, this.strategy);
|
|
2319
|
-
const { profile } = await this.authHandler(result);
|
|
2320
|
-
const response = {
|
|
2321
|
-
profile,
|
|
2322
|
-
providerInfo: {}
|
|
2323
|
-
};
|
|
2324
|
-
if (this.signInResolver) {
|
|
2325
|
-
const signInResponse = await this.signInResolver({
|
|
2326
|
-
result,
|
|
2327
|
-
profile
|
|
2328
|
-
}, {
|
|
2329
|
-
tokenIssuer: this.tokenIssuer,
|
|
2330
|
-
catalogIdentityClient: this.catalogIdentityClient,
|
|
2331
|
-
logger: this.logger
|
|
2332
|
-
});
|
|
2333
|
-
response.backstageIdentity = prepareBackstageIdentityResponse(signInResponse);
|
|
2334
|
-
}
|
|
2335
|
-
return postMessageResponse(res, this.appUrl, {
|
|
2336
|
-
type: "authorization_response",
|
|
2337
|
-
response
|
|
2338
|
-
});
|
|
2339
|
-
} catch (error) {
|
|
2340
|
-
const { name, message } = errors.isError(error) ? error : new Error("Encountered invalid error");
|
|
2341
|
-
return postMessageResponse(res, this.appUrl, {
|
|
2342
|
-
type: "authorization_response",
|
|
2343
|
-
error: { name, message }
|
|
2344
|
-
});
|
|
2345
|
-
}
|
|
2346
|
-
}
|
|
2347
|
-
async logout(_req, res) {
|
|
2348
|
-
res.end();
|
|
2349
|
-
}
|
|
2350
|
-
}
|
|
2351
|
-
const samlDefaultSignInResolver = async (info, ctx) => {
|
|
2352
|
-
const id = info.result.fullProfile.nameID;
|
|
2578
|
+
const userId = profile.email.split("@")[0];
|
|
2353
2579
|
const token = await ctx.tokenIssuer.issueToken({
|
|
2354
|
-
claims: { sub:
|
|
2580
|
+
claims: { sub: `user:default/${userId}`, ent: [`user:default/${userId}`] }
|
|
2355
2581
|
});
|
|
2356
|
-
return { id, token };
|
|
2582
|
+
return { id: userId, token };
|
|
2357
2583
|
};
|
|
2358
|
-
const
|
|
2584
|
+
const createOktaProvider = (_options) => {
|
|
2359
2585
|
return ({
|
|
2360
2586
|
providerId,
|
|
2361
2587
|
globalConfig,
|
|
2362
2588
|
config,
|
|
2363
2589
|
tokenIssuer,
|
|
2590
|
+
tokenManager,
|
|
2364
2591
|
catalogApi,
|
|
2365
2592
|
logger
|
|
2366
|
-
}) => {
|
|
2593
|
+
}) => OAuthEnvironmentHandler.mapConfig(config, (envConfig) => {
|
|
2367
2594
|
var _a, _b;
|
|
2595
|
+
const clientId = envConfig.getString("clientId");
|
|
2596
|
+
const clientSecret = envConfig.getString("clientSecret");
|
|
2597
|
+
const audience = envConfig.getString("audience");
|
|
2598
|
+
const callbackUrl = `${globalConfig.baseUrl}/${providerId}/handler/frame`;
|
|
2599
|
+
if (!audience.startsWith("https://")) {
|
|
2600
|
+
throw new Error("URL for 'audience' must start with 'https://'.");
|
|
2601
|
+
}
|
|
2368
2602
|
const catalogIdentityClient = new CatalogIdentityClient({
|
|
2369
2603
|
catalogApi,
|
|
2370
|
-
|
|
2604
|
+
tokenManager
|
|
2371
2605
|
});
|
|
2372
|
-
const authHandler = (
|
|
2373
|
-
profile:
|
|
2374
|
-
email: fullProfile.email,
|
|
2375
|
-
displayName: fullProfile.displayName
|
|
2376
|
-
}
|
|
2606
|
+
const authHandler = (_options == null ? void 0 : _options.authHandler) ? _options.authHandler : async ({ fullProfile, params }) => ({
|
|
2607
|
+
profile: makeProfileInfo(fullProfile, params.id_token)
|
|
2377
2608
|
});
|
|
2378
|
-
const signInResolverFn = (_b = (_a =
|
|
2609
|
+
const signInResolverFn = (_b = (_a = _options == null ? void 0 : _options.signIn) == null ? void 0 : _a.resolver) != null ? _b : oktaDefaultSignInResolver;
|
|
2379
2610
|
const signInResolver = (info) => signInResolverFn(info, {
|
|
2380
2611
|
catalogIdentityClient,
|
|
2381
2612
|
tokenIssuer,
|
|
2382
|
-
logger
|
|
2383
|
-
});
|
|
2384
|
-
return new SamlAuthProvider({
|
|
2385
|
-
callbackUrl: `${globalConfig.baseUrl}/${providerId}/handler/frame`,
|
|
2386
|
-
entryPoint: config.getString("entryPoint"),
|
|
2387
|
-
logoutUrl: config.getOptionalString("logoutUrl"),
|
|
2388
|
-
audience: config.getOptionalString("audience"),
|
|
2389
|
-
issuer: config.getString("issuer"),
|
|
2390
|
-
cert: config.getString("cert"),
|
|
2391
|
-
privateKey: config.getOptionalString("privateKey"),
|
|
2392
|
-
authnContext: config.getOptionalStringArray("authnContext"),
|
|
2393
|
-
identifierFormat: config.getOptionalString("identifierFormat"),
|
|
2394
|
-
decryptionPvk: config.getOptionalString("decryptionPvk"),
|
|
2395
|
-
signatureAlgorithm: config.getOptionalString("signatureAlgorithm"),
|
|
2396
|
-
digestAlgorithm: config.getOptionalString("digestAlgorithm"),
|
|
2397
|
-
acceptedClockSkewMs: config.getOptionalNumber("acceptedClockSkewMs"),
|
|
2398
|
-
tokenIssuer,
|
|
2399
|
-
appUrl: globalConfig.appUrl,
|
|
2400
|
-
authHandler,
|
|
2401
|
-
signInResolver,
|
|
2402
|
-
logger,
|
|
2403
|
-
catalogIdentityClient
|
|
2404
|
-
});
|
|
2405
|
-
};
|
|
2406
|
-
};
|
|
2407
|
-
|
|
2408
|
-
const IAP_JWT_HEADER = "x-goog-iap-jwt-assertion";
|
|
2409
|
-
|
|
2410
|
-
function createTokenValidator(audience, mockClient) {
|
|
2411
|
-
const client = mockClient != null ? mockClient : new googleAuthLibrary.OAuth2Client();
|
|
2412
|
-
return async function tokenValidator(token) {
|
|
2413
|
-
const response = await client.getIapPublicKeys();
|
|
2414
|
-
const ticket = await client.verifySignedJwtWithCertsAsync(token, response.pubkeys, audience, ["https://cloud.google.com/iap"]);
|
|
2415
|
-
const payload = ticket.getPayload();
|
|
2416
|
-
if (!payload) {
|
|
2417
|
-
throw new TypeError("Token had no payload");
|
|
2418
|
-
}
|
|
2419
|
-
return payload;
|
|
2420
|
-
};
|
|
2421
|
-
}
|
|
2422
|
-
async function parseRequestToken(jwtToken, tokenValidator) {
|
|
2423
|
-
if (typeof jwtToken !== "string" || !jwtToken) {
|
|
2424
|
-
throw new errors.AuthenticationError(`Missing Google IAP header: ${IAP_JWT_HEADER}`);
|
|
2425
|
-
}
|
|
2426
|
-
let payload;
|
|
2427
|
-
try {
|
|
2428
|
-
payload = await tokenValidator(jwtToken);
|
|
2429
|
-
} catch (e) {
|
|
2430
|
-
throw new errors.AuthenticationError(`Google IAP token verification failed, ${e}`);
|
|
2431
|
-
}
|
|
2432
|
-
if (!payload.sub || !payload.email) {
|
|
2433
|
-
throw new errors.AuthenticationError("Google IAP token payload is missing sub and/or email claim");
|
|
2434
|
-
}
|
|
2435
|
-
return {
|
|
2436
|
-
iapToken: {
|
|
2437
|
-
...payload,
|
|
2438
|
-
sub: payload.sub,
|
|
2439
|
-
email: payload.email
|
|
2440
|
-
}
|
|
2441
|
-
};
|
|
2442
|
-
}
|
|
2443
|
-
const defaultAuthHandler = async ({
|
|
2444
|
-
iapToken
|
|
2445
|
-
}) => ({ profile: { email: iapToken.email } });
|
|
2446
|
-
|
|
2447
|
-
class GcpIapProvider {
|
|
2448
|
-
constructor(options) {
|
|
2449
|
-
this.authHandler = options.authHandler;
|
|
2450
|
-
this.signInResolver = options.signInResolver;
|
|
2451
|
-
this.tokenValidator = options.tokenValidator;
|
|
2452
|
-
this.tokenIssuer = options.tokenIssuer;
|
|
2453
|
-
this.catalogIdentityClient = options.catalogIdentityClient;
|
|
2454
|
-
this.logger = options.logger;
|
|
2455
|
-
}
|
|
2456
|
-
async start() {
|
|
2457
|
-
}
|
|
2458
|
-
async frameHandler() {
|
|
2459
|
-
}
|
|
2460
|
-
async refresh(req, res) {
|
|
2461
|
-
const result = await parseRequestToken(req.header(IAP_JWT_HEADER), this.tokenValidator);
|
|
2462
|
-
const { profile } = await this.authHandler(result);
|
|
2463
|
-
const backstageIdentity = await this.signInResolver({ profile, result }, {
|
|
2464
|
-
tokenIssuer: this.tokenIssuer,
|
|
2465
|
-
catalogIdentityClient: this.catalogIdentityClient,
|
|
2466
|
-
logger: this.logger
|
|
2467
|
-
});
|
|
2468
|
-
const response = {
|
|
2469
|
-
providerInfo: { iapToken: result.iapToken },
|
|
2470
|
-
profile,
|
|
2471
|
-
backstageIdentity: prepareBackstageIdentityResponse(backstageIdentity)
|
|
2472
|
-
};
|
|
2473
|
-
res.json(response);
|
|
2474
|
-
}
|
|
2475
|
-
}
|
|
2476
|
-
function createGcpIapProvider(options) {
|
|
2477
|
-
return ({ config, tokenIssuer, catalogApi, logger }) => {
|
|
2478
|
-
var _a;
|
|
2479
|
-
const audience = config.getString("audience");
|
|
2480
|
-
const authHandler = (_a = options.authHandler) != null ? _a : defaultAuthHandler;
|
|
2481
|
-
const signInResolver = options.signIn.resolver;
|
|
2482
|
-
const tokenValidator = createTokenValidator(audience);
|
|
2483
|
-
const catalogIdentityClient = new CatalogIdentityClient({
|
|
2484
|
-
catalogApi,
|
|
2485
|
-
tokenIssuer
|
|
2486
|
-
});
|
|
2487
|
-
return new GcpIapProvider({
|
|
2488
|
-
authHandler,
|
|
2489
|
-
signInResolver,
|
|
2490
|
-
tokenValidator,
|
|
2491
|
-
tokenIssuer,
|
|
2492
|
-
catalogIdentityClient,
|
|
2493
|
-
logger
|
|
2494
|
-
});
|
|
2495
|
-
};
|
|
2496
|
-
}
|
|
2497
|
-
|
|
2498
|
-
const factories = {
|
|
2499
|
-
google: createGoogleProvider(),
|
|
2500
|
-
github: createGithubProvider(),
|
|
2501
|
-
gitlab: createGitlabProvider(),
|
|
2502
|
-
saml: createSamlProvider(),
|
|
2503
|
-
okta: createOktaProvider(),
|
|
2504
|
-
auth0: createAuth0Provider(),
|
|
2505
|
-
microsoft: createMicrosoftProvider(),
|
|
2506
|
-
oauth2: createOAuth2Provider(),
|
|
2507
|
-
oidc: createOidcProvider(),
|
|
2508
|
-
onelogin: createOneLoginProvider(),
|
|
2509
|
-
awsalb: createAwsAlbProvider(),
|
|
2510
|
-
bitbucket: createBitbucketProvider(),
|
|
2511
|
-
atlassian: createAtlassianProvider()
|
|
2512
|
-
};
|
|
2513
|
-
|
|
2514
|
-
function createOidcRouter(options) {
|
|
2515
|
-
const { baseUrl, tokenIssuer } = options;
|
|
2516
|
-
const router = Router__default["default"]();
|
|
2517
|
-
const config = {
|
|
2518
|
-
issuer: baseUrl,
|
|
2519
|
-
token_endpoint: `${baseUrl}/v1/token`,
|
|
2520
|
-
userinfo_endpoint: `${baseUrl}/v1/userinfo`,
|
|
2521
|
-
jwks_uri: `${baseUrl}/.well-known/jwks.json`,
|
|
2522
|
-
response_types_supported: ["id_token"],
|
|
2523
|
-
subject_types_supported: ["public"],
|
|
2524
|
-
id_token_signing_alg_values_supported: ["RS256"],
|
|
2525
|
-
scopes_supported: ["openid"],
|
|
2526
|
-
token_endpoint_auth_methods_supported: [],
|
|
2527
|
-
claims_supported: ["sub"],
|
|
2528
|
-
grant_types_supported: []
|
|
2529
|
-
};
|
|
2530
|
-
router.get("/.well-known/openid-configuration", (_req, res) => {
|
|
2531
|
-
res.json(config);
|
|
2532
|
-
});
|
|
2533
|
-
router.get("/.well-known/jwks.json", async (_req, res) => {
|
|
2534
|
-
const { keys } = await tokenIssuer.listPublicKeys();
|
|
2535
|
-
res.json({ keys });
|
|
2536
|
-
});
|
|
2537
|
-
router.get("/v1/token", (_req, res) => {
|
|
2538
|
-
res.status(501).send("Not Implemented");
|
|
2539
|
-
});
|
|
2540
|
-
router.get("/v1/userinfo", (_req, res) => {
|
|
2541
|
-
res.status(501).send("Not Implemented");
|
|
2542
|
-
});
|
|
2543
|
-
return router;
|
|
2544
|
-
}
|
|
2545
|
-
|
|
2546
|
-
const CLOCK_MARGIN_S = 10;
|
|
2547
|
-
class IdentityClient {
|
|
2548
|
-
constructor(options) {
|
|
2549
|
-
this.discovery = options.discovery;
|
|
2550
|
-
this.issuer = options.issuer;
|
|
2551
|
-
this.keyStore = new jose.JWKS.KeyStore();
|
|
2552
|
-
this.keyStoreUpdated = 0;
|
|
2553
|
-
}
|
|
2554
|
-
async authenticate(token) {
|
|
2555
|
-
var _a;
|
|
2556
|
-
if (!token) {
|
|
2557
|
-
throw new errors.AuthenticationError("No token specified");
|
|
2558
|
-
}
|
|
2559
|
-
const key = await this.getKey(token);
|
|
2560
|
-
if (!key) {
|
|
2561
|
-
throw new errors.AuthenticationError("No signing key matching token found");
|
|
2562
|
-
}
|
|
2563
|
-
const decoded = jose.JWT.IdToken.verify(token, key, {
|
|
2564
|
-
algorithms: ["ES256"],
|
|
2565
|
-
audience: "backstage",
|
|
2566
|
-
issuer: this.issuer
|
|
2567
|
-
});
|
|
2568
|
-
if (!decoded.sub) {
|
|
2569
|
-
throw new errors.AuthenticationError("No user sub found in token");
|
|
2570
|
-
}
|
|
2571
|
-
const user = {
|
|
2572
|
-
id: decoded.sub,
|
|
2573
|
-
token,
|
|
2574
|
-
identity: {
|
|
2575
|
-
type: "user",
|
|
2576
|
-
userEntityRef: decoded.sub,
|
|
2577
|
-
ownershipEntityRefs: (_a = decoded.ent) != null ? _a : []
|
|
2578
|
-
}
|
|
2579
|
-
};
|
|
2580
|
-
return user;
|
|
2581
|
-
}
|
|
2582
|
-
static getBearerToken(authorizationHeader) {
|
|
2583
|
-
if (typeof authorizationHeader !== "string") {
|
|
2584
|
-
return void 0;
|
|
2585
|
-
}
|
|
2586
|
-
const matches = authorizationHeader.match(/Bearer\s+(\S+)/i);
|
|
2587
|
-
return matches == null ? void 0 : matches[1];
|
|
2588
|
-
}
|
|
2589
|
-
async getKey(rawJwtToken) {
|
|
2590
|
-
const { header, payload } = jose.JWT.decode(rawJwtToken, {
|
|
2591
|
-
complete: true
|
|
2592
|
-
});
|
|
2593
|
-
const keyStoreHasKey = !!this.keyStore.get({ kid: header.kid });
|
|
2594
|
-
const issuedAfterLastRefresh = (payload == null ? void 0 : payload.iat) && payload.iat > this.keyStoreUpdated - CLOCK_MARGIN_S;
|
|
2595
|
-
if (!keyStoreHasKey && issuedAfterLastRefresh) {
|
|
2596
|
-
await this.refreshKeyStore();
|
|
2597
|
-
}
|
|
2598
|
-
return this.keyStore.get({ kid: header.kid });
|
|
2599
|
-
}
|
|
2600
|
-
async listPublicKeys() {
|
|
2601
|
-
const url = `${await this.discovery.getBaseUrl("auth")}/.well-known/jwks.json`;
|
|
2602
|
-
const response = await fetch__default["default"](url);
|
|
2603
|
-
if (!response.ok) {
|
|
2604
|
-
const payload = await response.text();
|
|
2605
|
-
const message = `Request failed with ${response.status} ${response.statusText}, ${payload}`;
|
|
2606
|
-
throw new Error(message);
|
|
2607
|
-
}
|
|
2608
|
-
const publicKeys = await response.json();
|
|
2609
|
-
return publicKeys;
|
|
2610
|
-
}
|
|
2611
|
-
async refreshKeyStore() {
|
|
2612
|
-
const now = Date.now() / 1e3;
|
|
2613
|
-
const publicKeys = await this.listPublicKeys();
|
|
2614
|
-
this.keyStore = jose.JWKS.asKeyStore({
|
|
2615
|
-
keys: publicKeys.keys.map((key) => key)
|
|
2613
|
+
logger
|
|
2616
2614
|
});
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2615
|
+
const provider = new OktaAuthProvider({
|
|
2616
|
+
audience,
|
|
2617
|
+
clientId,
|
|
2618
|
+
clientSecret,
|
|
2619
|
+
callbackUrl,
|
|
2620
|
+
authHandler,
|
|
2621
|
+
signInResolver,
|
|
2622
|
+
tokenIssuer,
|
|
2623
|
+
catalogIdentityClient,
|
|
2624
|
+
logger
|
|
2625
|
+
});
|
|
2626
|
+
return OAuthAdapter.fromConfig(globalConfig, provider, {
|
|
2627
|
+
disableRefresh: false,
|
|
2628
|
+
providerId,
|
|
2629
|
+
tokenIssuer
|
|
2630
|
+
});
|
|
2631
|
+
});
|
|
2632
|
+
};
|
|
2620
2633
|
|
|
2621
|
-
|
|
2622
|
-
class TokenFactory {
|
|
2634
|
+
class OneLoginProvider {
|
|
2623
2635
|
constructor(options) {
|
|
2624
|
-
this.
|
|
2636
|
+
this.signInResolver = options.signInResolver;
|
|
2637
|
+
this.authHandler = options.authHandler;
|
|
2638
|
+
this.tokenIssuer = options.tokenIssuer;
|
|
2639
|
+
this.catalogIdentityClient = options.catalogIdentityClient;
|
|
2625
2640
|
this.logger = options.logger;
|
|
2626
|
-
this.
|
|
2627
|
-
|
|
2641
|
+
this._strategy = new passportOneloginOauth.Strategy({
|
|
2642
|
+
issuer: options.issuer,
|
|
2643
|
+
clientID: options.clientId,
|
|
2644
|
+
clientSecret: options.clientSecret,
|
|
2645
|
+
callbackURL: options.callbackUrl,
|
|
2646
|
+
passReqToCallback: false
|
|
2647
|
+
}, (accessToken, refreshToken, params, fullProfile, done) => {
|
|
2648
|
+
done(void 0, {
|
|
2649
|
+
accessToken,
|
|
2650
|
+
refreshToken,
|
|
2651
|
+
params,
|
|
2652
|
+
fullProfile
|
|
2653
|
+
}, {
|
|
2654
|
+
refreshToken
|
|
2655
|
+
});
|
|
2656
|
+
});
|
|
2628
2657
|
}
|
|
2629
|
-
async
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
const iat = Math.floor(Date.now() / MS_IN_S);
|
|
2636
|
-
const exp = iat + this.keyDurationSeconds;
|
|
2637
|
-
this.logger.info(`Issuing token for ${sub}, with entities ${ent != null ? ent : []}`);
|
|
2638
|
-
return jose.JWS.sign({ iss, sub, aud, iat, exp, ent }, key, {
|
|
2639
|
-
alg: key.alg,
|
|
2640
|
-
kid: key.kid
|
|
2658
|
+
async start(req) {
|
|
2659
|
+
return await executeRedirectStrategy(req, this._strategy, {
|
|
2660
|
+
accessType: "offline",
|
|
2661
|
+
prompt: "consent",
|
|
2662
|
+
scope: "openid",
|
|
2663
|
+
state: encodeState(req.state)
|
|
2641
2664
|
});
|
|
2642
2665
|
}
|
|
2643
|
-
async
|
|
2644
|
-
const {
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
seconds: 3 * this.keyDurationSeconds
|
|
2650
|
-
});
|
|
2651
|
-
if (expireAt < luxon.DateTime.local()) {
|
|
2652
|
-
expiredKeys.push(key);
|
|
2653
|
-
} else {
|
|
2654
|
-
validKeys.push(key);
|
|
2655
|
-
}
|
|
2656
|
-
}
|
|
2657
|
-
if (expiredKeys.length > 0) {
|
|
2658
|
-
const kids = expiredKeys.map(({ key }) => key.kid);
|
|
2659
|
-
this.logger.info(`Removing expired signing keys, '${kids.join("', '")}'`);
|
|
2660
|
-
this.keyStore.removeKeys(kids).catch((error) => {
|
|
2661
|
-
this.logger.error(`Failed to remove expired keys, ${error}`);
|
|
2662
|
-
});
|
|
2663
|
-
}
|
|
2664
|
-
return { keys: validKeys.map(({ key }) => key) };
|
|
2666
|
+
async handler(req) {
|
|
2667
|
+
const { result, privateInfo } = await executeFrameHandlerStrategy(req, this._strategy);
|
|
2668
|
+
return {
|
|
2669
|
+
response: await this.handleResult(result),
|
|
2670
|
+
refreshToken: privateInfo.refreshToken
|
|
2671
|
+
};
|
|
2665
2672
|
}
|
|
2666
|
-
async
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
}
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
2673
|
+
async refresh(req) {
|
|
2674
|
+
const { accessToken, refreshToken, params } = await executeRefreshTokenStrategy(this._strategy, req.refreshToken, req.scope);
|
|
2675
|
+
const fullProfile = await executeFetchUserProfileStrategy(this._strategy, accessToken);
|
|
2676
|
+
return {
|
|
2677
|
+
response: await this.handleResult({
|
|
2678
|
+
fullProfile,
|
|
2679
|
+
params,
|
|
2680
|
+
accessToken
|
|
2681
|
+
}),
|
|
2682
|
+
refreshToken
|
|
2683
|
+
};
|
|
2684
|
+
}
|
|
2685
|
+
async handleResult(result) {
|
|
2686
|
+
const context = {
|
|
2687
|
+
logger: this.logger,
|
|
2688
|
+
catalogIdentityClient: this.catalogIdentityClient,
|
|
2689
|
+
tokenIssuer: this.tokenIssuer
|
|
2690
|
+
};
|
|
2691
|
+
const { profile } = await this.authHandler(result, context);
|
|
2692
|
+
const response = {
|
|
2693
|
+
providerInfo: {
|
|
2694
|
+
idToken: result.params.id_token,
|
|
2695
|
+
accessToken: result.accessToken,
|
|
2696
|
+
scope: result.params.scope,
|
|
2697
|
+
expiresInSeconds: result.params.expires_in
|
|
2698
|
+
},
|
|
2699
|
+
profile
|
|
2700
|
+
};
|
|
2701
|
+
if (this.signInResolver) {
|
|
2702
|
+
response.backstageIdentity = await this.signInResolver({
|
|
2703
|
+
result,
|
|
2704
|
+
profile
|
|
2705
|
+
}, context);
|
|
2694
2706
|
}
|
|
2695
|
-
return
|
|
2707
|
+
return response;
|
|
2696
2708
|
}
|
|
2697
2709
|
}
|
|
2698
|
-
|
|
2699
|
-
const
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
const parsedDate = typeof date === "string" ? luxon.DateTime.fromSQL(date, { zone: "UTC" }) : luxon.DateTime.fromJSDate(date);
|
|
2703
|
-
if (!parsedDate.isValid) {
|
|
2704
|
-
throw new Error(`Failed to parse date, reason: ${parsedDate.invalidReason}, explanation: ${parsedDate.invalidExplanation}`);
|
|
2710
|
+
const defaultSignInResolver = async (info) => {
|
|
2711
|
+
const { profile } = info;
|
|
2712
|
+
if (!profile.email) {
|
|
2713
|
+
throw new Error("OIDC profile contained no email");
|
|
2705
2714
|
}
|
|
2706
|
-
|
|
2715
|
+
const id = profile.email.split("@")[0];
|
|
2716
|
+
return { id, token: "" };
|
|
2707
2717
|
};
|
|
2708
|
-
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
|
|
2718
|
+
const createOneLoginProvider = (options) => {
|
|
2719
|
+
return ({
|
|
2720
|
+
providerId,
|
|
2721
|
+
globalConfig,
|
|
2722
|
+
config,
|
|
2723
|
+
tokenIssuer,
|
|
2724
|
+
tokenManager,
|
|
2725
|
+
catalogApi,
|
|
2726
|
+
logger
|
|
2727
|
+
}) => OAuthEnvironmentHandler.mapConfig(config, (envConfig) => {
|
|
2728
|
+
var _a, _b;
|
|
2729
|
+
const clientId = envConfig.getString("clientId");
|
|
2730
|
+
const clientSecret = envConfig.getString("clientSecret");
|
|
2731
|
+
const issuer = envConfig.getString("issuer");
|
|
2732
|
+
const callbackUrl = `${globalConfig.baseUrl}/${providerId}/handler/frame`;
|
|
2733
|
+
const catalogIdentityClient = new CatalogIdentityClient({
|
|
2734
|
+
catalogApi,
|
|
2735
|
+
tokenManager
|
|
2713
2736
|
});
|
|
2714
|
-
|
|
2715
|
-
|
|
2737
|
+
const authHandler = (options == null ? void 0 : options.authHandler) ? options.authHandler : async ({ fullProfile, params }) => ({
|
|
2738
|
+
profile: makeProfileInfo(fullProfile, params.id_token)
|
|
2739
|
+
});
|
|
2740
|
+
const signInResolver = (_b = (_a = options == null ? void 0 : options.signIn) == null ? void 0 : _a.resolver) != null ? _b : defaultSignInResolver;
|
|
2741
|
+
const provider = new OneLoginProvider({
|
|
2742
|
+
clientId,
|
|
2743
|
+
clientSecret,
|
|
2744
|
+
callbackUrl,
|
|
2745
|
+
issuer,
|
|
2746
|
+
authHandler,
|
|
2747
|
+
signInResolver,
|
|
2748
|
+
tokenIssuer,
|
|
2749
|
+
catalogIdentityClient,
|
|
2750
|
+
logger
|
|
2751
|
+
});
|
|
2752
|
+
return OAuthAdapter.fromConfig(globalConfig, provider, {
|
|
2753
|
+
disableRefresh: false,
|
|
2754
|
+
providerId,
|
|
2755
|
+
tokenIssuer
|
|
2756
|
+
});
|
|
2757
|
+
});
|
|
2758
|
+
};
|
|
2759
|
+
|
|
2760
|
+
class SamlAuthProvider {
|
|
2716
2761
|
constructor(options) {
|
|
2717
|
-
this.
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2762
|
+
this.appUrl = options.appUrl;
|
|
2763
|
+
this.signInResolver = options.signInResolver;
|
|
2764
|
+
this.authHandler = options.authHandler;
|
|
2765
|
+
this.tokenIssuer = options.tokenIssuer;
|
|
2766
|
+
this.catalogIdentityClient = options.catalogIdentityClient;
|
|
2767
|
+
this.logger = options.logger;
|
|
2768
|
+
this.strategy = new passportSaml.Strategy({ ...options }, (fullProfile, done) => {
|
|
2769
|
+
done(void 0, { fullProfile });
|
|
2723
2770
|
});
|
|
2724
2771
|
}
|
|
2725
|
-
async
|
|
2726
|
-
const
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
|
|
2730
|
-
|
|
2731
|
-
|
|
2732
|
-
|
|
2772
|
+
async start(req, res) {
|
|
2773
|
+
const { url } = await executeRedirectStrategy(req, this.strategy, {});
|
|
2774
|
+
res.redirect(url);
|
|
2775
|
+
}
|
|
2776
|
+
async frameHandler(req, res) {
|
|
2777
|
+
try {
|
|
2778
|
+
const context = {
|
|
2779
|
+
logger: this.logger,
|
|
2780
|
+
catalogIdentityClient: this.catalogIdentityClient,
|
|
2781
|
+
tokenIssuer: this.tokenIssuer
|
|
2782
|
+
};
|
|
2783
|
+
const { result } = await executeFrameHandlerStrategy(req, this.strategy);
|
|
2784
|
+
const { profile } = await this.authHandler(result, context);
|
|
2785
|
+
const response = {
|
|
2786
|
+
profile,
|
|
2787
|
+
providerInfo: {}
|
|
2788
|
+
};
|
|
2789
|
+
if (this.signInResolver) {
|
|
2790
|
+
const signInResponse = await this.signInResolver({
|
|
2791
|
+
result,
|
|
2792
|
+
profile
|
|
2793
|
+
}, context);
|
|
2794
|
+
response.backstageIdentity = prepareBackstageIdentityResponse(signInResponse);
|
|
2795
|
+
}
|
|
2796
|
+
return postMessageResponse(res, this.appUrl, {
|
|
2797
|
+
type: "authorization_response",
|
|
2798
|
+
response
|
|
2799
|
+
});
|
|
2800
|
+
} catch (error) {
|
|
2801
|
+
const { name, message } = errors.isError(error) ? error : new Error("Encountered invalid error");
|
|
2802
|
+
return postMessageResponse(res, this.appUrl, {
|
|
2803
|
+
type: "authorization_response",
|
|
2804
|
+
error: { name, message }
|
|
2805
|
+
});
|
|
2806
|
+
}
|
|
2733
2807
|
}
|
|
2734
|
-
async
|
|
2735
|
-
|
|
2808
|
+
async logout(_req, res) {
|
|
2809
|
+
res.end();
|
|
2736
2810
|
}
|
|
2737
2811
|
}
|
|
2738
|
-
|
|
2739
|
-
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
}
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
|
|
2812
|
+
const samlDefaultSignInResolver = async (info, ctx) => {
|
|
2813
|
+
const id = info.result.fullProfile.nameID;
|
|
2814
|
+
const token = await ctx.tokenIssuer.issueToken({
|
|
2815
|
+
claims: { sub: id }
|
|
2816
|
+
});
|
|
2817
|
+
return { id, token };
|
|
2818
|
+
};
|
|
2819
|
+
const createSamlProvider = (options) => {
|
|
2820
|
+
return ({
|
|
2821
|
+
providerId,
|
|
2822
|
+
globalConfig,
|
|
2823
|
+
config,
|
|
2824
|
+
tokenIssuer,
|
|
2825
|
+
tokenManager,
|
|
2826
|
+
catalogApi,
|
|
2827
|
+
logger
|
|
2828
|
+
}) => {
|
|
2829
|
+
var _a, _b;
|
|
2830
|
+
const catalogIdentityClient = new CatalogIdentityClient({
|
|
2831
|
+
catalogApi,
|
|
2832
|
+
tokenManager
|
|
2747
2833
|
});
|
|
2748
|
-
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
|
|
2834
|
+
const authHandler = (options == null ? void 0 : options.authHandler) ? options.authHandler : async ({ fullProfile }) => ({
|
|
2835
|
+
profile: {
|
|
2836
|
+
email: fullProfile.email,
|
|
2837
|
+
displayName: fullProfile.displayName
|
|
2838
|
+
}
|
|
2839
|
+
});
|
|
2840
|
+
const signInResolverFn = (_b = (_a = options == null ? void 0 : options.signIn) == null ? void 0 : _a.resolver) != null ? _b : samlDefaultSignInResolver;
|
|
2841
|
+
const signInResolver = (info) => signInResolverFn(info, {
|
|
2842
|
+
catalogIdentityClient,
|
|
2843
|
+
tokenIssuer,
|
|
2844
|
+
logger
|
|
2845
|
+
});
|
|
2846
|
+
return new SamlAuthProvider({
|
|
2847
|
+
callbackUrl: `${globalConfig.baseUrl}/${providerId}/handler/frame`,
|
|
2848
|
+
entryPoint: config.getString("entryPoint"),
|
|
2849
|
+
logoutUrl: config.getOptionalString("logoutUrl"),
|
|
2850
|
+
audience: config.getOptionalString("audience"),
|
|
2851
|
+
issuer: config.getString("issuer"),
|
|
2852
|
+
cert: config.getString("cert"),
|
|
2853
|
+
privateKey: config.getOptionalString("privateKey"),
|
|
2854
|
+
authnContext: config.getOptionalStringArray("authnContext"),
|
|
2855
|
+
identifierFormat: config.getOptionalString("identifierFormat"),
|
|
2856
|
+
decryptionPvk: config.getOptionalString("decryptionPvk"),
|
|
2857
|
+
signatureAlgorithm: config.getOptionalString("signatureAlgorithm"),
|
|
2858
|
+
digestAlgorithm: config.getOptionalString("digestAlgorithm"),
|
|
2859
|
+
acceptedClockSkewMs: config.getOptionalNumber("acceptedClockSkewMs"),
|
|
2860
|
+
tokenIssuer,
|
|
2861
|
+
appUrl: globalConfig.appUrl,
|
|
2862
|
+
authHandler,
|
|
2863
|
+
signInResolver,
|
|
2864
|
+
logger,
|
|
2865
|
+
catalogIdentityClient
|
|
2866
|
+
});
|
|
2867
|
+
};
|
|
2868
|
+
};
|
|
2869
|
+
|
|
2870
|
+
const IAP_JWT_HEADER = "x-goog-iap-jwt-assertion";
|
|
2871
|
+
|
|
2872
|
+
function createTokenValidator(audience, mockClient) {
|
|
2873
|
+
const client = mockClient != null ? mockClient : new googleAuthLibrary.OAuth2Client();
|
|
2874
|
+
return async function tokenValidator(token) {
|
|
2875
|
+
const response = await client.getIapPublicKeys();
|
|
2876
|
+
const ticket = await client.verifySignedJwtWithCertsAsync(token, response.pubkeys, audience, ["https://cloud.google.com/iap"]);
|
|
2877
|
+
const payload = ticket.getPayload();
|
|
2878
|
+
if (!payload) {
|
|
2879
|
+
throw new TypeError("Token had no payload");
|
|
2752
2880
|
}
|
|
2881
|
+
return payload;
|
|
2882
|
+
};
|
|
2883
|
+
}
|
|
2884
|
+
async function parseRequestToken(jwtToken, tokenValidator) {
|
|
2885
|
+
if (typeof jwtToken !== "string" || !jwtToken) {
|
|
2886
|
+
throw new errors.AuthenticationError(`Missing Google IAP header: ${IAP_JWT_HEADER}`);
|
|
2753
2887
|
}
|
|
2754
|
-
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
|
|
2759
|
-
|
|
2760
|
-
|
|
2888
|
+
let payload;
|
|
2889
|
+
try {
|
|
2890
|
+
payload = await tokenValidator(jwtToken);
|
|
2891
|
+
} catch (e) {
|
|
2892
|
+
throw new errors.AuthenticationError(`Google IAP token verification failed, ${e}`);
|
|
2893
|
+
}
|
|
2894
|
+
if (!payload.sub || !payload.email) {
|
|
2895
|
+
throw new errors.AuthenticationError("Google IAP token payload is missing sub and/or email claim");
|
|
2761
2896
|
}
|
|
2897
|
+
return {
|
|
2898
|
+
iapToken: {
|
|
2899
|
+
...payload,
|
|
2900
|
+
sub: payload.sub,
|
|
2901
|
+
email: payload.email
|
|
2902
|
+
}
|
|
2903
|
+
};
|
|
2762
2904
|
}
|
|
2905
|
+
const defaultAuthHandler = async ({
|
|
2906
|
+
iapToken
|
|
2907
|
+
}) => ({ profile: { email: iapToken.email } });
|
|
2763
2908
|
|
|
2764
|
-
|
|
2765
|
-
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
this.
|
|
2769
|
-
this.
|
|
2770
|
-
this.
|
|
2771
|
-
|
|
2772
|
-
static async create(settings) {
|
|
2773
|
-
const { path, timeout, ...firestoreSettings } = settings != null ? settings : {};
|
|
2774
|
-
const database = new firestore.Firestore(firestoreSettings);
|
|
2775
|
-
return new FirestoreKeyStore(database, path != null ? path : DEFAULT_DOCUMENT_PATH, timeout != null ? timeout : DEFAULT_TIMEOUT_MS);
|
|
2909
|
+
class GcpIapProvider {
|
|
2910
|
+
constructor(options) {
|
|
2911
|
+
this.authHandler = options.authHandler;
|
|
2912
|
+
this.signInResolver = options.signInResolver;
|
|
2913
|
+
this.tokenValidator = options.tokenValidator;
|
|
2914
|
+
this.tokenIssuer = options.tokenIssuer;
|
|
2915
|
+
this.catalogIdentityClient = options.catalogIdentityClient;
|
|
2916
|
+
this.logger = options.logger;
|
|
2776
2917
|
}
|
|
2777
|
-
|
|
2778
|
-
try {
|
|
2779
|
-
await keyStore.verify();
|
|
2780
|
-
} catch (error) {
|
|
2781
|
-
if (process.env.NODE_ENV !== "development") {
|
|
2782
|
-
throw new Error(`Failed to connect to database: ${error.message}`);
|
|
2783
|
-
}
|
|
2784
|
-
logger == null ? void 0 : logger.warn(`Failed to connect to database: ${error.message}`);
|
|
2785
|
-
}
|
|
2918
|
+
async start() {
|
|
2786
2919
|
}
|
|
2787
|
-
async
|
|
2788
|
-
await this.withTimeout(this.database.collection(this.path).doc(key.kid).set({
|
|
2789
|
-
kid: key.kid,
|
|
2790
|
-
key: JSON.stringify(key)
|
|
2791
|
-
}));
|
|
2920
|
+
async frameHandler() {
|
|
2792
2921
|
}
|
|
2793
|
-
async
|
|
2794
|
-
const
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
|
|
2798
|
-
|
|
2799
|
-
}))
|
|
2922
|
+
async refresh(req, res) {
|
|
2923
|
+
const result = await parseRequestToken(req.header(IAP_JWT_HEADER), this.tokenValidator);
|
|
2924
|
+
const context = {
|
|
2925
|
+
logger: this.logger,
|
|
2926
|
+
catalogIdentityClient: this.catalogIdentityClient,
|
|
2927
|
+
tokenIssuer: this.tokenIssuer
|
|
2800
2928
|
};
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
reject(new Error(`Operation timed out after ${this.timeout}ms`));
|
|
2810
|
-
}, this.timeout));
|
|
2811
|
-
return Promise.race([operation, timer]);
|
|
2812
|
-
}
|
|
2813
|
-
async verify() {
|
|
2814
|
-
await this.withTimeout(this.database.collection(this.path).limit(1).get());
|
|
2929
|
+
const { profile } = await this.authHandler(result, context);
|
|
2930
|
+
const backstageIdentity = await this.signInResolver({ profile, result }, context);
|
|
2931
|
+
const response = {
|
|
2932
|
+
providerInfo: { iapToken: result.iapToken },
|
|
2933
|
+
profile,
|
|
2934
|
+
backstageIdentity: prepareBackstageIdentityResponse(backstageIdentity)
|
|
2935
|
+
};
|
|
2936
|
+
res.json(response);
|
|
2815
2937
|
}
|
|
2816
2938
|
}
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
static async fromConfig(config, options) {
|
|
2939
|
+
function createGcpIapProvider(options) {
|
|
2940
|
+
return ({ config, tokenIssuer, catalogApi, logger, tokenManager }) => {
|
|
2820
2941
|
var _a;
|
|
2821
|
-
const
|
|
2822
|
-
const
|
|
2823
|
-
const
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
const keyStore = await FirestoreKeyStore.create(lodash.pickBy({
|
|
2839
|
-
projectId: settings == null ? void 0 : settings.getOptionalString("projectId"),
|
|
2840
|
-
keyFilename: settings == null ? void 0 : settings.getOptionalString("keyFilename"),
|
|
2841
|
-
host: settings == null ? void 0 : settings.getOptionalString("host"),
|
|
2842
|
-
port: settings == null ? void 0 : settings.getOptionalNumber("port"),
|
|
2843
|
-
ssl: settings == null ? void 0 : settings.getOptionalBoolean("ssl"),
|
|
2844
|
-
path: settings == null ? void 0 : settings.getOptionalString("path"),
|
|
2845
|
-
timeout: settings == null ? void 0 : settings.getOptionalNumber("timeout")
|
|
2846
|
-
}, (value) => value !== void 0));
|
|
2847
|
-
await FirestoreKeyStore.verifyConnection(keyStore, logger);
|
|
2848
|
-
return keyStore;
|
|
2849
|
-
}
|
|
2850
|
-
throw new Error(`Unknown KeyStore provider: ${provider}`);
|
|
2851
|
-
}
|
|
2942
|
+
const audience = config.getString("audience");
|
|
2943
|
+
const authHandler = (_a = options.authHandler) != null ? _a : defaultAuthHandler;
|
|
2944
|
+
const signInResolver = options.signIn.resolver;
|
|
2945
|
+
const tokenValidator = createTokenValidator(audience);
|
|
2946
|
+
const catalogIdentityClient = new CatalogIdentityClient({
|
|
2947
|
+
catalogApi,
|
|
2948
|
+
tokenManager
|
|
2949
|
+
});
|
|
2950
|
+
return new GcpIapProvider({
|
|
2951
|
+
authHandler,
|
|
2952
|
+
signInResolver,
|
|
2953
|
+
tokenValidator,
|
|
2954
|
+
tokenIssuer,
|
|
2955
|
+
catalogIdentityClient,
|
|
2956
|
+
logger
|
|
2957
|
+
});
|
|
2958
|
+
};
|
|
2852
2959
|
}
|
|
2853
2960
|
|
|
2961
|
+
const factories = {
|
|
2962
|
+
google: createGoogleProvider(),
|
|
2963
|
+
github: createGithubProvider(),
|
|
2964
|
+
gitlab: createGitlabProvider(),
|
|
2965
|
+
saml: createSamlProvider(),
|
|
2966
|
+
okta: createOktaProvider(),
|
|
2967
|
+
auth0: createAuth0Provider(),
|
|
2968
|
+
microsoft: createMicrosoftProvider(),
|
|
2969
|
+
oauth2: createOAuth2Provider(),
|
|
2970
|
+
oidc: createOidcProvider(),
|
|
2971
|
+
onelogin: createOneLoginProvider(),
|
|
2972
|
+
awsalb: createAwsAlbProvider(),
|
|
2973
|
+
bitbucket: createBitbucketProvider(),
|
|
2974
|
+
atlassian: createAtlassianProvider()
|
|
2975
|
+
};
|
|
2976
|
+
|
|
2854
2977
|
async function createRouter(options) {
|
|
2855
|
-
const {
|
|
2978
|
+
const {
|
|
2979
|
+
logger,
|
|
2980
|
+
config,
|
|
2981
|
+
discovery,
|
|
2982
|
+
database,
|
|
2983
|
+
tokenManager,
|
|
2984
|
+
providerFactories
|
|
2985
|
+
} = options;
|
|
2856
2986
|
const router = Router__default["default"]();
|
|
2857
2987
|
const appUrl = config.getString("app.baseUrl");
|
|
2858
2988
|
const authUrl = await discovery.getExternalBaseUrl("auth");
|
|
@@ -2898,6 +3028,7 @@ async function createRouter(options) {
|
|
|
2898
3028
|
globalConfig: { baseUrl: authUrl, appUrl, isOriginAllowed },
|
|
2899
3029
|
config: providersConfig.getConfig(providerId),
|
|
2900
3030
|
logger,
|
|
3031
|
+
tokenManager,
|
|
2901
3032
|
tokenIssuer,
|
|
2902
3033
|
discovery,
|
|
2903
3034
|
catalogApi
|
|
@@ -2969,6 +3100,7 @@ exports.createGitlabProvider = createGitlabProvider;
|
|
|
2969
3100
|
exports.createGoogleProvider = createGoogleProvider;
|
|
2970
3101
|
exports.createMicrosoftProvider = createMicrosoftProvider;
|
|
2971
3102
|
exports.createOAuth2Provider = createOAuth2Provider;
|
|
3103
|
+
exports.createOauth2ProxyProvider = createOauth2ProxyProvider;
|
|
2972
3104
|
exports.createOidcProvider = createOidcProvider;
|
|
2973
3105
|
exports.createOktaProvider = createOktaProvider;
|
|
2974
3106
|
exports.createOneLoginProvider = createOneLoginProvider;
|