@backstage/plugin-auth-backend 0.4.4 → 0.4.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,59 @@
1
1
  # @backstage/plugin-auth-backend
2
2
 
3
+ ## 0.4.8
4
+
5
+ ### Patch Changes
6
+
7
+ - 892c1d9202: Update OAuthAdapter to create identity.token from identity.idToken if it does not exist, and prevent overwrites to identity.toke. Update login page commonProvider to prefer .token over .idToken
8
+ - Updated dependencies
9
+ - @backstage/catalog-client@0.5.2
10
+ - @backstage/catalog-model@0.9.7
11
+ - @backstage/backend-common@0.9.10
12
+ - @backstage/test-utils@0.1.22
13
+
14
+ ## 0.4.7
15
+
16
+ ### Patch Changes
17
+
18
+ - 5ee31f860b: Only use settings that have a value when creating a new FirestoreKeyStore instance
19
+ - 3e0e2f09d5: Added forwarding of the `audience` option for the SAML provider, making it possible to enable `audience` verification.
20
+ - Updated dependencies
21
+ - @backstage/backend-common@0.9.9
22
+ - @backstage/test-utils@0.1.21
23
+ - @backstage/catalog-client@0.5.1
24
+
25
+ ## 0.4.6
26
+
27
+ ### Patch Changes
28
+
29
+ - 3b767f19c9: Allow OAuth state to be encoded by a stateEncoder.
30
+ - Updated dependencies
31
+ - @backstage/test-utils@0.1.20
32
+ - @backstage/config@0.1.11
33
+ - @backstage/errors@0.1.4
34
+ - @backstage/backend-common@0.9.8
35
+ - @backstage/catalog-model@0.9.6
36
+
37
+ ## 0.4.5
38
+
39
+ ### Patch Changes
40
+
41
+ - 9322e632e9: Require that audience URLs for Okta authentication start with https
42
+ - de3e26aecc: Fix a bug preventing an access token to be refreshed a second time with the GitHub provider.
43
+ - ab9b4a6ea6: Add Firestore as key-store provider.
44
+ Add `auth.keyStore` section to application config.
45
+ - 202f322927: Atlassian auth provider
46
+
47
+ - AtlassianAuth added to core-app-api
48
+ - Atlassian provider added to plugin-auth-backend
49
+ - Updated user-settings with Atlassian connection
50
+
51
+ - 36e67d2f24: Internal updates to apply more strict checks to throw errors.
52
+ - Updated dependencies
53
+ - @backstage/backend-common@0.9.7
54
+ - @backstage/errors@0.1.3
55
+ - @backstage/catalog-model@0.9.5
56
+
3
57
  ## 0.4.4
4
58
 
5
59
  ### Patch Changes
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,17 +458,19 @@ class OAuthAdapter {
458
458
  }
459
459
  res.status(200).json(response);
460
460
  } catch (error) {
461
- res.status(401).send(`${error.message}`);
461
+ res.status(401).send(String(error));
462
462
  }
463
463
  }
