@backstage/plugin-auth-backend 0.0.0-nightly-202191922036 → 0.0.0-nightly-2021101122259
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 +37 -6
- package/config.d.ts +27 -0
- package/dist/index.cjs.js +309 -15
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +156 -97
- package/package.json +10 -9
package/CHANGELOG.md
CHANGED
|
@@ -1,16 +1,47 @@
|
|
|
1
1
|
# @backstage/plugin-auth-backend
|
|
2
2
|
|
|
3
|
-
## 0.0.0-nightly-
|
|
3
|
+
## 0.0.0-nightly-2021101122259
|
|
4
4
|
|
|
5
5
|
### Patch Changes
|
|
6
6
|
|
|
7
|
+
- 5ee31f860b: Only use settings that have a value when creating a new FirestoreKeyStore instance
|
|
8
|
+
- 3e0e2f09d5: Added forwarding of the `audience` option for the SAML provider, making it possible to enable `audience` verification.
|
|
9
|
+
- Updated dependencies
|
|
10
|
+
- @backstage/backend-common@0.0.0-nightly-2021101122259
|
|
11
|
+
- @backstage/test-utils@0.0.0-nightly-2021101122259
|
|
12
|
+
- @backstage/catalog-client@0.0.0-nightly-2021101122259
|
|
13
|
+
|
|
14
|
+
## 0.4.6
|
|
15
|
+
|
|
16
|
+
### Patch Changes
|
|
17
|
+
|
|
18
|
+
- 3b767f19c9: Allow OAuth state to be encoded by a stateEncoder.
|
|
19
|
+
- Updated dependencies
|
|
20
|
+
- @backstage/test-utils@0.1.20
|
|
21
|
+
- @backstage/config@0.1.11
|
|
22
|
+
- @backstage/errors@0.1.4
|
|
23
|
+
- @backstage/backend-common@0.9.8
|
|
24
|
+
- @backstage/catalog-model@0.9.6
|
|
25
|
+
|
|
26
|
+
## 0.4.5
|
|
27
|
+
|
|
28
|
+
### Patch Changes
|
|
29
|
+
|
|
30
|
+
- 9322e632e9: Require that audience URLs for Okta authentication start with https
|
|
7
31
|
- de3e26aecc: Fix a bug preventing an access token to be refreshed a second time with the GitHub provider.
|
|
32
|
+
- ab9b4a6ea6: Add Firestore as key-store provider.
|
|
33
|
+
Add `auth.keyStore` section to application config.
|
|
34
|
+
- 202f322927: Atlassian auth provider
|
|
35
|
+
|
|
36
|
+
- AtlassianAuth added to core-app-api
|
|
37
|
+
- Atlassian provider added to plugin-auth-backend
|
|
38
|
+
- Updated user-settings with Atlassian connection
|
|
39
|
+
|
|
40
|
+
- 36e67d2f24: Internal updates to apply more strict checks to throw errors.
|
|
8
41
|
- Updated dependencies
|
|
9
|
-
- @backstage/backend-common@0.
|
|
10
|
-
- @backstage/
|
|
11
|
-
- @backstage/catalog-model@0.
|
|
12
|
-
- @backstage/errors@0.0.0-nightly-202191922036
|
|
13
|
-
- @backstage/test-utils@0.0.0-nightly-202191922036
|
|
42
|
+
- @backstage/backend-common@0.9.7
|
|
43
|
+
- @backstage/errors@0.1.3
|
|
44
|
+
- @backstage/catalog-model@0.9.5
|
|
14
45
|
|
|
15
46
|
## 0.4.4
|
|
16
47
|
|
package/config.d.ts
CHANGED
|
@@ -31,6 +31,32 @@ export interface Config {
|
|
|
31
31
|
secret?: string;
|
|
32
32
|
};
|
|
33
33
|
|
|
34
|
+
/** To control how to store JWK data in auth-backend */
|
|
35
|
+
keyStore?: {
|
|
36
|
+
provider?: 'database' | 'memory' | 'firestore';
|
|
37
|
+
firestore?: {
|
|
38
|
+
/** The host to connect to */
|
|
39
|
+
host?: string;
|
|
40
|
+
/** The port to connect to */
|
|
41
|
+
port?: number;
|
|
42
|
+
/** Whether to use SSL when connecting. */
|
|
43
|
+
ssl?: boolean;
|
|
44
|
+
/** The Google Cloud Project ID */
|
|
45
|
+
projectId?: string;
|
|
46
|
+
/**
|
|
47
|
+
* Local file containing the Service Account credentials.
|
|
48
|
+
* You can omit this value to automatically read from
|
|
49
|
+
* GOOGLE_APPLICATION_CREDENTIALS env which is useful for local
|
|
50
|
+
* development.
|
|
51
|
+
*/
|
|
52
|
+
keyFilename?: string;
|
|
53
|
+
/** The path to use for the collection. Defaults to 'sessions' */
|
|
54
|
+
path?: string;
|
|
55
|
+
/** Timeout used for database operations. Defaults to 10000ms */
|
|
56
|
+
timeout?: number;
|
|
57
|
+
};
|
|
58
|
+
};
|
|
59
|
+
|
|
34
60
|
/**
|
|
35
61
|
* The available auth-provider options and attributes
|
|
36
62
|
*/
|
|
@@ -49,6 +75,7 @@ export interface Config {
|
|
|
49
75
|
logoutUrl?: string;
|
|
50
76
|
issuer: string;
|
|
51
77
|
cert: string;
|
|
78
|
+
audience?: string;
|
|
52
79
|
privateKey?: string;
|
|
53
80
|
authnContext?: string[];
|
|
54
81
|
identifierFormat?: string;
|
package/dist/index.cjs.js
CHANGED
|
@@ -29,6 +29,8 @@ var catalogClient = require('@backstage/catalog-client');
|
|
|
29
29
|
var uuid = require('uuid');
|
|
30
30
|
var luxon = require('luxon');
|
|
31
31
|
var backendCommon = require('@backstage/backend-common');
|
|
32
|
+
var firestore = require('@google-cloud/firestore');
|
|
33
|
+
var lodash = require('lodash');
|
|
32
34
|
var session = require('express-session');
|
|
33
35
|
var passport = require('passport');
|
|
34
36
|
var minimatch = require('minimatch');
|
|
@@ -417,12 +419,10 @@ class OAuthAdapter {
|
|
|
417
419
|
response
|
|
418
420
|
});
|
|
419
421
|
} catch (error) {
|
|
422
|
+
const {name, message} = errors.isError(error) ? error : new Error("Encountered invalid error");
|
|
420
423
|
return postMessageResponse(res, appOrigin, {
|
|
421
424
|
type: "authorization_response",
|
|
422
|
-
error: {
|
|
423
|
-
name: error.name,
|
|
424
|
-
message: error.message
|
|
425
|
-
}
|
|
425
|
+
error: {name, message}
|
|
426
426
|
});
|
|
427
427
|
}
|
|
428
428
|
}
|
|
@@ -458,7 +458,7 @@ class OAuthAdapter {
|
|
|
458
458
|
}
|
|
459
459
|
res.status(200).json(response);
|
|
460
460
|
} catch (error) {
|
|
461
|
-
res.status(401).send(
|
|
461
|
+
res.status(401).send(String(error));
|
|
462
462
|
}
|
|
463
463
|
}
|
|
464
464
|
async populateIdentity(identity) {
|
|
@@ -551,6 +551,7 @@ class GithubAuthProvider {
|
|
|
551
551
|
constructor(options) {
|
|
552
552
|
this.signInResolver = options.signInResolver;
|
|
553
553
|
this.authHandler = options.authHandler;
|
|
554
|
+
this.stateEncoder = options.stateEncoder;
|
|
554
555
|
this.tokenIssuer = options.tokenIssuer;
|
|
555
556
|
this.catalogIdentityClient = options.catalogIdentityClient;
|
|
556
557
|
this.logger = options.logger;
|
|
@@ -568,7 +569,7 @@ class GithubAuthProvider {
|
|
|
568
569
|
async start(req) {
|
|
569
570
|
return await executeRedirectStrategy(req, this._strategy, {
|
|
570
571
|
scope: req.scope,
|
|
571
|
-
state:
|
|
572
|
+
state: (await this.stateEncoder(req)).encodedState
|
|
572
573
|
});
|
|
573
574
|
}
|
|
574
575
|
async handler(req) {
|
|
@@ -634,7 +635,7 @@ const createGithubProvider = (options) => {
|
|
|
634
635
|
catalogApi,
|
|
635
636
|
logger
|
|
636
637
|
}) => OAuthEnvironmentHandler.mapConfig(config, (envConfig) => {
|
|
637
|
-
var _a, _b;
|
|
638
|
+
var _a, _b, _c;
|
|
638
639
|
const clientId = envConfig.getString("clientId");
|
|
639
640
|
const clientSecret = envConfig.getString("clientSecret");
|
|
640
641
|
const enterpriseInstanceUrl = envConfig.getOptionalString("enterpriseInstanceUrl");
|
|
@@ -656,6 +657,9 @@ const createGithubProvider = (options) => {
|
|
|
656
657
|
tokenIssuer,
|
|
657
658
|
logger
|
|
658
659
|
});
|
|
660
|
+
const stateEncoder = (_c = options == null ? void 0 : options.stateEncoder) != null ? _c : async (req) => {
|
|
661
|
+
return {encodedState: encodeState(req.state)};
|
|
662
|
+
};
|
|
659
663
|
const provider = new GithubAuthProvider({
|
|
660
664
|
clientId,
|
|
661
665
|
clientSecret,
|
|
@@ -667,6 +671,7 @@ const createGithubProvider = (options) => {
|
|
|
667
671
|
authHandler,
|
|
668
672
|
tokenIssuer,
|
|
669
673
|
catalogIdentityClient,
|
|
674
|
+
stateEncoder,
|
|
670
675
|
logger
|
|
671
676
|
});
|
|
672
677
|
return OAuthAdapter.fromConfig(globalConfig, provider, {
|
|
@@ -1384,6 +1389,9 @@ const createOktaProvider = (_options) => {
|
|
|
1384
1389
|
const clientSecret = envConfig.getString("clientSecret");
|
|
1385
1390
|
const audience = envConfig.getString("audience");
|
|
1386
1391
|
const callbackUrl = `${globalConfig.baseUrl}/${providerId}/handler/frame`;
|
|
1392
|
+
if (!audience.startsWith("https://")) {
|
|
1393
|
+
throw new Error("URL for 'audience' must start with 'https://'.");
|
|
1394
|
+
}
|
|
1387
1395
|
const catalogIdentityClient = new CatalogIdentityClient({
|
|
1388
1396
|
catalogApi,
|
|
1389
1397
|
tokenIssuer
|
|
@@ -1555,6 +1563,177 @@ const createBitbucketProvider = (options) => {
|
|
|
1555
1563
|
});
|
|
1556
1564
|
};
|
|
1557
1565
|
|
|
1566
|
+
const defaultScopes = ["offline_access", "read:me"];
|
|
1567
|
+
class AtlassianStrategy extends OAuth2Strategy__default['default'] {
|
|
1568
|
+
constructor(options, verify) {
|
|
1569
|
+
if (!options.scope) {
|
|
1570
|
+
throw new TypeError("Atlassian requires a scope option");
|
|
1571
|
+
}
|
|
1572
|
+
const scopes = options.scope.split(" ");
|
|
1573
|
+
const optionsWithURLs = {
|
|
1574
|
+
...options,
|
|
1575
|
+
authorizationURL: `https://auth.atlassian.com/authorize`,
|
|
1576
|
+
tokenURL: `https://auth.atlassian.com/oauth/token`,
|
|
1577
|
+
scope: Array.from(new Set([...defaultScopes, ...scopes]))
|
|
1578
|
+
};
|
|
1579
|
+
super(optionsWithURLs, verify);
|
|
1580
|
+
this.profileURL = "https://api.atlassian.com/me";
|
|
1581
|
+
this.name = "atlassian";
|
|
1582
|
+
this._oauth2.useAuthorizationHeaderforGET(true);
|
|
1583
|
+
}
|
|
1584
|
+
authorizationParams() {
|
|
1585
|
+
return {
|
|
1586
|
+
audience: "api.atlassian.com",
|
|
1587
|
+
prompt: "consent"
|
|
1588
|
+
};
|
|
1589
|
+
}
|
|
1590
|
+
userProfile(accessToken, done) {
|
|
1591
|
+
this._oauth2.get(this.profileURL, accessToken, (err, body) => {
|
|
1592
|
+
if (err) {
|
|
1593
|
+
return done(new OAuth2Strategy.InternalOAuthError("Failed to fetch user profile", err.statusCode));
|
|
1594
|
+
}
|
|
1595
|
+
if (!body) {
|
|
1596
|
+
return done(new Error("Failed to fetch user profile, body cannot be empty"));
|
|
1597
|
+
}
|
|
1598
|
+
try {
|
|
1599
|
+
const json = typeof body !== "string" ? body.toString() : body;
|
|
1600
|
+
const profile = AtlassianStrategy.parse(json);
|
|
1601
|
+
return done(null, profile);
|
|
1602
|
+
} catch (e) {
|
|
1603
|
+
return done(new Error("Failed to parse user profile"));
|
|
1604
|
+
}
|
|
1605
|
+
});
|
|
1606
|
+
}
|
|
1607
|
+
static parse(json) {
|
|
1608
|
+
const resp = JSON.parse(json);
|
|
1609
|
+
return {
|
|
1610
|
+
id: resp.account_id,
|
|
1611
|
+
provider: "atlassian",
|
|
1612
|
+
username: resp.nickname,
|
|
1613
|
+
displayName: resp.name,
|
|
1614
|
+
emails: [{value: resp.email}],
|
|
1615
|
+
photos: [{value: resp.picture}]
|
|
1616
|
+
};
|
|
1617
|
+
}
|
|
1618
|
+
}
|
|
1619
|
+
|
|
1620
|
+
const atlassianDefaultAuthHandler = async ({
|
|
1621
|
+
fullProfile,
|
|
1622
|
+
params
|
|
1623
|
+
}) => ({
|
|
1624
|
+
profile: makeProfileInfo(fullProfile, params.id_token)
|
|
1625
|
+
});
|
|
1626
|
+
class AtlassianAuthProvider {
|
|
1627
|
+
constructor(options) {
|
|
1628
|
+
this.catalogIdentityClient = options.catalogIdentityClient;
|
|
1629
|
+
this.logger = options.logger;
|
|
1630
|
+
this.tokenIssuer = options.tokenIssuer;
|
|
1631
|
+
this.authHandler = options.authHandler;
|
|
1632
|
+
this.signInResolver = options.signInResolver;
|
|
1633
|
+
this._strategy = new AtlassianStrategy({
|
|
1634
|
+
clientID: options.clientId,
|
|
1635
|
+
clientSecret: options.clientSecret,
|
|
1636
|
+
callbackURL: options.callbackUrl,
|
|
1637
|
+
scope: options.scopes
|
|
1638
|
+
}, (accessToken, refreshToken, params, fullProfile, done) => {
|
|
1639
|
+
done(void 0, {
|
|
1640
|
+
fullProfile,
|
|
1641
|
+
accessToken,
|
|
1642
|
+
refreshToken,
|
|
1643
|
+
params
|
|
1644
|
+
});
|
|
1645
|
+
});
|
|
1646
|
+
}
|
|
1647
|
+
async start(req) {
|
|
1648
|
+
return await executeRedirectStrategy(req, this._strategy, {
|
|
1649
|
+
state: encodeState(req.state)
|
|
1650
|
+
});
|
|
1651
|
+
}
|
|
1652
|
+
async handler(req) {
|
|
1653
|
+
var _a;
|
|
1654
|
+
const {result} = await executeFrameHandlerStrategy(req, this._strategy);
|
|
1655
|
+
return {
|
|
1656
|
+
response: await this.handleResult(result),
|
|
1657
|
+
refreshToken: (_a = result.refreshToken) != null ? _a : ""
|
|
1658
|
+
};
|
|
1659
|
+
}
|
|
1660
|
+
async handleResult(result) {
|
|
1661
|
+
const {profile} = await this.authHandler(result);
|
|
1662
|
+
const response = {
|
|
1663
|
+
providerInfo: {
|
|
1664
|
+
idToken: result.params.id_token,
|
|
1665
|
+
accessToken: result.accessToken,
|
|
1666
|
+
refreshToken: result.refreshToken,
|
|
1667
|
+
scope: result.params.scope,
|
|
1668
|
+
expiresInSeconds: result.params.expires_in
|
|
1669
|
+
},
|
|
1670
|
+
profile
|
|
1671
|
+
};
|
|
1672
|
+
if (this.signInResolver) {
|
|
1673
|
+
response.backstageIdentity = await this.signInResolver({
|
|
1674
|
+
result,
|
|
1675
|
+
profile
|
|
1676
|
+
}, {
|
|
1677
|
+
tokenIssuer: this.tokenIssuer,
|
|
1678
|
+
catalogIdentityClient: this.catalogIdentityClient,
|
|
1679
|
+
logger: this.logger
|
|
1680
|
+
});
|
|
1681
|
+
}
|
|
1682
|
+
return response;
|
|
1683
|
+
}
|
|
1684
|
+
async refresh(req) {
|
|
1685
|
+
const {
|
|
1686
|
+
accessToken,
|
|
1687
|
+
params,
|
|
1688
|
+
refreshToken: newRefreshToken
|
|
1689
|
+
} = await executeRefreshTokenStrategy(this._strategy, req.refreshToken, req.scope);
|
|
1690
|
+
const fullProfile = await executeFetchUserProfileStrategy(this._strategy, accessToken);
|
|
1691
|
+
return this.handleResult({
|
|
1692
|
+
fullProfile,
|
|
1693
|
+
params,
|
|
1694
|
+
accessToken,
|
|
1695
|
+
refreshToken: newRefreshToken
|
|
1696
|
+
});
|
|
1697
|
+
}
|
|
1698
|
+
}
|
|
1699
|
+
const createAtlassianProvider = (options) => {
|
|
1700
|
+
return ({
|
|
1701
|
+
providerId,
|
|
1702
|
+
globalConfig,
|
|
1703
|
+
config,
|
|
1704
|
+
tokenIssuer,
|
|
1705
|
+
catalogApi,
|
|
1706
|
+
logger
|
|
1707
|
+
}) => OAuthEnvironmentHandler.mapConfig(config, (envConfig) => {
|
|
1708
|
+
var _a, _b;
|
|
1709
|
+
const clientId = envConfig.getString("clientId");
|
|
1710
|
+
const clientSecret = envConfig.getString("clientSecret");
|
|
1711
|
+
const scopes = envConfig.getString("scopes");
|
|
1712
|
+
const callbackUrl = `${globalConfig.baseUrl}/${providerId}/handler/frame`;
|
|
1713
|
+
const catalogIdentityClient = new CatalogIdentityClient({
|
|
1714
|
+
catalogApi,
|
|
1715
|
+
tokenIssuer
|
|
1716
|
+
});
|
|
1717
|
+
const authHandler = (_a = options == null ? void 0 : options.authHandler) != null ? _a : atlassianDefaultAuthHandler;
|
|
1718
|
+
const provider = new AtlassianAuthProvider({
|
|
1719
|
+
clientId,
|
|
1720
|
+
clientSecret,
|
|
1721
|
+
scopes,
|
|
1722
|
+
callbackUrl,
|
|
1723
|
+
authHandler,
|
|
1724
|
+
signInResolver: (_b = options == null ? void 0 : options.signIn) == null ? void 0 : _b.resolver,
|
|
1725
|
+
catalogIdentityClient,
|
|
1726
|
+
logger,
|
|
1727
|
+
tokenIssuer
|
|
1728
|
+
});
|
|
1729
|
+
return OAuthAdapter.fromConfig(globalConfig, provider, {
|
|
1730
|
+
disableRefresh: true,
|
|
1731
|
+
providerId,
|
|
1732
|
+
tokenIssuer
|
|
1733
|
+
});
|
|
1734
|
+
});
|
|
1735
|
+
};
|
|
1736
|
+
|
|
1558
1737
|
const ALB_JWT_HEADER = "x-amzn-oidc-data";
|
|
1559
1738
|
const ALB_ACCESSTOKEN_HEADER = "x-amzn-oidc-accesstoken";
|
|
1560
1739
|
const getJWTHeaders = (input) => {
|
|
@@ -1835,12 +2014,10 @@ class SamlAuthProvider {
|
|
|
1835
2014
|
}
|
|
1836
2015
|
});
|
|
1837
2016
|
} catch (error) {
|
|
2017
|
+
const {name, message} = errors.isError(error) ? error : new Error("Encountered invalid error");
|
|
1838
2018
|
return postMessageResponse(res, this.appUrl, {
|
|
1839
2019
|
type: "authorization_response",
|
|
1840
|
-
error: {
|
|
1841
|
-
name: error.name,
|
|
1842
|
-
message: error.message
|
|
1843
|
-
}
|
|
2020
|
+
error: {name, message}
|
|
1844
2021
|
});
|
|
1845
2022
|
}
|
|
1846
2023
|
}
|
|
@@ -1857,6 +2034,7 @@ const createSamlProvider = (_options) => {
|
|
|
1857
2034
|
callbackUrl: `${globalConfig.baseUrl}/${providerId}/handler/frame`,
|
|
1858
2035
|
entryPoint: config.getString("entryPoint"),
|
|
1859
2036
|
logoutUrl: config.getOptionalString("logoutUrl"),
|
|
2037
|
+
audience: config.getOptionalString("audience"),
|
|
1860
2038
|
issuer: config.getString("issuer"),
|
|
1861
2039
|
cert: config.getString("cert"),
|
|
1862
2040
|
privateCert: config.getOptionalString("privateKey"),
|
|
@@ -2070,7 +2248,8 @@ const factories = {
|
|
|
2070
2248
|
oidc: createOidcProvider(),
|
|
2071
2249
|
onelogin: createOneLoginProvider(),
|
|
2072
2250
|
awsalb: createAwsAlbProvider(),
|
|
2073
|
-
bitbucket: createBitbucketProvider()
|
|
2251
|
+
bitbucket: createBitbucketProvider(),
|
|
2252
|
+
atlassian: createAtlassianProvider()
|
|
2074
2253
|
};
|
|
2075
2254
|
|
|
2076
2255
|
function createOidcRouter(options) {
|
|
@@ -2289,6 +2468,121 @@ class DatabaseKeyStore {
|
|
|
2289
2468
|
}
|
|
2290
2469
|
}
|
|
2291
2470
|
|
|
2471
|
+
class MemoryKeyStore {
|
|
2472
|
+
constructor() {
|
|
2473
|
+
this.keys = new Map();
|
|
2474
|
+
}
|
|
2475
|
+
async addKey(key) {
|
|
2476
|
+
this.keys.set(key.kid, {
|
|
2477
|
+
createdAt: luxon.DateTime.utc().toJSDate(),
|
|
2478
|
+
key: JSON.stringify(key)
|
|
2479
|
+
});
|
|
2480
|
+
}
|
|
2481
|
+
async removeKeys(kids) {
|
|
2482
|
+
for (const kid of kids) {
|
|
2483
|
+
this.keys.delete(kid);
|
|
2484
|
+
}
|
|
2485
|
+
}
|
|
2486
|
+
async listKeys() {
|
|
2487
|
+
return {
|
|
2488
|
+
items: Array.from(this.keys).map(([, {createdAt, key: keyStr}]) => ({
|
|
2489
|
+
createdAt,
|
|
2490
|
+
key: JSON.parse(keyStr)
|
|
2491
|
+
}))
|
|
2492
|
+
};
|
|
2493
|
+
}
|
|
2494
|
+
}
|
|
2495
|
+
|
|
2496
|
+
const DEFAULT_TIMEOUT_MS = 1e4;
|
|
2497
|
+
const DEFAULT_DOCUMENT_PATH = "sessions";
|
|
2498
|
+
class FirestoreKeyStore {
|
|
2499
|
+
constructor(database, path, timeout) {
|
|
2500
|
+
this.database = database;
|
|
2501
|
+
this.path = path;
|
|
2502
|
+
this.timeout = timeout;
|
|
2503
|
+
}
|
|
2504
|
+
static async create(settings) {
|
|
2505
|
+
const {path, timeout, ...firestoreSettings} = settings != null ? settings : {};
|
|
2506
|
+
const database = new firestore.Firestore(firestoreSettings);
|
|
2507
|
+
return new FirestoreKeyStore(database, path != null ? path : DEFAULT_DOCUMENT_PATH, timeout != null ? timeout : DEFAULT_TIMEOUT_MS);
|
|
2508
|
+
}
|
|
2509
|
+
static async verifyConnection(keyStore, logger) {
|
|
2510
|
+
try {
|
|
2511
|
+
await keyStore.verify();
|
|
2512
|
+
} catch (error) {
|
|
2513
|
+
if (process.env.NODE_ENV !== "development") {
|
|
2514
|
+
throw new Error(`Failed to connect to database: ${error.message}`);
|
|
2515
|
+
}
|
|
2516
|
+
logger == null ? void 0 : logger.warn(`Failed to connect to database: ${error.message}`);
|
|
2517
|
+
}
|
|
2518
|
+
}
|
|
2519
|
+
async addKey(key) {
|
|
2520
|
+
await this.withTimeout(this.database.collection(this.path).doc(key.kid).set({
|
|
2521
|
+
kid: key.kid,
|
|
2522
|
+
key: JSON.stringify(key)
|
|
2523
|
+
}));
|
|
2524
|
+
}
|
|
2525
|
+
async listKeys() {
|
|
2526
|
+
const keys = await this.withTimeout(this.database.collection(this.path).get());
|
|
2527
|
+
return {
|
|
2528
|
+
items: keys.docs.map((key) => ({
|
|
2529
|
+
key: key.data(),
|
|
2530
|
+
createdAt: key.createTime.toDate()
|
|
2531
|
+
}))
|
|
2532
|
+
};
|
|
2533
|
+
}
|
|
2534
|
+
async removeKeys(kids) {
|
|
2535
|
+
for (const kid of kids) {
|
|
2536
|
+
await this.withTimeout(this.database.collection(this.path).doc(kid).delete());
|
|
2537
|
+
}
|
|
2538
|
+
}
|
|
2539
|
+
async withTimeout(operation) {
|
|
2540
|
+
const timer = new Promise((_, reject) => setTimeout(() => {
|
|
2541
|
+
reject(new Error(`Operation timed out after ${this.timeout}ms`));
|
|
2542
|
+
}, this.timeout));
|
|
2543
|
+
return Promise.race([operation, timer]);
|
|
2544
|
+
}
|
|
2545
|
+
async verify() {
|
|
2546
|
+
await this.withTimeout(this.database.collection(this.path).limit(1).get());
|
|
2547
|
+
}
|
|
2548
|
+
}
|
|
2549
|
+
|
|
2550
|
+
class KeyStores {
|
|
2551
|
+
static async fromConfig(config, options) {
|
|
2552
|
+
var _a;
|
|
2553
|
+
const {logger, database} = options != null ? options : {};
|
|
2554
|
+
const ks = config.getOptionalConfig("auth.keyStore");
|
|
2555
|
+
const provider = (_a = ks == null ? void 0 : ks.getOptionalString("provider")) != null ? _a : "database";
|
|
2556
|
+
logger == null ? void 0 : logger.info(`Configuring "${provider}" as KeyStore provider`);
|
|
2557
|
+
if (provider === "database") {
|
|
2558
|
+
if (!database) {
|
|
2559
|
+
throw new Error("This KeyStore provider requires a database");
|
|
2560
|
+
}
|
|
2561
|
+
return await DatabaseKeyStore.create({
|
|
2562
|
+
database: await database.getClient()
|
|
2563
|
+
});
|
|
2564
|
+
}
|
|
2565
|
+
if (provider === "memory") {
|
|
2566
|
+
return new MemoryKeyStore();
|
|
2567
|
+
}
|
|
2568
|
+
if (provider === "firestore") {
|
|
2569
|
+
const settings = ks == null ? void 0 : ks.getConfig(provider);
|
|
2570
|
+
const keyStore = await FirestoreKeyStore.create(lodash.pickBy({
|
|
2571
|
+
projectId: settings == null ? void 0 : settings.getOptionalString("projectId"),
|
|
2572
|
+
keyFilename: settings == null ? void 0 : settings.getOptionalString("keyFilename"),
|
|
2573
|
+
host: settings == null ? void 0 : settings.getOptionalString("host"),
|
|
2574
|
+
port: settings == null ? void 0 : settings.getOptionalNumber("port"),
|
|
2575
|
+
ssl: settings == null ? void 0 : settings.getOptionalBoolean("ssl"),
|
|
2576
|
+
path: settings == null ? void 0 : settings.getOptionalString("path"),
|
|
2577
|
+
timeout: settings == null ? void 0 : settings.getOptionalNumber("timeout")
|
|
2578
|
+
}, (value) => value !== void 0));
|
|
2579
|
+
await FirestoreKeyStore.verifyConnection(keyStore, logger);
|
|
2580
|
+
return keyStore;
|
|
2581
|
+
}
|
|
2582
|
+
throw new Error(`Unknown KeyStore provider: ${provider}`);
|
|
2583
|
+
}
|
|
2584
|
+
}
|
|
2585
|
+
|
|
2292
2586
|
async function createRouter({
|
|
2293
2587
|
logger,
|
|
2294
2588
|
config,
|
|
@@ -2299,10 +2593,8 @@ async function createRouter({
|
|
|
2299
2593
|
const router = Router__default['default']();
|
|
2300
2594
|
const appUrl = config.getString("app.baseUrl");
|
|
2301
2595
|
const authUrl = await discovery.getExternalBaseUrl("auth");
|
|
2596
|
+
const keyStore = await KeyStores.fromConfig(config, {logger, database});
|
|
2302
2597
|
const keyDurationSeconds = 3600;
|
|
2303
|
-
const keyStore = await DatabaseKeyStore.create({
|
|
2304
|
-
database: await database.getClient()
|
|
2305
|
-
});
|
|
2306
2598
|
const tokenIssuer = new TokenFactory({
|
|
2307
2599
|
issuer: authUrl,
|
|
2308
2600
|
keyStore,
|
|
@@ -2353,6 +2645,7 @@ async function createRouter({
|
|
|
2353
2645
|
}
|
|
2354
2646
|
router.use(`/${providerId}`, r);
|
|
2355
2647
|
} catch (e) {
|
|
2648
|
+
errors.assertError(e);
|
|
2356
2649
|
if (process.env.NODE_ENV !== "development") {
|
|
2357
2650
|
throw new Error(`Failed to initialize ${providerId} auth provider, ${e.message}`);
|
|
2358
2651
|
}
|
|
@@ -2396,6 +2689,7 @@ exports.OAuthAdapter = OAuthAdapter;
|
|
|
2396
2689
|
exports.OAuthEnvironmentHandler = OAuthEnvironmentHandler;
|
|
2397
2690
|
exports.bitbucketUserIdSignInResolver = bitbucketUserIdSignInResolver;
|
|
2398
2691
|
exports.bitbucketUsernameSignInResolver = bitbucketUsernameSignInResolver;
|
|
2692
|
+
exports.createAtlassianProvider = createAtlassianProvider;
|
|
2399
2693
|
exports.createAwsAlbProvider = createAwsAlbProvider;
|
|
2400
2694
|
exports.createBitbucketProvider = createBitbucketProvider;
|
|
2401
2695
|
exports.createGithubProvider = createGithubProvider;
|