@backstage/plugin-auth-backend 0.4.6 → 0.4.10
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 +58 -0
- package/README.md +6 -6
- package/config.d.ts +1 -0
- package/dist/index.cjs.js +266 -178
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +40 -1
- package/package.json +9 -9
package/dist/index.cjs.js
CHANGED
|
@@ -17,12 +17,12 @@ var passportGoogleOauth20 = require('passport-google-oauth20');
|
|
|
17
17
|
var passportMicrosoft = require('passport-microsoft');
|
|
18
18
|
var got = require('got');
|
|
19
19
|
var OAuth2Strategy = require('passport-oauth2');
|
|
20
|
+
var openidClient = require('openid-client');
|
|
20
21
|
var passportOktaOauth = require('passport-okta-oauth');
|
|
21
22
|
var passportBitbucketOauth2 = require('passport-bitbucket-oauth2');
|
|
22
|
-
var fetch = require('
|
|
23
|
+
var fetch = require('node-fetch');
|
|
23
24
|
var NodeCache = require('node-cache');
|
|
24
25
|
var jose = require('jose');
|
|
25
|
-
var openidClient = require('openid-client');
|
|
26
26
|
var passportSaml = require('passport-saml');
|
|
27
27
|
var passportOneloginOauth = require('passport-onelogin-oauth');
|
|
28
28
|
var catalogClient = require('@backstage/catalog-client');
|
|
@@ -30,6 +30,7 @@ var uuid = require('uuid');
|
|
|
30
30
|
var luxon = require('luxon');
|
|
31
31
|
var backendCommon = require('@backstage/backend-common');
|
|
32
32
|
var firestore = require('@google-cloud/firestore');
|
|
33
|
+
var lodash = require('lodash');
|
|
33
34
|
var session = require('express-session');
|
|
34
35
|
var passport = require('passport');
|
|
35
36
|
var minimatch = require('minimatch');
|
|
@@ -226,22 +227,22 @@ class OAuthEnvironmentHandler {
|
|
|
226
227
|
return new OAuthEnvironmentHandler(handlers);
|
|
227
228
|
}
|
|
228
229
|
async start(req, res) {
|
|
229
|
-
const provider = this.getProviderForEnv(req
|
|
230
|
-
await
|
|
230
|
+
const provider = this.getProviderForEnv(req);
|
|
231
|
+
await provider.start(req, res);
|
|
231
232
|
}
|
|
232
233
|
async frameHandler(req, res) {
|
|
233
|
-
const provider = this.getProviderForEnv(req
|
|
234
|
-
await
|
|
234
|
+
const provider = this.getProviderForEnv(req);
|
|
235
|
+
await provider.frameHandler(req, res);
|
|
235
236
|
}
|
|
236
237
|
async refresh(req, res) {
|
|
237
238
|
var _a;
|
|
238
|
-
const provider = this.getProviderForEnv(req
|
|
239
|
-
await ((_a = provider
|
|
239
|
+
const provider = this.getProviderForEnv(req);
|
|
240
|
+
await ((_a = provider.refresh) == null ? void 0 : _a.call(provider, req, res));
|
|
240
241
|
}
|
|
241
242
|
async logout(req, res) {
|
|
242
243
|
var _a;
|
|
243
|
-
const provider = this.getProviderForEnv(req
|
|
244
|
-
await ((_a = provider
|
|
244
|
+
const provider = this.getProviderForEnv(req);
|
|
245
|
+
await ((_a = provider.logout) == null ? void 0 : _a.call(provider, req, res));
|
|
245
246
|
}
|
|
246
247
|
getRequestFromEnv(req) {
|
|
247
248
|
var _a, _b;
|
|
@@ -256,19 +257,16 @@ class OAuthEnvironmentHandler {
|
|
|
256
257
|
const env = readState(stateParams).env;
|
|
257
258
|
return env;
|
|
258
259
|
}
|
|
259
|
-
getProviderForEnv(req
|
|
260
|
+
getProviderForEnv(req) {
|
|
260
261
|
const env = this.getRequestFromEnv(req);
|
|
261
262
|
if (!env) {
|
|
262
263
|
throw new errors.InputError(`Must specify 'env' query to select environment`);
|
|
263
264
|
}
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
<br>
|
|
268
|
-
For this flow to work you need to supply a valid configuration for the "${env}" environment of provider.`);
|
|
269
|
-
return void 0;
|
|
265
|
+
const handler = this.handlers.get(env);
|
|
266
|
+
if (!handler) {
|
|
267
|
+
throw new errors.NotFoundError(`No configuration available for the '${env}' environment of this provider.`);
|
|
270
268
|
}
|
|
271
|
-
return
|
|
269
|
+
return handler;
|
|
272
270
|
}
|
|
273
271
|
}
|
|
274
272
|
|
|
@@ -427,26 +425,23 @@ class OAuthAdapter {
|
|
|
427
425
|
}
|
|
428
426
|
async logout(req, res) {
|
|
429
427
|
if (!ensuresXRequestedWith(req)) {
|
|
430
|
-
|
|
431
|
-
return;
|
|
428
|
+
throw new errors.AuthenticationError("Invalid X-Requested-With header");
|
|
432
429
|
}
|
|
433
430
|
this.removeRefreshTokenCookie(res);
|
|
434
|
-
res.status(200).
|
|
431
|
+
res.status(200).end();
|
|
435
432
|
}
|
|
436
433
|
async refresh(req, res) {
|
|
437
434
|
var _a, _b;
|
|
438
435
|
if (!ensuresXRequestedWith(req)) {
|
|
439
|
-
|
|
440
|
-
return;
|
|
436
|
+
throw new errors.AuthenticationError("Invalid X-Requested-With header");
|
|
441
437
|
}
|
|
442
438
|
if (!this.handlers.refresh || this.options.disableRefresh) {
|
|
443
|
-
|
|
444
|
-
return;
|
|
439
|
+
throw new errors.InputError(`Refresh token is not supported for provider ${this.options.providerId}`);
|
|
445
440
|
}
|
|
446
441
|
try {
|
|
447
442
|
const refreshToken = req.cookies[`${this.options.providerId}-refresh-token`];
|
|
448
443
|
if (!refreshToken) {
|
|
449
|
-
throw new
|
|
444
|
+
throw new errors.InputError("Missing session cookie");
|
|
450
445
|
}
|
|
451
446
|
const scope = (_b = (_a = req.query.scope) == null ? void 0 : _a.toString()) != null ? _b : "";
|
|
452
447
|
const forwardReq = Object.assign(req, {scope, refreshToken});
|
|
@@ -457,17 +452,19 @@ class OAuthAdapter {
|
|
|
457
452
|
}
|
|
458
453
|
res.status(200).json(response);
|
|
459
454
|
} catch (error) {
|
|
460
|
-
|
|
455
|
+
throw new errors.AuthenticationError("Refresh failed", error);
|
|
461
456
|
}
|
|
462
457
|
}
|
|
463
458
|
async populateIdentity(identity) {
|
|
464
459
|
if (!identity) {
|
|
465
460
|
return;
|
|
466
461
|
}
|
|
467
|
-
if (!identity.idToken) {
|
|
468
|
-
identity.
|
|
462
|
+
if (!(identity.token || identity.idToken)) {
|
|
463
|
+
identity.token = await this.options.tokenIssuer.issueToken({
|
|
469
464
|
claims: {sub: identity.id}
|
|
470
465
|
});
|
|
466
|
+
} else if (!identity.token && identity.idToken) {
|
|
467
|
+
identity.token = identity.idToken;
|
|
471
468
|
}
|
|
472
469
|
}
|
|
473
470
|
}
|
|
@@ -1137,7 +1134,10 @@ class OAuth2AuthProvider {
|
|
|
1137
1134
|
authorizationURL: options.authorizationUrl,
|
|
1138
1135
|
tokenURL: options.tokenUrl,
|
|
1139
1136
|
passReqToCallback: false,
|
|
1140
|
-
scope: options.scope
|
|
1137
|
+
scope: options.scope,
|
|
1138
|
+
customHeaders: {
|
|
1139
|
+
Authorization: `Basic ${this.encodeClientCredentials(options.clientId, options.clientSecret)}`
|
|
1140
|
+
}
|
|
1141
1141
|
}, (accessToken, refreshToken, params, fullProfile, done) => {
|
|
1142
1142
|
done(void 0, {
|
|
1143
1143
|
fullProfile,
|
|
@@ -1203,8 +1203,11 @@ class OAuth2AuthProvider {
|
|
|
1203
1203
|
}
|
|
1204
1204
|
return response;
|
|
1205
1205
|
}
|
|
1206
|
+
encodeClientCredentials(clientID, clientSecret) {
|
|
1207
|
+
return Buffer.from(`${clientID}:${clientSecret}`).toString("base64");
|
|
1208
|
+
}
|
|
1206
1209
|
}
|
|
1207
|
-
const oAuth2DefaultSignInResolver = async (info, ctx) => {
|
|
1210
|
+
const oAuth2DefaultSignInResolver$1 = async (info, ctx) => {
|
|
1208
1211
|
const {profile} = info;
|
|
1209
1212
|
if (!profile.email) {
|
|
1210
1213
|
throw new Error("Profile contained no email");
|
|
@@ -1239,7 +1242,7 @@ const createOAuth2Provider = (options) => {
|
|
|
1239
1242
|
const authHandler = (options == null ? void 0 : options.authHandler) ? options.authHandler : async ({fullProfile, params}) => ({
|
|
1240
1243
|
profile: makeProfileInfo(fullProfile, params.id_token)
|
|
1241
1244
|
});
|
|
1242
|
-
const signInResolverFn = (_c = (_b = options == null ? void 0 : options.signIn) == null ? void 0 : _b.resolver) != null ? _c : oAuth2DefaultSignInResolver;
|
|
1245
|
+
const signInResolverFn = (_c = (_b = options == null ? void 0 : options.signIn) == null ? void 0 : _b.resolver) != null ? _c : oAuth2DefaultSignInResolver$1;
|
|
1243
1246
|
const signInResolver = (info) => signInResolverFn(info, {
|
|
1244
1247
|
catalogIdentityClient,
|
|
1245
1248
|
tokenIssuer,
|
|
@@ -1266,6 +1269,168 @@ const createOAuth2Provider = (options) => {
|
|
|
1266
1269
|
});
|
|
1267
1270
|
};
|
|
1268
1271
|
|
|
1272
|
+
class OidcAuthProvider {
|
|
1273
|
+
constructor(options) {
|
|
1274
|
+
this.implementation = this.setupStrategy(options);
|
|
1275
|
+
this.scope = options.scope;
|
|
1276
|
+
this.prompt = options.prompt;
|
|
1277
|
+
this.signInResolver = options.signInResolver;
|
|
1278
|
+
this.authHandler = options.authHandler;
|
|
1279
|
+
this.tokenIssuer = options.tokenIssuer;
|
|
1280
|
+
this.catalogIdentityClient = options.catalogIdentityClient;
|
|
1281
|
+
this.logger = options.logger;
|
|
1282
|
+
}
|
|
1283
|
+
async start(req) {
|
|
1284
|
+
const {strategy} = await this.implementation;
|
|
1285
|
+
const options = {
|
|
1286
|
+
scope: req.scope || this.scope || "openid profile email",
|
|
1287
|
+
state: encodeState(req.state)
|
|
1288
|
+
};
|
|
1289
|
+
const prompt = this.prompt || "none";
|
|
1290
|
+
if (prompt !== "auto") {
|
|
1291
|
+
options.prompt = prompt;
|
|
1292
|
+
}
|
|
1293
|
+
return await executeRedirectStrategy(req, strategy, options);
|
|
1294
|
+
}
|
|
1295
|
+
async handler(req) {
|
|
1296
|
+
const {strategy} = await this.implementation;
|
|
1297
|
+
const strategyResponse = await executeFrameHandlerStrategy(req, strategy);
|
|
1298
|
+
const {
|
|
1299
|
+
result: {userinfo, tokenset},
|
|
1300
|
+
privateInfo
|
|
1301
|
+
} = strategyResponse;
|
|
1302
|
+
const identityResponse = await this.handleResult({tokenset, userinfo});
|
|
1303
|
+
return {
|
|
1304
|
+
response: identityResponse,
|
|
1305
|
+
refreshToken: privateInfo.refreshToken
|
|
1306
|
+
};
|
|
1307
|
+
}
|
|
1308
|
+
async refresh(req) {
|
|
1309
|
+
const {client} = await this.implementation;
|
|
1310
|
+
const tokenset = await client.refresh(req.refreshToken);
|
|
1311
|
+
if (!tokenset.access_token) {
|
|
1312
|
+
throw new Error("Refresh failed");
|
|
1313
|
+
}
|
|
1314
|
+
const profile = await client.userinfo(tokenset.access_token);
|
|
1315
|
+
return this.handleResult({tokenset, userinfo: profile});
|
|
1316
|
+
}
|
|
1317
|
+
async setupStrategy(options) {
|
|
1318
|
+
const issuer = await openidClient.Issuer.discover(options.metadataUrl);
|
|
1319
|
+
const client = new issuer.Client({
|
|
1320
|
+
access_type: "offline",
|
|
1321
|
+
client_id: options.clientId,
|
|
1322
|
+
client_secret: options.clientSecret,
|
|
1323
|
+
redirect_uris: [options.callbackUrl],
|
|
1324
|
+
response_types: ["code"],
|
|
1325
|
+
id_token_signed_response_alg: options.tokenSignedResponseAlg || "RS256",
|
|
1326
|
+
scope: options.scope || ""
|
|
1327
|
+
});
|
|
1328
|
+
const strategy = new openidClient.Strategy({
|
|
1329
|
+
client,
|
|
1330
|
+
passReqToCallback: false
|
|
1331
|
+
}, (tokenset, userinfo, done) => {
|
|
1332
|
+
if (typeof done !== "function") {
|
|
1333
|
+
throw new Error("OIDC IdP must provide a userinfo_endpoint in the metadata response");
|
|
1334
|
+
}
|
|
1335
|
+
done(void 0, {tokenset, userinfo}, {
|
|
1336
|
+
refreshToken: tokenset.refresh_token
|
|
1337
|
+
});
|
|
1338
|
+
});
|
|
1339
|
+
strategy.error = console.error;
|
|
1340
|
+
return {strategy, client};
|
|
1341
|
+
}
|
|
1342
|
+
async handleResult(result) {
|
|
1343
|
+
const {profile} = await this.authHandler(result);
|
|
1344
|
+
const response = {
|
|
1345
|
+
providerInfo: {
|
|
1346
|
+
idToken: result.tokenset.id_token,
|
|
1347
|
+
accessToken: result.tokenset.access_token,
|
|
1348
|
+
refreshToken: result.tokenset.refresh_token,
|
|
1349
|
+
scope: result.tokenset.scope,
|
|
1350
|
+
expiresInSeconds: result.tokenset.expires_in
|
|
1351
|
+
},
|
|
1352
|
+
profile
|
|
1353
|
+
};
|
|
1354
|
+
if (this.signInResolver) {
|
|
1355
|
+
response.backstageIdentity = await this.signInResolver({
|
|
1356
|
+
result,
|
|
1357
|
+
profile
|
|
1358
|
+
}, {
|
|
1359
|
+
tokenIssuer: this.tokenIssuer,
|
|
1360
|
+
catalogIdentityClient: this.catalogIdentityClient,
|
|
1361
|
+
logger: this.logger
|
|
1362
|
+
});
|
|
1363
|
+
}
|
|
1364
|
+
return response;
|
|
1365
|
+
}
|
|
1366
|
+
}
|
|
1367
|
+
const oAuth2DefaultSignInResolver = async (info, ctx) => {
|
|
1368
|
+
const {profile} = info;
|
|
1369
|
+
if (!profile.email) {
|
|
1370
|
+
throw new Error("Profile contained no email");
|
|
1371
|
+
}
|
|
1372
|
+
const userId = profile.email.split("@")[0];
|
|
1373
|
+
const token = await ctx.tokenIssuer.issueToken({
|
|
1374
|
+
claims: {sub: userId, ent: [`user:default/${userId}`]}
|
|
1375
|
+
});
|
|
1376
|
+
return {id: userId, token};
|
|
1377
|
+
};
|
|
1378
|
+
const createOidcProvider = (options) => {
|
|
1379
|
+
return ({
|
|
1380
|
+
providerId,
|
|
1381
|
+
globalConfig,
|
|
1382
|
+
config,
|
|
1383
|
+
tokenIssuer,
|
|
1384
|
+
catalogApi,
|
|
1385
|
+
logger
|
|
1386
|
+
}) => OAuthEnvironmentHandler.mapConfig(config, (envConfig) => {
|
|
1387
|
+
var _a, _b;
|
|
1388
|
+
const clientId = envConfig.getString("clientId");
|
|
1389
|
+
const clientSecret = envConfig.getString("clientSecret");
|
|
1390
|
+
const callbackUrl = `${globalConfig.baseUrl}/${providerId}/handler/frame`;
|
|
1391
|
+
const metadataUrl = envConfig.getString("metadataUrl");
|
|
1392
|
+
const tokenSignedResponseAlg = envConfig.getOptionalString("tokenSignedResponseAlg");
|
|
1393
|
+
const scope = envConfig.getOptionalString("scope");
|
|
1394
|
+
const prompt = envConfig.getOptionalString("prompt");
|
|
1395
|
+
const catalogIdentityClient = new CatalogIdentityClient({
|
|
1396
|
+
catalogApi,
|
|
1397
|
+
tokenIssuer
|
|
1398
|
+
});
|
|
1399
|
+
const authHandler = (options == null ? void 0 : options.authHandler) ? options.authHandler : async ({userinfo}) => ({
|
|
1400
|
+
profile: {
|
|
1401
|
+
displayName: userinfo.name,
|
|
1402
|
+
email: userinfo.email,
|
|
1403
|
+
picture: userinfo.picture
|
|
1404
|
+
}
|
|
1405
|
+
});
|
|
1406
|
+
const signInResolverFn = (_b = (_a = options == null ? void 0 : options.signIn) == null ? void 0 : _a.resolver) != null ? _b : oAuth2DefaultSignInResolver;
|
|
1407
|
+
const signInResolver = (info) => signInResolverFn(info, {
|
|
1408
|
+
catalogIdentityClient,
|
|
1409
|
+
tokenIssuer,
|
|
1410
|
+
logger
|
|
1411
|
+
});
|
|
1412
|
+
const provider = new OidcAuthProvider({
|
|
1413
|
+
clientId,
|
|
1414
|
+
clientSecret,
|
|
1415
|
+
callbackUrl,
|
|
1416
|
+
tokenSignedResponseAlg,
|
|
1417
|
+
metadataUrl,
|
|
1418
|
+
scope,
|
|
1419
|
+
prompt,
|
|
1420
|
+
signInResolver,
|
|
1421
|
+
authHandler,
|
|
1422
|
+
logger,
|
|
1423
|
+
tokenIssuer,
|
|
1424
|
+
catalogIdentityClient
|
|
1425
|
+
});
|
|
1426
|
+
return OAuthAdapter.fromConfig(globalConfig, provider, {
|
|
1427
|
+
disableRefresh: false,
|
|
1428
|
+
providerId,
|
|
1429
|
+
tokenIssuer
|
|
1430
|
+
});
|
|
1431
|
+
});
|
|
1432
|
+
};
|
|
1433
|
+
|
|
1269
1434
|
class OktaAuthProvider {
|
|
1270
1435
|
constructor(options) {
|
|
1271
1436
|
this._store = {
|
|
@@ -1861,131 +2026,14 @@ const createAwsAlbProvider = (options) => {
|
|
|
1861
2026
|
};
|
|
1862
2027
|
};
|
|
1863
2028
|
|
|
1864
|
-
class OidcAuthProvider {
|
|
1865
|
-
constructor(options) {
|
|
1866
|
-
this.implementation = this.setupStrategy(options);
|
|
1867
|
-
this.scope = options.scope;
|
|
1868
|
-
this.prompt = options.prompt;
|
|
1869
|
-
}
|
|
1870
|
-
async start(req) {
|
|
1871
|
-
const {strategy} = await this.implementation;
|
|
1872
|
-
const options = {
|
|
1873
|
-
accessType: "offline",
|
|
1874
|
-
scope: req.scope || this.scope || "openid profile email",
|
|
1875
|
-
state: encodeState(req.state)
|
|
1876
|
-
};
|
|
1877
|
-
const prompt = this.prompt || "none";
|
|
1878
|
-
if (prompt !== "auto") {
|
|
1879
|
-
options.prompt = prompt;
|
|
1880
|
-
}
|
|
1881
|
-
return await executeRedirectStrategy(req, strategy, options);
|
|
1882
|
-
}
|
|
1883
|
-
async handler(req) {
|
|
1884
|
-
const {strategy} = await this.implementation;
|
|
1885
|
-
const strategyResponse = await executeFrameHandlerStrategy(req, strategy);
|
|
1886
|
-
const {
|
|
1887
|
-
result: {userinfo, tokenset},
|
|
1888
|
-
privateInfo
|
|
1889
|
-
} = strategyResponse;
|
|
1890
|
-
const identityResponse = await this.populateIdentity({
|
|
1891
|
-
profile: {
|
|
1892
|
-
displayName: userinfo.name,
|
|
1893
|
-
email: userinfo.email,
|
|
1894
|
-
picture: userinfo.picture
|
|
1895
|
-
},
|
|
1896
|
-
providerInfo: {
|
|
1897
|
-
idToken: tokenset.id_token,
|
|
1898
|
-
accessToken: tokenset.access_token || "",
|
|
1899
|
-
scope: tokenset.scope || "",
|
|
1900
|
-
expiresInSeconds: tokenset.expires_in
|
|
1901
|
-
}
|
|
1902
|
-
});
|
|
1903
|
-
return {
|
|
1904
|
-
response: identityResponse,
|
|
1905
|
-
refreshToken: privateInfo.refreshToken
|
|
1906
|
-
};
|
|
1907
|
-
}
|
|
1908
|
-
async refresh(req) {
|
|
1909
|
-
const {client} = await this.implementation;
|
|
1910
|
-
const tokenset = await client.refresh(req.refreshToken);
|
|
1911
|
-
if (!tokenset.access_token) {
|
|
1912
|
-
throw new Error("Refresh failed");
|
|
1913
|
-
}
|
|
1914
|
-
const profile = await client.userinfo(tokenset.access_token);
|
|
1915
|
-
return this.populateIdentity({
|
|
1916
|
-
providerInfo: {
|
|
1917
|
-
accessToken: tokenset.access_token,
|
|
1918
|
-
refreshToken: tokenset.refresh_token,
|
|
1919
|
-
expiresInSeconds: tokenset.expires_in,
|
|
1920
|
-
idToken: tokenset.id_token,
|
|
1921
|
-
scope: tokenset.scope || ""
|
|
1922
|
-
},
|
|
1923
|
-
profile
|
|
1924
|
-
});
|
|
1925
|
-
}
|
|
1926
|
-
async setupStrategy(options) {
|
|
1927
|
-
const issuer = await openidClient.Issuer.discover(options.metadataUrl);
|
|
1928
|
-
const client = new issuer.Client({
|
|
1929
|
-
client_id: options.clientId,
|
|
1930
|
-
client_secret: options.clientSecret,
|
|
1931
|
-
redirect_uris: [options.callbackUrl],
|
|
1932
|
-
response_types: ["code"],
|
|
1933
|
-
id_token_signed_response_alg: options.tokenSignedResponseAlg || "RS256",
|
|
1934
|
-
scope: options.scope || ""
|
|
1935
|
-
});
|
|
1936
|
-
const strategy = new openidClient.Strategy({
|
|
1937
|
-
client,
|
|
1938
|
-
passReqToCallback: false
|
|
1939
|
-
}, (tokenset, userinfo, done) => {
|
|
1940
|
-
if (typeof done !== "function") {
|
|
1941
|
-
throw new Error("OIDC IdP must provide a userinfo_endpoint in the metadata response");
|
|
1942
|
-
}
|
|
1943
|
-
done(void 0, {tokenset, userinfo}, {
|
|
1944
|
-
refreshToken: tokenset.refresh_token
|
|
1945
|
-
});
|
|
1946
|
-
});
|
|
1947
|
-
strategy.error = console.error;
|
|
1948
|
-
return {strategy, client};
|
|
1949
|
-
}
|
|
1950
|
-
async populateIdentity(response) {
|
|
1951
|
-
const {profile} = response;
|
|
1952
|
-
if (!profile.email) {
|
|
1953
|
-
throw new Error("Profile does not contain an email");
|
|
1954
|
-
}
|
|
1955
|
-
const id = profile.email.split("@")[0];
|
|
1956
|
-
return {...response, backstageIdentity: {id}};
|
|
1957
|
-
}
|
|
1958
|
-
}
|
|
1959
|
-
const createOidcProvider = (_options) => {
|
|
1960
|
-
return ({providerId, globalConfig, config, tokenIssuer}) => OAuthEnvironmentHandler.mapConfig(config, (envConfig) => {
|
|
1961
|
-
const clientId = envConfig.getString("clientId");
|
|
1962
|
-
const clientSecret = envConfig.getString("clientSecret");
|
|
1963
|
-
const callbackUrl = `${globalConfig.baseUrl}/${providerId}/handler/frame`;
|
|
1964
|
-
const metadataUrl = envConfig.getString("metadataUrl");
|
|
1965
|
-
const tokenSignedResponseAlg = envConfig.getOptionalString("tokenSignedResponseAlg");
|
|
1966
|
-
const scope = envConfig.getOptionalString("scope");
|
|
1967
|
-
const prompt = envConfig.getOptionalString("prompt");
|
|
1968
|
-
const provider = new OidcAuthProvider({
|
|
1969
|
-
clientId,
|
|
1970
|
-
clientSecret,
|
|
1971
|
-
callbackUrl,
|
|
1972
|
-
tokenSignedResponseAlg,
|
|
1973
|
-
metadataUrl,
|
|
1974
|
-
scope,
|
|
1975
|
-
prompt
|
|
1976
|
-
});
|
|
1977
|
-
return OAuthAdapter.fromConfig(globalConfig, provider, {
|
|
1978
|
-
disableRefresh: false,
|
|
1979
|
-
providerId,
|
|
1980
|
-
tokenIssuer
|
|
1981
|
-
});
|
|
1982
|
-
});
|
|
1983
|
-
};
|
|
1984
|
-
|
|
1985
2029
|
class SamlAuthProvider {
|
|
1986
2030
|
constructor(options) {
|
|
1987
2031
|
this.appUrl = options.appUrl;
|
|
2032
|
+
this.signInResolver = options.signInResolver;
|
|
2033
|
+
this.authHandler = options.authHandler;
|
|
1988
2034
|
this.tokenIssuer = options.tokenIssuer;
|
|
2035
|
+
this.catalogIdentityClient = options.catalogIdentityClient;
|
|
2036
|
+
this.logger = options.logger;
|
|
1989
2037
|
this.strategy = new passportSaml.Strategy({...options}, (fullProfile, done) => {
|
|
1990
2038
|
done(void 0, {fullProfile});
|
|
1991
2039
|
});
|
|
@@ -1997,20 +2045,24 @@ class SamlAuthProvider {
|
|
|
1997
2045
|
async frameHandler(req, res) {
|
|
1998
2046
|
try {
|
|
1999
2047
|
const {result} = await executeFrameHandlerStrategy(req, this.strategy);
|
|
2000
|
-
const
|
|
2001
|
-
const
|
|
2002
|
-
|
|
2003
|
-
|
|
2048
|
+
const {profile} = await this.authHandler(result);
|
|
2049
|
+
const response = {
|
|
2050
|
+
profile,
|
|
2051
|
+
providerInfo: {}
|
|
2052
|
+
};
|
|
2053
|
+
if (this.signInResolver) {
|
|
2054
|
+
response.backstageIdentity = await this.signInResolver({
|
|
2055
|
+
result,
|
|
2056
|
+
profile
|
|
2057
|
+
}, {
|
|
2058
|
+
tokenIssuer: this.tokenIssuer,
|
|
2059
|
+
catalogIdentityClient: this.catalogIdentityClient,
|
|
2060
|
+
logger: this.logger
|
|
2061
|
+
});
|
|
2062
|
+
}
|
|
2004
2063
|
return postMessageResponse(res, this.appUrl, {
|
|
2005
2064
|
type: "authorization_response",
|
|
2006
|
-
response
|
|
2007
|
-
profile: {
|
|
2008
|
-
email: result.fullProfile.email,
|
|
2009
|
-
displayName: result.fullProfile.displayName
|
|
2010
|
-
},
|
|
2011
|
-
providerInfo: {},
|
|
2012
|
-
backstageIdentity: {id, idToken}
|
|
2013
|
-
}
|
|
2065
|
+
response
|
|
2014
2066
|
});
|
|
2015
2067
|
} catch (error) {
|
|
2016
2068
|
const {name, message} = errors.isError(error) ? error : new Error("Encountered invalid error");
|
|
@@ -2021,21 +2073,50 @@ class SamlAuthProvider {
|
|
|
2021
2073
|
}
|
|
2022
2074
|
}
|
|
2023
2075
|
async logout(_req, res) {
|
|
2024
|
-
res.
|
|
2025
|
-
}
|
|
2026
|
-
identifyEnv() {
|
|
2027
|
-
return void 0;
|
|
2076
|
+
res.end();
|
|
2028
2077
|
}
|
|
2029
2078
|
}
|
|
2030
|
-
const
|
|
2031
|
-
|
|
2032
|
-
|
|
2079
|
+
const samlDefaultSignInResolver = async (info, ctx) => {
|
|
2080
|
+
const id = info.result.fullProfile.nameID;
|
|
2081
|
+
const token = await ctx.tokenIssuer.issueToken({
|
|
2082
|
+
claims: {sub: id}
|
|
2083
|
+
});
|
|
2084
|
+
return {id, token};
|
|
2085
|
+
};
|
|
2086
|
+
const createSamlProvider = (options) => {
|
|
2087
|
+
return ({
|
|
2088
|
+
providerId,
|
|
2089
|
+
globalConfig,
|
|
2090
|
+
config,
|
|
2091
|
+
tokenIssuer,
|
|
2092
|
+
catalogApi,
|
|
2093
|
+
logger
|
|
2094
|
+
}) => {
|
|
2095
|
+
var _a, _b;
|
|
2096
|
+
const catalogIdentityClient = new CatalogIdentityClient({
|
|
2097
|
+
catalogApi,
|
|
2098
|
+
tokenIssuer
|
|
2099
|
+
});
|
|
2100
|
+
const authHandler = (options == null ? void 0 : options.authHandler) ? options.authHandler : async ({fullProfile}) => ({
|
|
2101
|
+
profile: {
|
|
2102
|
+
email: fullProfile.email,
|
|
2103
|
+
displayName: fullProfile.displayName
|
|
2104
|
+
}
|
|
2105
|
+
});
|
|
2106
|
+
const signInResolverFn = (_b = (_a = options == null ? void 0 : options.signIn) == null ? void 0 : _a.resolver) != null ? _b : samlDefaultSignInResolver;
|
|
2107
|
+
const signInResolver = (info) => signInResolverFn(info, {
|
|
2108
|
+
catalogIdentityClient,
|
|
2109
|
+
tokenIssuer,
|
|
2110
|
+
logger
|
|
2111
|
+
});
|
|
2112
|
+
return new SamlAuthProvider({
|
|
2033
2113
|
callbackUrl: `${globalConfig.baseUrl}/${providerId}/handler/frame`,
|
|
2034
2114
|
entryPoint: config.getString("entryPoint"),
|
|
2035
2115
|
logoutUrl: config.getOptionalString("logoutUrl"),
|
|
2116
|
+
audience: config.getOptionalString("audience"),
|
|
2036
2117
|
issuer: config.getString("issuer"),
|
|
2037
2118
|
cert: config.getString("cert"),
|
|
2038
|
-
|
|
2119
|
+
privateKey: config.getOptionalString("privateKey"),
|
|
2039
2120
|
authnContext: config.getOptionalStringArray("authnContext"),
|
|
2040
2121
|
identifierFormat: config.getOptionalString("identifierFormat"),
|
|
2041
2122
|
decryptionPvk: config.getOptionalString("decryptionPvk"),
|
|
@@ -2043,9 +2124,12 @@ const createSamlProvider = (_options) => {
|
|
|
2043
2124
|
digestAlgorithm: config.getOptionalString("digestAlgorithm"),
|
|
2044
2125
|
acceptedClockSkewMs: config.getOptionalNumber("acceptedClockSkewMs"),
|
|
2045
2126
|
tokenIssuer,
|
|
2046
|
-
appUrl: globalConfig.appUrl
|
|
2047
|
-
|
|
2048
|
-
|
|
2127
|
+
appUrl: globalConfig.appUrl,
|
|
2128
|
+
authHandler,
|
|
2129
|
+
signInResolver,
|
|
2130
|
+
logger,
|
|
2131
|
+
catalogIdentityClient
|
|
2132
|
+
});
|
|
2049
2133
|
};
|
|
2050
2134
|
};
|
|
2051
2135
|
|
|
@@ -2565,7 +2649,7 @@ class KeyStores {
|
|
|
2565
2649
|
}
|
|
2566
2650
|
if (provider === "firestore") {
|
|
2567
2651
|
const settings = ks == null ? void 0 : ks.getConfig(provider);
|
|
2568
|
-
const keyStore = await FirestoreKeyStore.create({
|
|
2652
|
+
const keyStore = await FirestoreKeyStore.create(lodash.pickBy({
|
|
2569
2653
|
projectId: settings == null ? void 0 : settings.getOptionalString("projectId"),
|
|
2570
2654
|
keyFilename: settings == null ? void 0 : settings.getOptionalString("keyFilename"),
|
|
2571
2655
|
host: settings == null ? void 0 : settings.getOptionalString("host"),
|
|
@@ -2573,7 +2657,7 @@ class KeyStores {
|
|
|
2573
2657
|
ssl: settings == null ? void 0 : settings.getOptionalBoolean("ssl"),
|
|
2574
2658
|
path: settings == null ? void 0 : settings.getOptionalString("path"),
|
|
2575
2659
|
timeout: settings == null ? void 0 : settings.getOptionalNumber("timeout")
|
|
2576
|
-
});
|
|
2660
|
+
}, (value) => value !== void 0));
|
|
2577
2661
|
await FirestoreKeyStore.verifyConnection(keyStore, logger);
|
|
2578
2662
|
return keyStore;
|
|
2579
2663
|
}
|
|
@@ -2682,6 +2766,7 @@ function createOriginFilter(config) {
|
|
|
2682
2766
|
};
|
|
2683
2767
|
}
|
|
2684
2768
|
|
|
2769
|
+
exports.CatalogIdentityClient = CatalogIdentityClient;
|
|
2685
2770
|
exports.IdentityClient = IdentityClient;
|
|
2686
2771
|
exports.OAuthAdapter = OAuthAdapter;
|
|
2687
2772
|
exports.OAuthEnvironmentHandler = OAuthEnvironmentHandler;
|
|
@@ -2695,12 +2780,15 @@ exports.createGitlabProvider = createGitlabProvider;
|
|
|
2695
2780
|
exports.createGoogleProvider = createGoogleProvider;
|
|
2696
2781
|
exports.createMicrosoftProvider = createMicrosoftProvider;
|
|
2697
2782
|
exports.createOAuth2Provider = createOAuth2Provider;
|
|
2783
|
+
exports.createOidcProvider = createOidcProvider;
|
|
2698
2784
|
exports.createOktaProvider = createOktaProvider;
|
|
2699
2785
|
exports.createOriginFilter = createOriginFilter;
|
|
2700
2786
|
exports.createRouter = createRouter;
|
|
2787
|
+
exports.createSamlProvider = createSamlProvider;
|
|
2701
2788
|
exports.defaultAuthProviderFactories = factories;
|
|
2702
2789
|
exports.encodeState = encodeState;
|
|
2703
2790
|
exports.ensuresXRequestedWith = ensuresXRequestedWith;
|
|
2791
|
+
exports.getEntityClaims = getEntityClaims;
|
|
2704
2792
|
exports.googleEmailSignInResolver = googleEmailSignInResolver;
|
|
2705
2793
|
exports.microsoftEmailSignInResolver = microsoftEmailSignInResolver;
|
|
2706
2794
|
exports.oktaEmailSignInResolver = oktaEmailSignInResolver;
|