464
464
  async populateIdentity(identity) {
465
465
  if (!identity) {
466
466
  return;
467
467
  }
468
- if (!identity.idToken) {
469
- identity.idToken = await this.options.tokenIssuer.issueToken({
468
+ if (!(identity.token || identity.idToken)) {
469
+ identity.token = await this.options.tokenIssuer.issueToken({
470
470
  claims: {sub: identity.id}
471
471
  });
472
+ } else if (!identity.token && identity.idToken) {
473
+ identity.token = identity.idToken;
472
474
  }
473
475
  }
474
476
  }
@@ -551,6 +553,7 @@ class GithubAuthProvider {
551
553
  constructor(options) {
552
554
  this.signInResolver = options.signInResolver;
553
555
  this.authHandler = options.authHandler;
556
+ this.stateEncoder = options.stateEncoder;
554
557
  this.tokenIssuer = options.tokenIssuer;
555
558
  this.catalogIdentityClient = options.catalogIdentityClient;
556
559
  this.logger = options.logger;
@@ -568,7 +571,7 @@ class GithubAuthProvider {
568
571
  async start(req) {
569
572
  return await executeRedirectStrategy(req, this._strategy, {
570
573
  scope: req.scope,
571
- state: encodeState(req.state)
574
+ state: (await this.stateEncoder(req)).encodedState
572
575
  });
573
576
  }
574
577
  async handler(req) {
@@ -579,13 +582,17 @@ class GithubAuthProvider {
579
582
  };
580
583
  }
581
584
  async refresh(req) {
582
- const {accessToken, params} = await executeRefreshTokenStrategy(this._strategy, req.refreshToken, req.scope);
585
+ const {
586
+ accessToken,
587
+ refreshToken: newRefreshToken,
588
+ params
589
+ } = await executeRefreshTokenStrategy(this._strategy, req.refreshToken, req.scope);
583
590
  const fullProfile = await executeFetchUserProfileStrategy(this._strategy, accessToken);
584
591
  return this.handleResult({
585
592
  fullProfile,
586
593
  params,
587
594
  accessToken,
588
- refreshToken: req.refreshToken
595
+ refreshToken: newRefreshToken
589
596
  });
590
597
  }
591
598
  async handleResult(result) {
@@ -594,6 +601,7 @@ class GithubAuthProvider {
594
601
  const response = {
595
602
  providerInfo: {
596
603
  accessToken: result.accessToken,
604
+ refreshToken: result.refreshToken,
597
605
  scope: result.params.scope,
598
606
  expiresInSeconds: expiresInStr === void 0 ? void 0 : Number(expiresInStr)
599
607
  },
@@ -629,7 +637,7 @@ const createGithubProvider = (options) => {
629
637
  catalogApi,
630
638
  logger
631
639
  }) => OAuthEnvironmentHandler.mapConfig(config, (envConfig) => {
632
- var _a, _b;
640
+ var _a, _b, _c;
633
641
  const clientId = envConfig.getString("clientId");
634
642
  const clientSecret = envConfig.getString("clientSecret");
635
643
  const enterpriseInstanceUrl = envConfig.getOptionalString("enterpriseInstanceUrl");
@@ -651,6 +659,9 @@ const createGithubProvider = (options) => {
651
659
  tokenIssuer,
652
660
  logger
653
661
  });
662
+ const stateEncoder = (_c = options == null ? void 0 : options.stateEncoder) != null ? _c : async (req) => {
663
+ return {encodedState: encodeState(req.state)};
664
+ };
654
665
  const provider = new GithubAuthProvider({
655
666
  clientId,
656
667
  clientSecret,
@@ -662,6 +673,7 @@ const createGithubProvider = (options) => {
662
673
  authHandler,
663
674
  tokenIssuer,
664
675
  catalogIdentityClient,
676
+ stateEncoder,
665
677
  logger
666
678
  });
667
679
  return OAuthAdapter.fromConfig(globalConfig, provider, {
@@ -1379,6 +1391,9 @@ const createOktaProvider = (_options) => {
1379
1391
  const clientSecret = envConfig.getString("clientSecret");
1380
1392
  const audience = envConfig.getString("audience");
1381
1393
  const callbackUrl = `${globalConfig.baseUrl}/${providerId}/handler/frame`;
1394
+ if (!audience.startsWith("https://")) {
1395
+ throw new Error("URL for 'audience' must start with 'https://'.");
1396
+ }
1382
1397
  const catalogIdentityClient = new CatalogIdentityClient({
1383
1398
  catalogApi,
1384
1399
  tokenIssuer
@@ -1550,6 +1565,177 @@ const createBitbucketProvider = (options) => {
1550
1565
  });
1551
1566
  };
1552
1567
 
1568
+ const defaultScopes = ["offline_access", "read:me"];
1569
+ class AtlassianStrategy extends OAuth2Strategy__default['default'] {
1570
+ constructor(options, verify) {
1571
+ if (!options.scope) {
1572
+ throw new TypeError("Atlassian requires a scope option");
1573
+ }
1574
+ const scopes = options.scope.split(" ");
1575
+ const optionsWithURLs = {
1576
+ ...options,
1577
+ authorizationURL: `https://auth.atlassian.com/authorize`,
1578
+ tokenURL: `https://auth.atlassian.com/oauth/token`,
1579
+ scope: Array.from(new Set([...defaultScopes, ...scopes]))
1580
+ };
1581
+ super(optionsWithURLs, verify);
1582
+ this.profileURL = "https://api.atlassian.com/me";
1583
+ this.name = "atlassian";
1584
+ this._oauth2.useAuthorizationHeaderforGET(true);
1585
+ }
1586
+ authorizationParams() {
1587
+ return {
1588
+ audience: "api.atlassian.com",
1589
+ prompt: "consent"
1590
+ };
1591
+ }
1592
+ userProfile(accessToken, done) {
1593
+ this._oauth2.get(this.profileURL, accessToken, (err, body) => {
1594
+ if (err) {
1595
+ return done(new OAuth2Strategy.InternalOAuthError("Failed to fetch user profile", err.statusCode));
1596
+ }
1597
+ if (!body) {
1598
+ return done(new Error("Failed to fetch user profile, body cannot be empty"));
1599
+ }
1600
+ try {
1601
+ const json = typeof body !== "string" ? body.toString() : body;
1602
+ const profile = AtlassianStrategy.parse(json);
1603
+ return done(null, profile);
1604
+ } catch (e) {
1605
+ return done(new Error("Failed to parse user profile"));
1606
+ }
1607
+ });
1608
+ }
1609
+ static parse(json) {
1610
+ const resp = JSON.parse(json);
1611
+ return {
1612
+ id: resp.account_id,
1613
+ provider: "atlassian",
1614
+ username: resp.nickname,
1615
+ displayName: resp.name,
1616
+ emails: [{value: resp.email}],
1617
+ photos: [{value: resp.picture}]
1618
+ };
1619
+ }
1620
+ }
1621
+
1622
+ const atlassianDefaultAuthHandler = async ({
1623
+ fullProfile,
1624
+ params
1625
+ }) => ({
1626
+ profile: makeProfileInfo(fullProfile, params.id_token)
1627
+ });
1628
+ class AtlassianAuthProvider {
1629
+ constructor(options) {
1630
+ this.catalogIdentityClient = options.catalogIdentityClient;
1631
+ this.logger = options.logger;
1632
+ this.tokenIssuer = options.tokenIssuer;
1633
+ this.authHandler = options.authHandler;
1634
+ this.signInResolver = options.signInResolver;
1635
+ this._strategy = new AtlassianStrategy({
1636
+ clientID: options.clientId,
1637
+ clientSecret: options.clientSecret,
1638
+ callbackURL: options.callbackUrl,
1639
+ scope: options.scopes
1640
+ }, (accessToken, refreshToken, params, fullProfile, done) => {
1641
+ done(void 0, {
1642
+ fullProfile,
1643
+ accessToken,
1644
+ refreshToken,
1645
+ params
1646
+ });
1647
+ });
1648
+ }
1649
+ async start(req) {
1650
+ return await executeRedirectStrategy(req, this._strategy, {
1651
+ state: encodeState(req.state)
1652
+ });
1653
+ }
1654
+ async handler(req) {
1655
+ var _a;
1656
+ const {result} = await executeFrameHandlerStrategy(req, this._strategy);
1657
+ return {
1658
+ response: await this.handleResult(result),
1659
+ refreshToken: (_a = result.refreshToken) != null ? _a : ""
1660
+ };
1661
+ }
1662
+ async handleResult(result) {
1663
+ const {profile} = await this.authHandler(result);
1664
+ const response = {
1665
+ providerInfo: {
1666
+ idToken: result.params.id_token,
1667
+ accessToken: result.accessToken,
1668
+ refreshToken: result.refreshToken,
1669
+ scope: result.params.scope,
1670
+ expiresInSeconds: result.params.expires_in
1671
+ },
1672
+ profile
1673
+ };
1674
+ if (this.signInResolver) {
1675
+ response.backstageIdentity = await this.signInResolver({
1676
+ result,
1677
+ profile
1678
+ }, {
1679
+ tokenIssuer: this.tokenIssuer,
1680
+ catalogIdentityClient: this.catalogIdentityClient,
1681
+ logger: this.logger
1682
+ });
1683
+ }
1684
+ return response;
1685
+ }
1686
+ async refresh(req) {
1687
+ const {
1688
+ accessToken,
1689
+ params,
1690
+ refreshToken: newRefreshToken
1691
+ } = await executeRefreshTokenStrategy(this._strategy, req.refreshToken, req.scope);
1692
+ const fullProfile = await executeFetchUserProfileStrategy(this._strategy, accessToken);
1693
+ return this.handleResult({
1694
+ fullProfile,
1695
+ params,
1696
+ accessToken,
1697
+ refreshToken: newRefreshToken
1698
+ });
1699
+ }
1700
+ }
1701
+ const createAtlassianProvider = (options) => {
1702
+ return ({
1703
+ providerId,
1704
+ globalConfig,
1705
+ config,
1706
+ tokenIssuer,
1707
+ catalogApi,
1708
+ logger
1709
+ }) => OAuthEnvironmentHandler.mapConfig(config, (envConfig) => {
1710
+ var _a, _b;
1711
+ const clientId = envConfig.getString("clientId");
1712
+ const clientSecret = envConfig.getString("clientSecret");
1713
+ const scopes = envConfig.getString("scopes");
1714
+ const callbackUrl = `${globalConfig.baseUrl}/${providerId}/handler/frame`;
1715
+ const catalogIdentityClient = new CatalogIdentityClient({
1716
+ catalogApi,
1717
+ tokenIssuer
1718
+ });
1719
+ const authHandler = (_a = options == null ? void 0 : options.authHandler) != null ? _a : atlassianDefaultAuthHandler;
1720
+ const provider = new AtlassianAuthProvider({
1721
+ clientId,
1722
+ clientSecret,
1723
+ scopes,
1724
+ callbackUrl,
1725
+ authHandler,
1726
+ signInResolver: (_b = options == null ? void 0 : options.signIn) == null ? void 0 : _b.resolver,
1727
+ catalogIdentityClient,
1728
+ logger,
1729
+ tokenIssuer
1730
+ });
1731
+ return OAuthAdapter.fromConfig(globalConfig, provider, {
1732
+ disableRefresh: true,
1733
+ providerId,
1734
+ tokenIssuer
1735
+ });
1736
+ });
1737
+ };
1738
+
1553
1739
  const ALB_JWT_HEADER = "x-amzn-oidc-data";
1554
1740
  const ALB_ACCESSTOKEN_HEADER = "x-amzn-oidc-accesstoken";
1555
1741
  const getJWTHeaders = (input) => {
@@ -1830,12 +2016,10 @@ class SamlAuthProvider {
1830
2016
  }
1831
2017
  });
1832
2018
  } catch (error) {
2019
+ const {name, message} = errors.isError(error) ? error : new Error("Encountered invalid error");
1833
2020
  return postMessageResponse(res, this.appUrl, {
1834
2021
  type: "authorization_response",
1835
- error: {
1836
- name: error.name,
1837
- message: error.message
1838
- }
2022
+ error: {name, message}
1839
2023
  });
1840
2024
  }
1841
2025
  }
@@ -1852,6 +2036,7 @@ const createSamlProvider = (_options) => {
1852
2036
  callbackUrl: `${globalConfig.baseUrl}/${providerId}/handler/frame`,
1853
2037
  entryPoint: config.getString("entryPoint"),
1854
2038
  logoutUrl: config.getOptionalString("logoutUrl"),
2039
+ audience: config.getOptionalString("audience"),
1855
2040
  issuer: config.getString("issuer"),
1856
2041
  cert: config.getString("cert"),
1857
2042
  privateCert: config.getOptionalString("privateKey"),
@@ -2065,7 +2250,8 @@ const factories = {
2065
2250
  oidc: createOidcProvider(),
2066
2251
  onelogin: createOneLoginProvider(),
2067
2252
  awsalb: createAwsAlbProvider(),
2068
- bitbucket: createBitbucketProvider()
2253
+ bitbucket: createBitbucketProvider(),
2254
+ atlassian: createAtlassianProvider()
2069
2255
  };
2070
2256
 
2071
2257
  function createOidcRouter(options) {
@@ -2284,6 +2470,121 @@ class DatabaseKeyStore {
2284
2470
  }
2285
2471
  }
2286
2472
 
2473
+ class MemoryKeyStore {
2474
+ constructor() {
2475
+ this.keys = new Map();
2476
+ }
2477
+ async addKey(key) {
2478
+ this.keys.set(key.kid, {
2479
+ createdAt: luxon.DateTime.utc().toJSDate(),
2480
+ key: JSON.stringify(key)
2481
+ });
2482
+ }
2483
+ async removeKeys(kids) {
2484
+ for (const kid of kids) {
2485
+ this.keys.delete(kid);
2486
+ }
2487
+ }
2488
+ async listKeys() {
2489
+ return {
2490
+ items: Array.from(this.keys).map(([, {createdAt, key: keyStr}]) => ({
2491
+ createdAt,
2492
+ key: JSON.parse(keyStr)
2493
+ }))
2494
+ };
2495
+ }
2496
+ }
2497
+
2498
+ const DEFAULT_TIMEOUT_MS = 1e4;
2499
+ const DEFAULT_DOCUMENT_PATH = "sessions";
2500
+ class FirestoreKeyStore {
2501
+ constructor(database, path, timeout) {
2502
+ this.database = database;
2503
+ this.path = path;
2504
+ this.timeout = timeout;
2505
+ }
2506
+ static async create(settings) {
2507
+ const {path, timeout, ...firestoreSettings} = settings != null ? settings : {};
2508
+ const database = new firestore.Firestore(firestoreSettings);
2509
+ return new FirestoreKeyStore(database, path != null ? path : DEFAULT_DOCUMENT_PATH, timeout != null ? timeout : DEFAULT_TIMEOUT_MS);
2510
+ }
2511
+ static async verifyConnection(keyStore, logger) {
2512
+ try {
2513
+ await keyStore.verify();
2514
+ } catch (error) {
2515
+ if (process.env.NODE_ENV !== "development") {
2516
+ throw new Error(`Failed to connect to database: ${error.message}`);
2517
+ }
2518
+ logger == null ? void 0 : logger.warn(`Failed to connect to database: ${error.message}`);
2519
+ }
2520
+ }
2521
+ async addKey(key) {
2522
+ await this.withTimeout(this.database.collection(this.path).doc(key.kid).set({
2523
+ kid: key.kid,
2524
+ key: JSON.stringify(key)
2525
+ }));
2526
+ }
2527
+ async listKeys() {
2528
+ const keys = await this.withTimeout(this.database.collection(this.path).get());
2529
+ return {
2530
+ items: keys.docs.map((key) => ({
2531
+ key: key.data(),
2532
+ createdAt: key.createTime.toDate()
2533
+ }))
2534
+ };
2535
+ }
2536
+ async removeKeys(kids) {
2537
+ for (const kid of kids) {
2538
+ await this.withTimeout(this.database.collection(this.path).doc(kid).delete());
2539
+ }
2540
+ }
2541
+ async withTimeout(operation) {
2542
+ const timer = new Promise((_, reject) => setTimeout(() => {
2543
+ reject(new Error(`Operation timed out after ${this.timeout}ms`));
2544
+ }, this.timeout));
2545
+ return Promise.race([operation, timer]);
2546
+ }
2547
+ async verify() {
2548
+ await this.withTimeout(this.database.collection(this.path).limit(1).get());
2549
+ }
2550
+ }
2551
+
2552
+ class KeyStores {
2553
+ static async fromConfig(config, options) {
2554
+ var _a;
2555
+ const {logger, database} = options != null ? options : {};
2556
+ const ks = config.getOptionalConfig("auth.keyStore");
2557
+ const provider = (_a = ks == null ? void 0 : ks.getOptionalString("provider")) != null ? _a : "database";
2558
+ logger == null ? void 0 : logger.info(`Configuring "${provider}" as KeyStore provider`);
2559
+ if (provider === "database") {
2560
+ if (!database) {
2561
+ throw new Error("This KeyStore provider requires a database");
2562
+ }
2563
+ return await DatabaseKeyStore.create({
2564
+ database: await database.getClient()
2565
+ });
2566
+ }
2567
+ if (provider === "memory") {
2568
+ return new MemoryKeyStore();
2569
+ }
2570
+ if (provider === "firestore") {
2571
+ const settings = ks == null ? void 0 : ks.getConfig(provider);
2572
+ const keyStore = await FirestoreKeyStore.create(lodash.pickBy({
2573
+ projectId: settings == null ? void 0 : settings.getOptionalString("projectId"),
2574
+ keyFilename: settings == null ? void 0 : settings.getOptionalString("keyFilename"),
2575
+ host: settings == null ? void 0 : settings.getOptionalString("host"),
2576
+ port: settings == null ? void 0 : settings.getOptionalNumber("port"),
2577
+ ssl: settings == null ? void 0 : settings.getOptionalBoolean("ssl"),
2578
+ path: settings == null ? void 0 : settings.getOptionalString("path"),
2579
+ timeout: settings == null ? void 0 : settings.getOptionalNumber("timeout")
2580
+ }, (value) => value !== void 0));
2581
+ await FirestoreKeyStore.verifyConnection(keyStore, logger);
2582
+ return keyStore;
2583
+ }
2584
+ throw new Error(`Unknown KeyStore provider: ${provider}`);
2585
+ }
2586
+ }
2587
+
2287
2588
  async function createRouter({
2288
2589
  logger,
2289
2590
  config,
@@ -2294,10 +2595,8 @@ async function createRouter({
2294
2595
  const router = Router__default['default']();
2295
2596
  const appUrl = config.getString("app.baseUrl");
2296
2597
  const authUrl = await discovery.getExternalBaseUrl("auth");
2598
+ const keyStore = await KeyStores.fromConfig(config, {logger, database});
2297
2599
  const keyDurationSeconds = 3600;
2298
- const keyStore = await DatabaseKeyStore.create({
2299
- database: await database.getClient()
2300
- });
2301
2600
  const tokenIssuer = new TokenFactory({
2302
2601
  issuer: authUrl,
2303
2602
  keyStore,
@@ -2348,6 +2647,7 @@ async function createRouter({
2348
2647
  }
2349
2648
  router.use(`/${providerId}`, r);
2350
2649
  } catch (e) {
2650
+ errors.assertError(e);
2351
2651
  if (process.env.NODE_ENV !== "development") {
2352
2652
  throw new Error(`Failed to initialize ${providerId} auth provider, ${e.message}`);
2353
2653
  }
@@ -2391,6 +2691,7 @@ exports.OAuthAdapter = OAuthAdapter;
2391
2691
  exports.OAuthEnvironmentHandler = OAuthEnvironmentHandler;
2392
2692
  exports.bitbucketUserIdSignInResolver = bitbucketUserIdSignInResolver;
2393
2693
  exports.bitbucketUsernameSignInResolver = bitbucketUsernameSignInResolver;
2694
+ exports.createAtlassianProvider = createAtlassianProvider;
2394
2695
  exports.createAwsAlbProvider = createAwsAlbProvider;
2395
2696
  exports.createBitbucketProvider = createBitbucketProvider;
2396
2697
  exports.createGithubProvider = createGithubProvider;