@backstage/plugin-auth-backend 0.6.0 → 0.7.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/dist/index.cjs.js CHANGED
@@ -20,16 +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 openidClient = require('openid-client');
24
- var passportOktaOauth = require('passport-okta-oauth');
25
- var passportOneloginOauth = require('passport-onelogin-oauth');
26
- var passportSaml = require('passport-saml');
27
- var catalogClient = require('@backstage/catalog-client');
28
23
  var uuid = require('uuid');
29
24
  var luxon = require('luxon');
30
25
  var backendCommon = require('@backstage/backend-common');
31
26
  var firestore = require('@google-cloud/firestore');
32
27
  var lodash = require('lodash');
28
+ var openidClient = require('openid-client');
29
+ var passportOktaOauth = require('passport-okta-oauth');
30
+ var passportOneloginOauth = require('passport-onelogin-oauth');
31
+ var passportSaml = require('passport-saml');
32
+ var googleAuthLibrary = require('google-auth-library');
33
+ var catalogClient = require('@backstage/catalog-client');
33
34
  var session = require('express-session');
34
35
  var passport = require('passport');
35
36
  var minimatch = require('minimatch');
@@ -244,6 +245,10 @@ function parseJwtPayload(token) {
244
245
  }
245
246
  function prepareBackstageIdentityResponse(result) {
246
247
  const { sub, ent } = parseJwtPayload(result.token);
248
+ const userEntityRef = catalogModel.stringifyEntityRef(catalogModel.parseEntityRef(sub, {
249
+ defaultKind: "user",
250
+ defaultNamespace: catalogModel.ENTITY_DEFAULT_NAMESPACE
251
+ }));
247
252
  return {
248
253
  ...{
249
254
  idToken: result.token,
@@ -251,7 +256,7 @@ function prepareBackstageIdentityResponse(result) {
251
256
  },
252
257
  identity: {
253
258
  type: "user",
254
- userEntityRef: sub,
259
+ userEntityRef,
255
260
  ownershipEntityRefs: ent != null ? ent : []
256
261
  }
257
262
  };
@@ -654,7 +659,12 @@ class AtlassianAuthProvider {
654
659
  };
655
660
  }
656
661
  async handleResult(result) {
657
- const { profile } = await this.authHandler(result);
662
+ const context = {
663
+ logger: this.logger,
664
+ catalogIdentityClient: this.catalogIdentityClient,
665
+ tokenIssuer: this.tokenIssuer
666
+ };
667
+ const { profile } = await this.authHandler(result, context);
658
668
  const response = {
659
669
  providerInfo: {
660
670
  idToken: result.params.id_token,
@@ -668,11 +678,7 @@ class AtlassianAuthProvider {
668
678
  response.backstageIdentity = await this.signInResolver({
669
679
  result,
670
680
  profile
671
- }, {
672
- tokenIssuer: this.tokenIssuer,
673
- catalogIdentityClient: this.catalogIdentityClient,
674
- logger: this.logger
675
- });
681
+ }, context);
676
682
  }
677
683
  return response;
678
684
  }
@@ -792,7 +798,12 @@ class Auth0AuthProvider {
792
798
  };
793
799
  }
794
800
  async handleResult(result) {
795
- const { profile } = await this.authHandler(result);
801
+ const context = {
802
+ logger: this.logger,
803
+ catalogIdentityClient: this.catalogIdentityClient,
804
+ tokenIssuer: this.tokenIssuer
805
+ };
806
+ const { profile } = await this.authHandler(result, context);
796
807
  const response = {
797
808
  providerInfo: {
798
809
  idToken: result.params.id_token,
@@ -806,11 +817,7 @@ class Auth0AuthProvider {
806
817
  response.backstageIdentity = await this.signInResolver({
807
818
  result,
808
819
  profile
809
- }, {
810
- tokenIssuer: this.tokenIssuer,
811
- catalogIdentityClient: this.catalogIdentityClient,
812
- logger: this.logger
813
- });
820
+ }, context);
814
821
  }
815
822
  return response;
816
823
  }
@@ -865,7 +872,7 @@ const createAuth0Provider = (options) => {
865
872
  };
866
873
 
867
874
  const ALB_JWT_HEADER = "x-amzn-oidc-data";
868
- const ALB_ACCESSTOKEN_HEADER = "x-amzn-oidc-accesstoken";
875
+ const ALB_ACCESS_TOKEN_HEADER = "x-amzn-oidc-accesstoken";
869
876
  const getJWTHeaders = (input) => {
870
877
  const encoded = input.split(".")[0];
871
878
  return JSON.parse(Buffer.from(encoded, "base64").toString("utf8"));
@@ -900,12 +907,12 @@ class AwsAlbAuthProvider {
900
907
  }
901
908
  async getResult(req) {
902
909
  const jwt = req.header(ALB_JWT_HEADER);
903
- const accessToken = req.header(ALB_ACCESSTOKEN_HEADER);
910
+ const accessToken = req.header(ALB_ACCESS_TOKEN_HEADER);
904
911
  if (jwt === void 0) {
905
912
  throw new errors.AuthenticationError(`Missing ALB OIDC header: ${ALB_JWT_HEADER}`);
906
913
  }
907
914
  if (accessToken === void 0) {
908
- throw new errors.AuthenticationError(`Missing ALB OIDC header: ${ALB_ACCESSTOKEN_HEADER}`);
915
+ throw new errors.AuthenticationError(`Missing ALB OIDC header: ${ALB_ACCESS_TOKEN_HEADER}`);
909
916
  }
910
917
  try {
911
918
  const headers = getJWTHeaders(jwt);
@@ -936,15 +943,16 @@ class AwsAlbAuthProvider {
936
943
  }
937
944
  }
938
945
  async handleResult(result) {
939
- const { profile } = await this.authHandler(result);
940
- const backstageIdentity = await this.signInResolver({
941
- result,
942
- profile
943
- }, {
946
+ const context = {
944
947
  tokenIssuer: this.tokenIssuer,
945
948
  catalogIdentityClient: this.catalogIdentityClient,
946
949
  logger: this.logger
947
- });
950
+ };
951
+ const { profile } = await this.authHandler(result, context);
952
+ const backstageIdentity = await this.signInResolver({
953
+ result,
954
+ profile
955
+ }, context);
948
956
  return {
949
957
  providerInfo: {
950
958
  accessToken: result.accessToken,
@@ -1044,7 +1052,12 @@ class BitbucketAuthProvider {
1044
1052
  }
1045
1053
  async handleResult(result) {
1046
1054
  result.fullProfile.avatarUrl = result.fullProfile._json.links.avatar.href;
1047
- const { profile } = await this.authHandler(result);
1055
+ const context = {
1056
+ logger: this.logger,
1057
+ catalogIdentityClient: this.catalogIdentityClient,
1058
+ tokenIssuer: this.tokenIssuer
1059
+ };
1060
+ const { profile } = await this.authHandler(result, context);
1048
1061
  const response = {
1049
1062
  providerInfo: {
1050
1063
  idToken: result.params.id_token,
@@ -1058,11 +1071,7 @@ class BitbucketAuthProvider {
1058
1071
  response.backstageIdentity = await this.signInResolver({
1059
1072
  result,
1060
1073
  profile
1061
- }, {
1062
- tokenIssuer: this.tokenIssuer,
1063
- catalogIdentityClient: this.catalogIdentityClient,
1064
- logger: this.logger
1065
- });
1074
+ }, context);
1066
1075
  }
1067
1076
  return response;
1068
1077
  }
@@ -1178,7 +1187,12 @@ class GithubAuthProvider {
1178
1187
  };
1179
1188
  }
1180
1189
  async handleResult(result) {
1181
- const { profile } = await this.authHandler(result);
1190
+ const context = {
1191
+ logger: this.logger,
1192
+ catalogIdentityClient: this.catalogIdentityClient,
1193
+ tokenIssuer: this.tokenIssuer
1194
+ };
1195
+ const { profile } = await this.authHandler(result, context);
1182
1196
  const expiresInStr = result.params.expires_in;
1183
1197
  const response = {
1184
1198
  providerInfo: {
@@ -1192,11 +1206,7 @@ class GithubAuthProvider {
1192
1206
  response.backstageIdentity = await this.signInResolver({
1193
1207
  result,
1194
1208
  profile
1195
- }, {
1196
- tokenIssuer: this.tokenIssuer,
1197
- catalogIdentityClient: this.catalogIdentityClient,
1198
- logger: this.logger
1199
- });
1209
+ }, context);
1200
1210
  }
1201
1211
  return response;
1202
1212
  }
@@ -1205,7 +1215,10 @@ const githubDefaultSignInResolver = async (info, ctx) => {
1205
1215
  const { fullProfile } = info.result;
1206
1216
  const userId = fullProfile.username || fullProfile.id;
1207
1217
  const token = await ctx.tokenIssuer.issueToken({
1208
- claims: { sub: userId, ent: [`user:default/${userId}`] }
1218
+ claims: {
1219
+ sub: `user:default/${userId}`,
1220
+ ent: [`user:default/${userId}`]
1221
+ }
1209
1222
  });
1210
1223
  return { id: userId, token };
1211
1224
  };
@@ -1272,7 +1285,7 @@ const gitlabDefaultSignInResolver = async (info, ctx) => {
1272
1285
  id = profile.email.split("@")[0];
1273
1286
  }
1274
1287
  const token = await ctx.tokenIssuer.issueToken({
1275
- claims: { sub: id, ent: [`user:default/${id}`] }
1288
+ claims: { sub: `user:default/${id}`, ent: [`user:default/${id}`] }
1276
1289
  });
1277
1290
  return { id, token };
1278
1291
  };
@@ -1326,7 +1339,12 @@ class GitlabAuthProvider {
1326
1339
  };
1327
1340
  }
1328
1341
  async handleResult(result) {
1329
- const { profile } = await this.authHandler(result);
1342
+ const context = {
1343
+ logger: this.logger,
1344
+ catalogIdentityClient: this.catalogIdentityClient,
1345
+ tokenIssuer: this.tokenIssuer
1346
+ };
1347
+ const { profile } = await this.authHandler(result, context);
1330
1348
  const response = {
1331
1349
  providerInfo: {
1332
1350
  idToken: result.params.id_token,
@@ -1340,11 +1358,7 @@ class GitlabAuthProvider {
1340
1358
  response.backstageIdentity = await this.signInResolver({
1341
1359
  result,
1342
1360
  profile
1343
- }, {
1344
- tokenIssuer: this.tokenIssuer,
1345
- catalogIdentityClient: this.catalogIdentityClient,
1346
- logger: this.logger
1347
- });
1361
+ }, context);
1348
1362
  }
1349
1363
  return response;
1350
1364
  }
@@ -1445,7 +1459,12 @@ class GoogleAuthProvider {
1445
1459
  };
1446
1460
  }
1447
1461
  async handleResult(result) {
1448
- const { profile } = await this.authHandler(result);
1462
+ const context = {
1463
+ logger: this.logger,
1464
+ catalogIdentityClient: this.catalogIdentityClient,
1465
+ tokenIssuer: this.tokenIssuer
1466
+ };
1467
+ const { profile } = await this.authHandler(result, context);
1449
1468
  const response = {
1450
1469
  providerInfo: {
1451
1470
  idToken: result.params.id_token,
@@ -1459,11 +1478,7 @@ class GoogleAuthProvider {
1459
1478
  response.backstageIdentity = await this.signInResolver({
1460
1479
  result,
1461
1480
  profile
1462
- }, {
1463
- tokenIssuer: this.tokenIssuer,
1464
- catalogIdentityClient: this.catalogIdentityClient,
1465
- logger: this.logger
1466
- });
1481
+ }, context);
1467
1482
  }
1468
1483
  return response;
1469
1484
  }
@@ -1594,7 +1609,12 @@ class MicrosoftAuthProvider {
1594
1609
  async handleResult(result) {
1595
1610
  const photo = await this.getUserPhoto(result.accessToken);
1596
1611
  result.fullProfile.photos = photo ? [{ value: photo }] : void 0;
1597
- const { profile } = await this.authHandler(result);
1612
+ const context = {
1613
+ logger: this.logger,
1614
+ catalogIdentityClient: this.catalogIdentityClient,
1615
+ tokenIssuer: this.tokenIssuer
1616
+ };
1617
+ const { profile } = await this.authHandler(result, context);
1598
1618
  const response = {
1599
1619
  providerInfo: {
1600
1620
  idToken: result.params.id_token,
@@ -1608,11 +1628,7 @@ class MicrosoftAuthProvider {
1608
1628
  response.backstageIdentity = await this.signInResolver({
1609
1629
  result,
1610
1630
  profile
1611
- }, {
1612
- tokenIssuer: this.tokenIssuer,
1613
- catalogIdentityClient: this.catalogIdentityClient,
1614
- logger: this.logger
1615
- });
1631
+ }, context);
1616
1632
  }
1617
1633
  return response;
1618
1634
  }
@@ -1653,7 +1669,10 @@ const microsoftDefaultSignInResolver = async (info, ctx) => {
1653
1669
  }
1654
1670
  const userId = profile.email.split("@")[0];
1655
1671
  const token = await ctx.tokenIssuer.issueToken({
1656
- claims: { sub: userId, ent: [`user:default/${userId}`] }
1672
+ claims: {
1673
+ sub: `user:default/${userId}`,
1674
+ ent: [`user:default/${userId}`]
1675
+ }
1657
1676
  });
1658
1677
  return { id: userId, token };
1659
1678
  };
@@ -1764,7 +1783,12 @@ class OAuth2AuthProvider {
1764
1783
  };
1765
1784
  }
1766
1785
  async handleResult(result) {
1767
- const { profile } = await this.authHandler(result);
1786
+ const context = {
1787
+ logger: this.logger,
1788
+ catalogIdentityClient: this.catalogIdentityClient,
1789
+ tokenIssuer: this.tokenIssuer
1790
+ };
1791
+ const { profile } = await this.authHandler(result, context);
1768
1792
  const response = {
1769
1793
  providerInfo: {
1770
1794
  idToken: result.params.id_token,
@@ -1778,11 +1802,7 @@ class OAuth2AuthProvider {
1778
1802
  response.backstageIdentity = await this.signInResolver({
1779
1803
  result,
1780
1804
  profile
1781
- }, {
1782
- tokenIssuer: this.tokenIssuer,
1783
- catalogIdentityClient: this.catalogIdentityClient,
1784
- logger: this.logger
1785
- });
1805
+ }, context);
1786
1806
  }
1787
1807
  return response;
1788
1808
  }
@@ -1797,7 +1817,7 @@ const oAuth2DefaultSignInResolver$1 = async (info, ctx) => {
1797
1817
  }
1798
1818
  const userId = profile.email.split("@")[0];
1799
1819
  const token = await ctx.tokenIssuer.issueToken({
1800
- claims: { sub: userId, ent: [`user:default/${userId}`] }
1820
+ claims: { sub: `user:default/${userId}`, ent: [`user:default/${userId}`] }
1801
1821
  });
1802
1822
  return { id: userId, token };
1803
1823
  };
@@ -1854,112 +1874,527 @@ const createOAuth2Provider = (options) => {
1854
1874
  });
1855
1875
  };
1856
1876
 
1857
- class OidcAuthProvider {
1877
+ function createOidcRouter(options) {
1878
+ const { baseUrl, tokenIssuer } = options;
1879
+ const router = Router__default["default"]();
1880
+ const config = {
1881
+ issuer: baseUrl,
1882
+ token_endpoint: `${baseUrl}/v1/token`,
1883
+ userinfo_endpoint: `${baseUrl}/v1/userinfo`,
1884
+ jwks_uri: `${baseUrl}/.well-known/jwks.json`,
1885
+ response_types_supported: ["id_token"],
1886
+ subject_types_supported: ["public"],
1887
+ id_token_signing_alg_values_supported: ["RS256"],
1888
+ scopes_supported: ["openid"],
1889
+ token_endpoint_auth_methods_supported: [],
1890
+ claims_supported: ["sub"],
1891
+ grant_types_supported: []
1892
+ };
1893
+ router.get("/.well-known/openid-configuration", (_req, res) => {
1894
+ res.json(config);
1895
+ });
1896
+ router.get("/.well-known/jwks.json", async (_req, res) => {
1897
+ const { keys } = await tokenIssuer.listPublicKeys();
1898
+ res.json({ keys });
1899
+ });
1900
+ router.get("/v1/token", (_req, res) => {
1901
+ res.status(501).send("Not Implemented");
1902
+ });
1903
+ router.get("/v1/userinfo", (_req, res) => {
1904
+ res.status(501).send("Not Implemented");
1905
+ });
1906
+ return router;
1907
+ }
1908
+
1909
+ const CLOCK_MARGIN_S = 10;
1910
+ class IdentityClient {
1858
1911
  constructor(options) {
1859
- this.implementation = this.setupStrategy(options);
1860
- this.scope = options.scope;
1861
- this.prompt = options.prompt;
1862
- this.signInResolver = options.signInResolver;
1863
- this.authHandler = options.authHandler;
1864
- this.tokenIssuer = options.tokenIssuer;
1865
- this.catalogIdentityClient = options.catalogIdentityClient;
1866
- this.logger = options.logger;
1912
+ this.discovery = options.discovery;
1913
+ this.issuer = options.issuer;
1914
+ this.keyStore = new jose.JWKS.KeyStore();
1915
+ this.keyStoreUpdated = 0;
1867
1916
  }
1868
- async start(req) {
1869
- const { strategy } = await this.implementation;
1870
- const options = {
1871
- scope: req.scope || this.scope || "openid profile email",
1872
- state: encodeState(req.state)
1873
- };
1874
- const prompt = this.prompt || "none";
1875
- if (prompt !== "auto") {
1876
- options.prompt = prompt;
1917
+ async authenticate(token) {
1918
+ var _a;
1919
+ if (!token) {
1920
+ throw new errors.AuthenticationError("No token specified");
1877
1921
  }
1878
- return await executeRedirectStrategy(req, strategy, options);
1879
- }
1880
- async handler(req) {
1881
- const { strategy } = await this.implementation;
1882
- const { result, privateInfo } = await executeFrameHandlerStrategy(req, strategy);
1883
- return {
1884
- response: await this.handleResult(result),
1885
- refreshToken: privateInfo.refreshToken
1922
+ const key = await this.getKey(token);
1923
+ if (!key) {
1924
+ throw new errors.AuthenticationError("No signing key matching token found");
1925
+ }
1926
+ const decoded = jose.JWT.IdToken.verify(token, key, {
1927
+ algorithms: ["ES256"],
1928
+ audience: "backstage",
1929
+ issuer: this.issuer
1930
+ });
1931
+ if (!decoded.sub) {
1932
+ throw new errors.AuthenticationError("No user sub found in token");
1933
+ }
1934
+ const user = {
1935
+ id: decoded.sub,
1936
+ token,
1937
+ identity: {
1938
+ type: "user",
1939
+ userEntityRef: decoded.sub,
1940
+ ownershipEntityRefs: (_a = decoded.ent) != null ? _a : []
1941
+ }
1886
1942
  };
1943
+ return user;
1887
1944
  }
1888
- async refresh(req) {
1889
- const { client } = await this.implementation;
1890
- const tokenset = await client.refresh(req.refreshToken);
1891
- if (!tokenset.access_token) {
1892
- throw new Error("Refresh failed");
1945
+ static getBearerToken(authorizationHeader) {
1946
+ if (typeof authorizationHeader !== "string") {
1947
+ return void 0;
1893
1948
  }
1894
- const userinfo = await client.userinfo(tokenset.access_token);
1895
- return {
1896
- response: await this.handleResult({ tokenset, userinfo }),
1897
- refreshToken: tokenset.refresh_token
1898
- };
1949
+ const matches = authorizationHeader.match(/Bearer\s+(\S+)/i);
1950
+ return matches == null ? void 0 : matches[1];
1899
1951
  }
1900
- async setupStrategy(options) {
1901
- const issuer = await openidClient.Issuer.discover(options.metadataUrl);
1902
- const client = new issuer.Client({
1903
- access_type: "offline",
1904
- client_id: options.clientId,
1905
- client_secret: options.clientSecret,
1906
- redirect_uris: [options.callbackUrl],
1907
- response_types: ["code"],
1908
- id_token_signed_response_alg: options.tokenSignedResponseAlg || "RS256",
1909
- scope: options.scope || ""
1910
- });
1911
- const strategy = new openidClient.Strategy({
1912
- client,
1913
- passReqToCallback: false
1914
- }, (tokenset, userinfo, done) => {
1915
- if (typeof done !== "function") {
1916
- throw new Error("OIDC IdP must provide a userinfo_endpoint in the metadata response");
1917
- }
1918
- done(void 0, { tokenset, userinfo }, {
1919
- refreshToken: tokenset.refresh_token
1920
- });
1952
+ async getKey(rawJwtToken) {
1953
+ const { header, payload } = jose.JWT.decode(rawJwtToken, {
1954
+ complete: true
1921
1955
  });
1922
- strategy.error = console.error;
1923
- return { strategy, client };
1956
+ const keyStoreHasKey = !!this.keyStore.get({ kid: header.kid });
1957
+ const issuedAfterLastRefresh = (payload == null ? void 0 : payload.iat) && payload.iat > this.keyStoreUpdated - CLOCK_MARGIN_S;
1958
+ if (!keyStoreHasKey && issuedAfterLastRefresh) {
1959
+ await this.refreshKeyStore();
1960
+ }
1961
+ return this.keyStore.get({ kid: header.kid });
1924
1962
  }
1925
- async handleResult(result) {
1926
- const { profile } = await this.authHandler(result);
1927
- const response = {
1928
- providerInfo: {
1929
- idToken: result.tokenset.id_token,
1930
- accessToken: result.tokenset.access_token,
1931
- scope: result.tokenset.scope,
1932
- expiresInSeconds: result.tokenset.expires_in
1933
- },
1934
- profile
1935
- };
1936
- if (this.signInResolver) {
1937
- response.backstageIdentity = await this.signInResolver({
1938
- result,
1939
- profile
1940
- }, {
1941
- tokenIssuer: this.tokenIssuer,
1942
- catalogIdentityClient: this.catalogIdentityClient,
1943
- logger: this.logger
1944
- });
1963
+ async listPublicKeys() {
1964
+ const url = `${await this.discovery.getBaseUrl("auth")}/.well-known/jwks.json`;
1965
+ const response = await fetch__default["default"](url);
1966
+ if (!response.ok) {
1967
+ const payload = await response.text();
1968
+ const message = `Request failed with ${response.status} ${response.statusText}, ${payload}`;
1969
+ throw new Error(message);
1945
1970
  }
1946
- return response;
1971
+ const publicKeys = await response.json();
1972
+ return publicKeys;
1973
+ }
1974
+ async refreshKeyStore() {
1975
+ const now = Date.now() / 1e3;
1976
+ const publicKeys = await this.listPublicKeys();
1977
+ this.keyStore = jose.JWKS.asKeyStore({
1978
+ keys: publicKeys.keys.map((key) => key)
1979
+ });
1980
+ this.keyStoreUpdated = now;
1947
1981
  }
1948
1982
  }
1949
- const oAuth2DefaultSignInResolver = async (info, ctx) => {
1950
- const { profile } = info;
1951
- if (!profile.email) {
1952
- throw new Error("Profile contained no email");
1983
+
1984
+ const MS_IN_S = 1e3;
1985
+ class TokenFactory {
1986
+ constructor(options) {
1987
+ this.issuer = options.issuer;
1988
+ this.logger = options.logger;
1989
+ this.keyStore = options.keyStore;
1990
+ this.keyDurationSeconds = options.keyDurationSeconds;
1953
1991
  }
1954
- const userId = profile.email.split("@")[0];
1955
- const token = await ctx.tokenIssuer.issueToken({
1956
- claims: { sub: userId, ent: [`user:default/${userId}`] }
1957
- });
1958
- return { id: userId, token };
1959
- };
1960
- const createOidcProvider = (options) => {
1961
- return ({
1962
- providerId,
1992
+ async issueToken(params) {
1993
+ const key = await this.getKey();
1994
+ const iss = this.issuer;
1995
+ const sub = params.claims.sub;
1996
+ const ent = params.claims.ent;
1997
+ const aud = "backstage";
1998
+ const iat = Math.floor(Date.now() / MS_IN_S);
1999
+ const exp = iat + this.keyDurationSeconds;
2000
+ this.logger.info(`Issuing token for ${sub}, with entities ${ent != null ? ent : []}`);
2001
+ return jose.JWS.sign({ iss, sub, aud, iat, exp, ent }, key, {
2002
+ alg: key.alg,
2003
+ kid: key.kid
2004
+ });
2005
+ }
2006
+ async listPublicKeys() {
2007
+ const { items: keys } = await this.keyStore.listKeys();
2008
+ const validKeys = [];
2009
+ const expiredKeys = [];
2010
+ for (const key of keys) {
2011
+ const expireAt = luxon.DateTime.fromJSDate(key.createdAt).plus({
2012
+ seconds: 3 * this.keyDurationSeconds
2013
+ });
2014
+ if (expireAt < luxon.DateTime.local()) {
2015
+ expiredKeys.push(key);
2016
+ } else {
2017
+ validKeys.push(key);
2018
+ }
2019
+ }
2020
+ if (expiredKeys.length > 0) {
2021
+ const kids = expiredKeys.map(({ key }) => key.kid);
2022
+ this.logger.info(`Removing expired signing keys, '${kids.join("', '")}'`);
2023
+ this.keyStore.removeKeys(kids).catch((error) => {
2024
+ this.logger.error(`Failed to remove expired keys, ${error}`);
2025
+ });
2026
+ }
2027
+ return { keys: validKeys.map(({ key }) => key) };
2028
+ }
2029
+ async getKey() {
2030
+ if (this.privateKeyPromise) {
2031
+ if (this.keyExpiry && luxon.DateTime.fromJSDate(this.keyExpiry) > luxon.DateTime.local()) {
2032
+ return this.privateKeyPromise;
2033
+ }
2034
+ this.logger.info(`Signing key has expired, generating new key`);
2035
+ delete this.privateKeyPromise;
2036
+ }
2037
+ this.keyExpiry = luxon.DateTime.utc().plus({
2038
+ seconds: this.keyDurationSeconds
2039
+ }).toJSDate();
2040
+ const promise = (async () => {
2041
+ const key = await jose.JWK.generate("EC", "P-256", {
2042
+ use: "sig",
2043
+ kid: uuid.v4(),
2044
+ alg: "ES256"
2045
+ });
2046
+ this.logger.info(`Created new signing key ${key.kid}`);
2047
+ await this.keyStore.addKey(key.toJWK(false));
2048
+ return key;
2049
+ })();
2050
+ this.privateKeyPromise = promise;
2051
+ try {
2052
+ await promise;
2053
+ } catch (error) {
2054
+ this.logger.error(`Failed to generate new signing key, ${error}`);
2055
+ delete this.keyExpiry;
2056
+ delete this.privateKeyPromise;
2057
+ }
2058
+ return promise;
2059
+ }
2060
+ }
2061
+
2062
+ const migrationsDir = backendCommon.resolvePackagePath("@backstage/plugin-auth-backend", "migrations");
2063
+ const TABLE = "signing_keys";
2064
+ const parseDate = (date) => {
2065
+ const parsedDate = typeof date === "string" ? luxon.DateTime.fromSQL(date, { zone: "UTC" }) : luxon.DateTime.fromJSDate(date);
2066
+ if (!parsedDate.isValid) {
2067
+ throw new Error(`Failed to parse date, reason: ${parsedDate.invalidReason}, explanation: ${parsedDate.invalidExplanation}`);
2068
+ }
2069
+ return parsedDate.toJSDate();
2070
+ };
2071
+ class DatabaseKeyStore {
2072
+ static async create(options) {
2073
+ const { database } = options;
2074
+ await database.migrate.latest({
2075
+ directory: migrationsDir
2076
+ });
2077
+ return new DatabaseKeyStore(options);
2078
+ }
2079
+ constructor(options) {
2080
+ this.database = options.database;
2081
+ }
2082
+ async addKey(key) {
2083
+ await this.database(TABLE).insert({
2084
+ kid: key.kid,
2085
+ key: JSON.stringify(key)
2086
+ });
2087
+ }
2088
+ async listKeys() {
2089
+ const rows = await this.database(TABLE).select();
2090
+ return {
2091
+ items: rows.map((row) => ({
2092
+ key: JSON.parse(row.key),
2093
+ createdAt: parseDate(row.created_at)
2094
+ }))
2095
+ };
2096
+ }
2097
+ async removeKeys(kids) {
2098
+ await this.database(TABLE).delete().whereIn("kid", kids);
2099
+ }
2100
+ }
2101
+
2102
+ class MemoryKeyStore {
2103
+ constructor() {
2104
+ this.keys = /* @__PURE__ */ new Map();
2105
+ }
2106
+ async addKey(key) {
2107
+ this.keys.set(key.kid, {
2108
+ createdAt: luxon.DateTime.utc().toJSDate(),
2109
+ key: JSON.stringify(key)
2110
+ });
2111
+ }
2112
+ async removeKeys(kids) {
2113
+ for (const kid of kids) {
2114
+ this.keys.delete(kid);
2115
+ }
2116
+ }
2117
+ async listKeys() {
2118
+ return {
2119
+ items: Array.from(this.keys).map(([, { createdAt, key: keyStr }]) => ({
2120
+ createdAt,
2121
+ key: JSON.parse(keyStr)
2122
+ }))
2123
+ };
2124
+ }
2125
+ }
2126
+
2127
+ const DEFAULT_TIMEOUT_MS = 1e4;
2128
+ const DEFAULT_DOCUMENT_PATH = "sessions";
2129
+ class FirestoreKeyStore {
2130
+ constructor(database, path, timeout) {
2131
+ this.database = database;
2132
+ this.path = path;
2133
+ this.timeout = timeout;
2134
+ }
2135
+ static async create(settings) {
2136
+ const { path, timeout, ...firestoreSettings } = settings != null ? settings : {};
2137
+ const database = new firestore.Firestore(firestoreSettings);
2138
+ return new FirestoreKeyStore(database, path != null ? path : DEFAULT_DOCUMENT_PATH, timeout != null ? timeout : DEFAULT_TIMEOUT_MS);
2139
+ }
2140
+ static async verifyConnection(keyStore, logger) {
2141
+ try {
2142
+ await keyStore.verify();
2143
+ } catch (error) {
2144
+ if (process.env.NODE_ENV !== "development") {
2145
+ throw new Error(`Failed to connect to database: ${error.message}`);
2146
+ }
2147
+ logger == null ? void 0 : logger.warn(`Failed to connect to database: ${error.message}`);
2148
+ }
2149
+ }
2150
+ async addKey(key) {
2151
+ await this.withTimeout(this.database.collection(this.path).doc(key.kid).set({
2152
+ kid: key.kid,
2153
+ key: JSON.stringify(key)
2154
+ }));
2155
+ }
2156
+ async listKeys() {
2157
+ const keys = await this.withTimeout(this.database.collection(this.path).get());
2158
+ return {
2159
+ items: keys.docs.map((key) => ({
2160
+ key: key.data(),
2161
+ createdAt: key.createTime.toDate()
2162
+ }))
2163
+ };
2164
+ }
2165
+ async removeKeys(kids) {
2166
+ for (const kid of kids) {
2167
+ await this.withTimeout(this.database.collection(this.path).doc(kid).delete());
2168
+ }
2169
+ }
2170
+ async withTimeout(operation) {
2171
+ const timer = new Promise((_, reject) => setTimeout(() => {
2172
+ reject(new Error(`Operation timed out after ${this.timeout}ms`));
2173
+ }, this.timeout));
2174
+ return Promise.race([operation, timer]);
2175
+ }
2176
+ async verify() {
2177
+ await this.withTimeout(this.database.collection(this.path).limit(1).get());
2178
+ }
2179
+ }
2180
+
2181
+ class KeyStores {
2182
+ static async fromConfig(config, options) {
2183
+ var _a;
2184
+ const { logger, database } = options != null ? options : {};
2185
+ const ks = config.getOptionalConfig("auth.keyStore");
2186
+ const provider = (_a = ks == null ? void 0 : ks.getOptionalString("provider")) != null ? _a : "database";
2187
+ logger == null ? void 0 : logger.info(`Configuring "${provider}" as KeyStore provider`);
2188
+ if (provider === "database") {
2189
+ if (!database) {
2190
+ throw new Error("This KeyStore provider requires a database");
2191
+ }
2192
+ return await DatabaseKeyStore.create({
2193
+ database: await database.getClient()
2194
+ });
2195
+ }
2196
+ if (provider === "memory") {
2197
+ return new MemoryKeyStore();
2198
+ }
2199
+ if (provider === "firestore") {
2200
+ const settings = ks == null ? void 0 : ks.getConfig(provider);
2201
+ const keyStore = await FirestoreKeyStore.create(lodash.pickBy({
2202
+ projectId: settings == null ? void 0 : settings.getOptionalString("projectId"),
2203
+ keyFilename: settings == null ? void 0 : settings.getOptionalString("keyFilename"),
2204
+ host: settings == null ? void 0 : settings.getOptionalString("host"),
2205
+ port: settings == null ? void 0 : settings.getOptionalNumber("port"),
2206
+ ssl: settings == null ? void 0 : settings.getOptionalBoolean("ssl"),
2207
+ path: settings == null ? void 0 : settings.getOptionalString("path"),
2208
+ timeout: settings == null ? void 0 : settings.getOptionalNumber("timeout")
2209
+ }, (value) => value !== void 0));
2210
+ await FirestoreKeyStore.verifyConnection(keyStore, logger);
2211
+ return keyStore;
2212
+ }
2213
+ throw new Error(`Unknown KeyStore provider: ${provider}`);
2214
+ }
2215
+ }
2216
+
2217
+ const OAUTH2_PROXY_JWT_HEADER = "X-OAUTH2-PROXY-ID-TOKEN";
2218
+ class Oauth2ProxyAuthProvider {
2219
+ constructor(options) {
2220
+ this.catalogIdentityClient = options.catalogIdentityClient;
2221
+ this.logger = options.logger;
2222
+ this.tokenIssuer = options.tokenIssuer;
2223
+ this.signInResolver = options.signInResolver;
2224
+ this.authHandler = options.authHandler;
2225
+ }
2226
+ frameHandler() {
2227
+ return Promise.resolve(void 0);
2228
+ }
2229
+ async refresh(req, res) {
2230
+ try {
2231
+ const result = this.getResult(req);
2232
+ const response = await this.handleResult(result);
2233
+ res.json(response);
2234
+ } catch (e) {
2235
+ this.logger.error(`Exception occurred during ${OAUTH2_PROXY_JWT_HEADER} refresh`, e);
2236
+ res.status(401);
2237
+ res.end();
2238
+ }
2239
+ }
2240
+ start() {
2241
+ return Promise.resolve(void 0);
2242
+ }
2243
+ async handleResult(result) {
2244
+ const ctx = {
2245
+ logger: this.logger,
2246
+ tokenIssuer: this.tokenIssuer,
2247
+ catalogIdentityClient: this.catalogIdentityClient
2248
+ };
2249
+ const { profile } = await this.authHandler(result, ctx);
2250
+ const backstageSignInResult = await this.signInResolver({
2251
+ result,
2252
+ profile
2253
+ }, ctx);
2254
+ return {
2255
+ providerInfo: {
2256
+ accessToken: result.accessToken
2257
+ },
2258
+ backstageIdentity: prepareBackstageIdentityResponse(backstageSignInResult),
2259
+ profile
2260
+ };
2261
+ }
2262
+ getResult(req) {
2263
+ const authHeader = req.header(OAUTH2_PROXY_JWT_HEADER);
2264
+ const jwt = IdentityClient.getBearerToken(authHeader);
2265
+ if (!jwt) {
2266
+ throw new errors.AuthenticationError(`Missing or in incorrect format - Oauth2Proxy OIDC header: ${OAUTH2_PROXY_JWT_HEADER}`);
2267
+ }
2268
+ const decodedJWT = jose.JWT.decode(jwt);
2269
+ return {
2270
+ fullProfile: decodedJWT,
2271
+ accessToken: jwt
2272
+ };
2273
+ }
2274
+ }
2275
+ const createOauth2ProxyProvider = (options) => ({ catalogApi, logger, tokenIssuer }) => {
2276
+ const signInResolver = options.signIn.resolver;
2277
+ const authHandler = options.authHandler;
2278
+ const catalogIdentityClient = new CatalogIdentityClient({
2279
+ catalogApi,
2280
+ tokenIssuer
2281
+ });
2282
+ return new Oauth2ProxyAuthProvider({
2283
+ logger,
2284
+ signInResolver,
2285
+ authHandler,
2286
+ tokenIssuer,
2287
+ catalogIdentityClient
2288
+ });
2289
+ };
2290
+
2291
+ class OidcAuthProvider {
2292
+ constructor(options) {
2293
+ this.implementation = this.setupStrategy(options);
2294
+ this.scope = options.scope;
2295
+ this.prompt = options.prompt;
2296
+ this.signInResolver = options.signInResolver;
2297
+ this.authHandler = options.authHandler;
2298
+ this.tokenIssuer = options.tokenIssuer;
2299
+ this.catalogIdentityClient = options.catalogIdentityClient;
2300
+ this.logger = options.logger;
2301
+ }
2302
+ async start(req) {
2303
+ const { strategy } = await this.implementation;
2304
+ const options = {
2305
+ scope: req.scope || this.scope || "openid profile email",
2306
+ state: encodeState(req.state)
2307
+ };
2308
+ const prompt = this.prompt || "none";
2309
+ if (prompt !== "auto") {
2310
+ options.prompt = prompt;
2311
+ }
2312
+ return await executeRedirectStrategy(req, strategy, options);
2313
+ }
2314
+ async handler(req) {
2315
+ const { strategy } = await this.implementation;
2316
+ const { result, privateInfo } = await executeFrameHandlerStrategy(req, strategy);
2317
+ return {
2318
+ response: await this.handleResult(result),
2319
+ refreshToken: privateInfo.refreshToken
2320
+ };
2321
+ }
2322
+ async refresh(req) {
2323
+ const { client } = await this.implementation;
2324
+ const tokenset = await client.refresh(req.refreshToken);
2325
+ if (!tokenset.access_token) {
2326
+ throw new Error("Refresh failed");
2327
+ }
2328
+ const userinfo = await client.userinfo(tokenset.access_token);
2329
+ return {
2330
+ response: await this.handleResult({ tokenset, userinfo }),
2331
+ refreshToken: tokenset.refresh_token
2332
+ };
2333
+ }
2334
+ async setupStrategy(options) {
2335
+ const issuer = await openidClient.Issuer.discover(options.metadataUrl);
2336
+ const client = new issuer.Client({
2337
+ access_type: "offline",
2338
+ client_id: options.clientId,
2339
+ client_secret: options.clientSecret,
2340
+ redirect_uris: [options.callbackUrl],
2341
+ response_types: ["code"],
2342
+ id_token_signed_response_alg: options.tokenSignedResponseAlg || "RS256",
2343
+ scope: options.scope || ""
2344
+ });
2345
+ const strategy = new openidClient.Strategy({
2346
+ client,
2347
+ passReqToCallback: false
2348
+ }, (tokenset, userinfo, done) => {
2349
+ if (typeof done !== "function") {
2350
+ throw new Error("OIDC IdP must provide a userinfo_endpoint in the metadata response");
2351
+ }
2352
+ done(void 0, { tokenset, userinfo }, {
2353
+ refreshToken: tokenset.refresh_token
2354
+ });
2355
+ });
2356
+ strategy.error = console.error;
2357
+ return { strategy, client };
2358
+ }
2359
+ async handleResult(result) {
2360
+ const context = {
2361
+ logger: this.logger,
2362
+ catalogIdentityClient: this.catalogIdentityClient,
2363
+ tokenIssuer: this.tokenIssuer
2364
+ };
2365
+ const { profile } = await this.authHandler(result, context);
2366
+ const response = {
2367
+ providerInfo: {
2368
+ idToken: result.tokenset.id_token,
2369
+ accessToken: result.tokenset.access_token,
2370
+ scope: result.tokenset.scope,
2371
+ expiresInSeconds: result.tokenset.expires_in
2372
+ },
2373
+ profile
2374
+ };
2375
+ if (this.signInResolver) {
2376
+ response.backstageIdentity = await this.signInResolver({
2377
+ result,
2378
+ profile
2379
+ }, context);
2380
+ }
2381
+ return response;
2382
+ }
2383
+ }
2384
+ const oAuth2DefaultSignInResolver = async (info, ctx) => {
2385
+ const { profile } = info;
2386
+ if (!profile.email) {
2387
+ throw new Error("Profile contained no email");
2388
+ }
2389
+ const userId = profile.email.split("@")[0];
2390
+ const token = await ctx.tokenIssuer.issueToken({
2391
+ claims: { sub: userId, ent: [`user:default/${userId}`] }
2392
+ });
2393
+ return { id: userId, token };
2394
+ };
2395
+ const createOidcProvider = (options) => {
2396
+ return ({
2397
+ providerId,
1963
2398
  globalConfig,
1964
2399
  config,
1965
2400
  tokenIssuer,
@@ -2075,7 +2510,12 @@ class OktaAuthProvider {
2075
2510
  };
2076
2511
  }
2077
2512
  async handleResult(result) {
2078
- const { profile } = await this._authHandler(result);
2513
+ const context = {
2514
+ logger: this._logger,
2515
+ catalogIdentityClient: this._catalogIdentityClient,
2516
+ tokenIssuer: this._tokenIssuer
2517
+ };
2518
+ const { profile } = await this._authHandler(result, context);
2079
2519
  const response = {
2080
2520
  providerInfo: {
2081
2521
  idToken: result.params.id_token,
@@ -2089,11 +2529,7 @@ class OktaAuthProvider {
2089
2529
  response.backstageIdentity = await this._signInResolver({
2090
2530
  result,
2091
2531
  profile
2092
- }, {
2093
- tokenIssuer: this._tokenIssuer,
2094
- catalogIdentityClient: this._catalogIdentityClient,
2095
- logger: this._logger
2096
- });
2532
+ }, context);
2097
2533
  }
2098
2534
  return response;
2099
2535
  }
@@ -2119,7 +2555,7 @@ const oktaDefaultSignInResolver = async (info, ctx) => {
2119
2555
  }
2120
2556
  const userId = profile.email.split("@")[0];
2121
2557
  const token = await ctx.tokenIssuer.issueToken({
2122
- claims: { sub: userId, ent: [`user:default/${userId}`] }
2558
+ claims: { sub: `user:default/${userId}`, ent: [`user:default/${userId}`] }
2123
2559
  });
2124
2560
  return { id: userId, token };
2125
2561
  };
@@ -2224,7 +2660,12 @@ class OneLoginProvider {
2224
2660
  };
2225
2661
  }
2226
2662
  async handleResult(result) {
2227
- const { profile } = await this.authHandler(result);
2663
+ const context = {
2664
+ logger: this.logger,
2665
+ catalogIdentityClient: this.catalogIdentityClient,
2666
+ tokenIssuer: this.tokenIssuer
2667
+ };
2668
+ const { profile } = await this.authHandler(result, context);
2228
2669
  const response = {
2229
2670
  providerInfo: {
2230
2671
  idToken: result.params.id_token,
@@ -2238,11 +2679,7 @@ class OneLoginProvider {
2238
2679
  response.backstageIdentity = await this.signInResolver({
2239
2680
  result,
2240
2681
  profile
2241
- }, {
2242
- tokenIssuer: this.tokenIssuer,
2243
- catalogIdentityClient: this.catalogIdentityClient,
2244
- logger: this.logger
2245
- });
2682
+ }, context);
2246
2683
  }
2247
2684
  return response;
2248
2685
  }
@@ -2263,503 +2700,255 @@ const createOneLoginProvider = (options) => {
2263
2700
  tokenIssuer,
2264
2701
  catalogApi,
2265
2702
  logger
2266
- }) => OAuthEnvironmentHandler.mapConfig(config, (envConfig) => {
2267
- var _a, _b;
2268
- const clientId = envConfig.getString("clientId");
2269
- const clientSecret = envConfig.getString("clientSecret");
2270
- const issuer = envConfig.getString("issuer");
2271
- const callbackUrl = `${globalConfig.baseUrl}/${providerId}/handler/frame`;
2272
- const catalogIdentityClient = new CatalogIdentityClient({
2273
- catalogApi,
2274
- tokenIssuer
2275
- });
2276
- const authHandler = (options == null ? void 0 : options.authHandler) ? options.authHandler : async ({ fullProfile, params }) => ({
2277
- profile: makeProfileInfo(fullProfile, params.id_token)
2278
- });
2279
- const signInResolver = (_b = (_a = options == null ? void 0 : options.signIn) == null ? void 0 : _a.resolver) != null ? _b : defaultSignInResolver;
2280
- const provider = new OneLoginProvider({
2281
- clientId,
2282
- clientSecret,
2283
- callbackUrl,
2284
- issuer,
2285
- authHandler,
2286
- signInResolver,
2287
- tokenIssuer,
2288
- catalogIdentityClient,
2289
- logger
2290
- });
2291
- return OAuthAdapter.fromConfig(globalConfig, provider, {
2292
- disableRefresh: false,
2293
- providerId,
2294
- tokenIssuer
2295
- });
2296
- });
2297
- };
2298
-
2299
- class SamlAuthProvider {
2300
- constructor(options) {
2301
- this.appUrl = options.appUrl;
2302
- this.signInResolver = options.signInResolver;
2303
- this.authHandler = options.authHandler;
2304
- this.tokenIssuer = options.tokenIssuer;
2305
- this.catalogIdentityClient = options.catalogIdentityClient;
2306
- this.logger = options.logger;
2307
- this.strategy = new passportSaml.Strategy({ ...options }, (fullProfile, done) => {
2308
- done(void 0, { fullProfile });
2309
- });
2310
- }
2311
- async start(req, res) {
2312
- const { url } = await executeRedirectStrategy(req, this.strategy, {});
2313
- res.redirect(url);
2314
- }
2315
- async frameHandler(req, res) {
2316
- try {
2317
- const { result } = await executeFrameHandlerStrategy(req, this.strategy);
2318
- const { profile } = await this.authHandler(result);
2319
- const response = {
2320
- profile,
2321
- providerInfo: {}
2322
- };
2323
- if (this.signInResolver) {
2324
- const signInResponse = await this.signInResolver({
2325
- result,
2326
- profile
2327
- }, {
2328
- tokenIssuer: this.tokenIssuer,
2329
- catalogIdentityClient: this.catalogIdentityClient,
2330
- logger: this.logger
2331
- });
2332
- response.backstageIdentity = prepareBackstageIdentityResponse(signInResponse);
2333
- }
2334
- return postMessageResponse(res, this.appUrl, {
2335
- type: "authorization_response",
2336
- response
2337
- });
2338
- } catch (error) {
2339
- const { name, message } = errors.isError(error) ? error : new Error("Encountered invalid error");
2340
- return postMessageResponse(res, this.appUrl, {
2341
- type: "authorization_response",
2342
- error: { name, message }
2343
- });
2344
- }
2345
- }
2346
- async logout(_req, res) {
2347
- res.end();
2348
- }
2349
- }
2350
- const samlDefaultSignInResolver = async (info, ctx) => {
2351
- const id = info.result.fullProfile.nameID;
2352
- const token = await ctx.tokenIssuer.issueToken({
2353
- claims: { sub: id }
2354
- });
2355
- return { id, token };
2356
- };
2357
- const createSamlProvider = (options) => {
2358
- return ({
2359
- providerId,
2360
- globalConfig,
2361
- config,
2362
- tokenIssuer,
2363
- catalogApi,
2364
- logger
2365
- }) => {
2703
+ }) => OAuthEnvironmentHandler.mapConfig(config, (envConfig) => {
2366
2704
  var _a, _b;
2705
+ const clientId = envConfig.getString("clientId");
2706
+ const clientSecret = envConfig.getString("clientSecret");
2707
+ const issuer = envConfig.getString("issuer");
2708
+ const callbackUrl = `${globalConfig.baseUrl}/${providerId}/handler/frame`;
2367
2709
  const catalogIdentityClient = new CatalogIdentityClient({
2368
2710
  catalogApi,
2369
2711
  tokenIssuer
2370
2712
  });
2371
- const authHandler = (options == null ? void 0 : options.authHandler) ? options.authHandler : async ({ fullProfile }) => ({
2372
- profile: {
2373
- email: fullProfile.email,
2374
- displayName: fullProfile.displayName
2375
- }
2713
+ const authHandler = (options == null ? void 0 : options.authHandler) ? options.authHandler : async ({ fullProfile, params }) => ({
2714
+ profile: makeProfileInfo(fullProfile, params.id_token)
2376
2715
  });
2377
- const signInResolverFn = (_b = (_a = options == null ? void 0 : options.signIn) == null ? void 0 : _a.resolver) != null ? _b : samlDefaultSignInResolver;
2378
- const signInResolver = (info) => signInResolverFn(info, {
2379
- catalogIdentityClient,
2716
+ const signInResolver = (_b = (_a = options == null ? void 0 : options.signIn) == null ? void 0 : _a.resolver) != null ? _b : defaultSignInResolver;
2717
+ const provider = new OneLoginProvider({
2718
+ clientId,
2719
+ clientSecret,
2720
+ callbackUrl,
2721
+ issuer,
2722
+ authHandler,
2723
+ signInResolver,
2380
2724
  tokenIssuer,
2725
+ catalogIdentityClient,
2381
2726
  logger
2382
2727
  });
2383
- return new SamlAuthProvider({
2384
- callbackUrl: `${globalConfig.baseUrl}/${providerId}/handler/frame`,
2385
- entryPoint: config.getString("entryPoint"),
2386
- logoutUrl: config.getOptionalString("logoutUrl"),
2387
- audience: config.getOptionalString("audience"),
2388
- issuer: config.getString("issuer"),
2389
- cert: config.getString("cert"),
2390
- privateKey: config.getOptionalString("privateKey"),
2391
- authnContext: config.getOptionalStringArray("authnContext"),
2392
- identifierFormat: config.getOptionalString("identifierFormat"),
2393
- decryptionPvk: config.getOptionalString("decryptionPvk"),
2394
- signatureAlgorithm: config.getOptionalString("signatureAlgorithm"),
2395
- digestAlgorithm: config.getOptionalString("digestAlgorithm"),
2396
- acceptedClockSkewMs: config.getOptionalNumber("acceptedClockSkewMs"),
2397
- tokenIssuer,
2398
- appUrl: globalConfig.appUrl,
2399
- authHandler,
2400
- signInResolver,
2401
- logger,
2402
- catalogIdentityClient
2728
+ return OAuthAdapter.fromConfig(globalConfig, provider, {
2729
+ disableRefresh: false,
2730
+ providerId,
2731
+ tokenIssuer
2403
2732
  });
2404
- };
2405
- };
2406
-
2407
- const factories = {
2408
- google: createGoogleProvider(),
2409
- github: createGithubProvider(),
2410
- gitlab: createGitlabProvider(),
2411
- saml: createSamlProvider(),
2412
- okta: createOktaProvider(),
2413
- auth0: createAuth0Provider(),
2414
- microsoft: createMicrosoftProvider(),
2415
- oauth2: createOAuth2Provider(),
2416
- oidc: createOidcProvider(),
2417
- onelogin: createOneLoginProvider(),
2418
- awsalb: createAwsAlbProvider(),
2419
- bitbucket: createBitbucketProvider(),
2420
- atlassian: createAtlassianProvider()
2421
- };
2422
-
2423
- function createOidcRouter(options) {
2424
- const { baseUrl, tokenIssuer } = options;
2425
- const router = Router__default["default"]();
2426
- const config = {
2427
- issuer: baseUrl,
2428
- token_endpoint: `${baseUrl}/v1/token`,
2429
- userinfo_endpoint: `${baseUrl}/v1/userinfo`,
2430
- jwks_uri: `${baseUrl}/.well-known/jwks.json`,
2431
- response_types_supported: ["id_token"],
2432
- subject_types_supported: ["public"],
2433
- id_token_signing_alg_values_supported: ["RS256"],
2434
- scopes_supported: ["openid"],
2435
- token_endpoint_auth_methods_supported: [],
2436
- claims_supported: ["sub"],
2437
- grant_types_supported: []
2438
- };
2439
- router.get("/.well-known/openid-configuration", (_req, res) => {
2440
- res.json(config);
2441
- });
2442
- router.get("/.well-known/jwks.json", async (_req, res) => {
2443
- const { keys } = await tokenIssuer.listPublicKeys();
2444
- res.json({ keys });
2445
- });
2446
- router.get("/v1/token", (_req, res) => {
2447
- res.status(501).send("Not Implemented");
2448
- });
2449
- router.get("/v1/userinfo", (_req, res) => {
2450
- res.status(501).send("Not Implemented");
2451
2733
  });
2452
- return router;
2453
- }
2454
-
2455
- const CLOCK_MARGIN_S = 10;
2456
- class IdentityClient {
2457
- constructor(options) {
2458
- this.discovery = options.discovery;
2459
- this.issuer = options.issuer;
2460
- this.keyStore = new jose.JWKS.KeyStore();
2461
- this.keyStoreUpdated = 0;
2462
- }
2463
- async authenticate(token) {
2464
- var _a;
2465
- if (!token) {
2466
- throw new errors.AuthenticationError("No token specified");
2467
- }
2468
- const key = await this.getKey(token);
2469
- if (!key) {
2470
- throw new errors.AuthenticationError("No signing key matching token found");
2471
- }
2472
- const decoded = jose.JWT.IdToken.verify(token, key, {
2473
- algorithms: ["ES256"],
2474
- audience: "backstage",
2475
- issuer: this.issuer
2476
- });
2477
- if (!decoded.sub) {
2478
- throw new errors.AuthenticationError("No user sub found in token");
2479
- }
2480
- const user = {
2481
- id: decoded.sub,
2482
- token,
2483
- identity: {
2484
- type: "user",
2485
- userEntityRef: decoded.sub,
2486
- ownershipEntityRefs: (_a = decoded.ent) != null ? _a : []
2487
- }
2488
- };
2489
- return user;
2490
- }
2491
- static getBearerToken(authorizationHeader) {
2492
- if (typeof authorizationHeader !== "string") {
2493
- return void 0;
2494
- }
2495
- const matches = authorizationHeader.match(/Bearer\s+(\S+)/i);
2496
- return matches == null ? void 0 : matches[1];
2497
- }
2498
- async getKey(rawJwtToken) {
2499
- const { header, payload } = jose.JWT.decode(rawJwtToken, {
2500
- complete: true
2501
- });
2502
- const keyStoreHasKey = !!this.keyStore.get({ kid: header.kid });
2503
- const issuedAfterLastRefresh = (payload == null ? void 0 : payload.iat) && payload.iat > this.keyStoreUpdated - CLOCK_MARGIN_S;
2504
- if (!keyStoreHasKey && issuedAfterLastRefresh) {
2505
- await this.refreshKeyStore();
2506
- }
2507
- return this.keyStore.get({ kid: header.kid });
2508
- }
2509
- async listPublicKeys() {
2510
- const url = `${await this.discovery.getBaseUrl("auth")}/.well-known/jwks.json`;
2511
- const response = await fetch__default["default"](url);
2512
- if (!response.ok) {
2513
- const payload = await response.text();
2514
- const message = `Request failed with ${response.status} ${response.statusText}, ${payload}`;
2515
- throw new Error(message);
2516
- }
2517
- const publicKeys = await response.json();
2518
- return publicKeys;
2519
- }
2520
- async refreshKeyStore() {
2521
- const now = Date.now() / 1e3;
2522
- const publicKeys = await this.listPublicKeys();
2523
- this.keyStore = jose.JWKS.asKeyStore({
2524
- keys: publicKeys.keys.map((key) => key)
2525
- });
2526
- this.keyStoreUpdated = now;
2527
- }
2528
- }
2734
+ };
2529
2735
 
2530
- const MS_IN_S = 1e3;
2531
- class TokenFactory {
2736
+ class SamlAuthProvider {
2532
2737
  constructor(options) {
2533
- this.issuer = options.issuer;
2738
+ this.appUrl = options.appUrl;
2739
+ this.signInResolver = options.signInResolver;
2740
+ this.authHandler = options.authHandler;
2741
+ this.tokenIssuer = options.tokenIssuer;
2742
+ this.catalogIdentityClient = options.catalogIdentityClient;
2534
2743
  this.logger = options.logger;
2535
- this.keyStore = options.keyStore;
2536
- this.keyDurationSeconds = options.keyDurationSeconds;
2537
- }
2538
- async issueToken(params) {
2539
- const key = await this.getKey();
2540
- const iss = this.issuer;
2541
- const sub = params.claims.sub;
2542
- const ent = params.claims.ent;
2543
- const aud = "backstage";
2544
- const iat = Math.floor(Date.now() / MS_IN_S);
2545
- const exp = iat + this.keyDurationSeconds;
2546
- this.logger.info(`Issuing token for ${sub}, with entities ${ent != null ? ent : []}`);
2547
- return jose.JWS.sign({ iss, sub, aud, iat, exp, ent }, key, {
2548
- alg: key.alg,
2549
- kid: key.kid
2744
+ this.strategy = new passportSaml.Strategy({ ...options }, (fullProfile, done) => {
2745
+ done(void 0, { fullProfile });
2550
2746
  });
2551
2747
  }
2552
- async listPublicKeys() {
2553
- const { items: keys } = await this.keyStore.listKeys();
2554
- const validKeys = [];
2555
- const expiredKeys = [];
2556
- for (const key of keys) {
2557
- const expireAt = luxon.DateTime.fromJSDate(key.createdAt).plus({
2558
- seconds: 3 * this.keyDurationSeconds
2559
- });
2560
- if (expireAt < luxon.DateTime.local()) {
2561
- expiredKeys.push(key);
2562
- } else {
2563
- validKeys.push(key);
2564
- }
2565
- }
2566
- if (expiredKeys.length > 0) {
2567
- const kids = expiredKeys.map(({ key }) => key.kid);
2568
- this.logger.info(`Removing expired signing keys, '${kids.join("', '")}'`);
2569
- this.keyStore.removeKeys(kids).catch((error) => {
2570
- this.logger.error(`Failed to remove expired keys, ${error}`);
2571
- });
2572
- }
2573
- return { keys: validKeys.map(({ key }) => key) };
2748
+ async start(req, res) {
2749
+ const { url } = await executeRedirectStrategy(req, this.strategy, {});
2750
+ res.redirect(url);
2574
2751
  }
2575
- async getKey() {
2576
- if (this.privateKeyPromise) {
2577
- if (this.keyExpiry && luxon.DateTime.fromJSDate(this.keyExpiry) > luxon.DateTime.local()) {
2578
- return this.privateKeyPromise;
2752
+ async frameHandler(req, res) {
2753
+ try {
2754
+ const context = {
2755
+ logger: this.logger,
2756
+ catalogIdentityClient: this.catalogIdentityClient,
2757
+ tokenIssuer: this.tokenIssuer
2758
+ };
2759
+ const { result } = await executeFrameHandlerStrategy(req, this.strategy);
2760
+ const { profile } = await this.authHandler(result, context);
2761
+ const response = {
2762
+ profile,
2763
+ providerInfo: {}
2764
+ };
2765
+ if (this.signInResolver) {
2766
+ const signInResponse = await this.signInResolver({
2767
+ result,
2768
+ profile
2769
+ }, context);
2770
+ response.backstageIdentity = prepareBackstageIdentityResponse(signInResponse);
2579
2771
  }
2580
- this.logger.info(`Signing key has expired, generating new key`);
2581
- delete this.privateKeyPromise;
2582
- }
2583
- this.keyExpiry = luxon.DateTime.utc().plus({
2584
- seconds: this.keyDurationSeconds
2585
- }).toJSDate();
2586
- const promise = (async () => {
2587
- const key = await jose.JWK.generate("EC", "P-256", {
2588
- use: "sig",
2589
- kid: uuid.v4(),
2590
- alg: "ES256"
2772
+ return postMessageResponse(res, this.appUrl, {
2773
+ type: "authorization_response",
2774
+ response
2591
2775
  });
2592
- this.logger.info(`Created new signing key ${key.kid}`);
2593
- await this.keyStore.addKey(key.toJWK(false));
2594
- return key;
2595
- })();
2596
- this.privateKeyPromise = promise;
2597
- try {
2598
- await promise;
2599
2776
  } catch (error) {
2600
- this.logger.error(`Failed to generate new signing key, ${error}`);
2601
- delete this.keyExpiry;
2602
- delete this.privateKeyPromise;
2777
+ const { name, message } = errors.isError(error) ? error : new Error("Encountered invalid error");
2778
+ return postMessageResponse(res, this.appUrl, {
2779
+ type: "authorization_response",
2780
+ error: { name, message }
2781
+ });
2603
2782
  }
2604
- return promise;
2605
2783
  }
2606
- }
2607
-
2608
- const migrationsDir = backendCommon.resolvePackagePath("@backstage/plugin-auth-backend", "migrations");
2609
- const TABLE = "signing_keys";
2610
- const parseDate = (date) => {
2611
- const parsedDate = typeof date === "string" ? luxon.DateTime.fromSQL(date, { zone: "UTC" }) : luxon.DateTime.fromJSDate(date);
2612
- if (!parsedDate.isValid) {
2613
- throw new Error(`Failed to parse date, reason: ${parsedDate.invalidReason}, explanation: ${parsedDate.invalidExplanation}`);
2784
+ async logout(_req, res) {
2785
+ res.end();
2614
2786
  }
2615
- return parsedDate.toJSDate();
2787
+ }
2788
+ const samlDefaultSignInResolver = async (info, ctx) => {
2789
+ const id = info.result.fullProfile.nameID;
2790
+ const token = await ctx.tokenIssuer.issueToken({
2791
+ claims: { sub: id }
2792
+ });
2793
+ return { id, token };
2616
2794
  };
2617
- class DatabaseKeyStore {
2618
- static async create(options) {
2619
- const { database } = options;
2620
- await database.migrate.latest({
2621
- directory: migrationsDir
2795
+ const createSamlProvider = (options) => {
2796
+ return ({
2797
+ providerId,
2798
+ globalConfig,
2799
+ config,
2800
+ tokenIssuer,
2801
+ catalogApi,
2802
+ logger
2803
+ }) => {
2804
+ var _a, _b;
2805
+ const catalogIdentityClient = new CatalogIdentityClient({
2806
+ catalogApi,
2807
+ tokenIssuer
2622
2808
  });
2623
- return new DatabaseKeyStore(options);
2624
- }
2625
- constructor(options) {
2626
- this.database = options.database;
2627
- }
2628
- async addKey(key) {
2629
- await this.database(TABLE).insert({
2630
- kid: key.kid,
2631
- key: JSON.stringify(key)
2809
+ const authHandler = (options == null ? void 0 : options.authHandler) ? options.authHandler : async ({ fullProfile }) => ({
2810
+ profile: {
2811
+ email: fullProfile.email,
2812
+ displayName: fullProfile.displayName
2813
+ }
2632
2814
  });
2633
- }
2634
- async listKeys() {
2635
- const rows = await this.database(TABLE).select();
2636
- return {
2637
- items: rows.map((row) => ({
2638
- key: JSON.parse(row.key),
2639
- createdAt: parseDate(row.created_at)
2640
- }))
2641
- };
2642
- }
2643
- async removeKeys(kids) {
2644
- await this.database(TABLE).delete().whereIn("kid", kids);
2645
- }
2646
- }
2647
-
2648
- class MemoryKeyStore {
2649
- constructor() {
2650
- this.keys = /* @__PURE__ */ new Map();
2651
- }
2652
- async addKey(key) {
2653
- this.keys.set(key.kid, {
2654
- createdAt: luxon.DateTime.utc().toJSDate(),
2655
- key: JSON.stringify(key)
2815
+ const signInResolverFn = (_b = (_a = options == null ? void 0 : options.signIn) == null ? void 0 : _a.resolver) != null ? _b : samlDefaultSignInResolver;
2816
+ const signInResolver = (info) => signInResolverFn(info, {
2817
+ catalogIdentityClient,
2818
+ tokenIssuer,
2819
+ logger
2820
+ });
2821
+ return new SamlAuthProvider({
2822
+ callbackUrl: `${globalConfig.baseUrl}/${providerId}/handler/frame`,
2823
+ entryPoint: config.getString("entryPoint"),
2824
+ logoutUrl: config.getOptionalString("logoutUrl"),
2825
+ audience: config.getOptionalString("audience"),
2826
+ issuer: config.getString("issuer"),
2827
+ cert: config.getString("cert"),
2828
+ privateKey: config.getOptionalString("privateKey"),
2829
+ authnContext: config.getOptionalStringArray("authnContext"),
2830
+ identifierFormat: config.getOptionalString("identifierFormat"),
2831
+ decryptionPvk: config.getOptionalString("decryptionPvk"),
2832
+ signatureAlgorithm: config.getOptionalString("signatureAlgorithm"),
2833
+ digestAlgorithm: config.getOptionalString("digestAlgorithm"),
2834
+ acceptedClockSkewMs: config.getOptionalNumber("acceptedClockSkewMs"),
2835
+ tokenIssuer,
2836
+ appUrl: globalConfig.appUrl,
2837
+ authHandler,
2838
+ signInResolver,
2839
+ logger,
2840
+ catalogIdentityClient
2656
2841
  });
2842
+ };
2843
+ };
2844
+
2845
+ const IAP_JWT_HEADER = "x-goog-iap-jwt-assertion";
2846
+
2847
+ function createTokenValidator(audience, mockClient) {
2848
+ const client = mockClient != null ? mockClient : new googleAuthLibrary.OAuth2Client();
2849
+ return async function tokenValidator(token) {
2850
+ const response = await client.getIapPublicKeys();
2851
+ const ticket = await client.verifySignedJwtWithCertsAsync(token, response.pubkeys, audience, ["https://cloud.google.com/iap"]);
2852
+ const payload = ticket.getPayload();
2853
+ if (!payload) {
2854
+ throw new TypeError("Token had no payload");
2855
+ }
2856
+ return payload;
2857
+ };
2858
+ }
2859
+ async function parseRequestToken(jwtToken, tokenValidator) {
2860
+ if (typeof jwtToken !== "string" || !jwtToken) {
2861
+ throw new errors.AuthenticationError(`Missing Google IAP header: ${IAP_JWT_HEADER}`);
2657
2862
  }
2658
- async removeKeys(kids) {
2659
- for (const kid of kids) {
2660
- this.keys.delete(kid);
2661
- }
2863
+ let payload;
2864
+ try {
2865
+ payload = await tokenValidator(jwtToken);
2866
+ } catch (e) {
2867
+ throw new errors.AuthenticationError(`Google IAP token verification failed, ${e}`);
2662
2868
  }
2663
- async listKeys() {
2664
- return {
2665
- items: Array.from(this.keys).map(([, { createdAt, key: keyStr }]) => ({
2666
- createdAt,
2667
- key: JSON.parse(keyStr)
2668
- }))
2669
- };
2869
+ if (!payload.sub || !payload.email) {
2870
+ throw new errors.AuthenticationError("Google IAP token payload is missing sub and/or email claim");
2670
2871
  }
2872
+ return {
2873
+ iapToken: {
2874
+ ...payload,
2875
+ sub: payload.sub,
2876
+ email: payload.email
2877
+ }
2878
+ };
2671
2879
  }
2880
+ const defaultAuthHandler = async ({
2881
+ iapToken
2882
+ }) => ({ profile: { email: iapToken.email } });
2672
2883
 
2673
- const DEFAULT_TIMEOUT_MS = 1e4;
2674
- const DEFAULT_DOCUMENT_PATH = "sessions";
2675
- class FirestoreKeyStore {
2676
- constructor(database, path, timeout) {
2677
- this.database = database;
2678
- this.path = path;
2679
- this.timeout = timeout;
2680
- }
2681
- static async create(settings) {
2682
- const { path, timeout, ...firestoreSettings } = settings != null ? settings : {};
2683
- const database = new firestore.Firestore(firestoreSettings);
2684
- return new FirestoreKeyStore(database, path != null ? path : DEFAULT_DOCUMENT_PATH, timeout != null ? timeout : DEFAULT_TIMEOUT_MS);
2884
+ class GcpIapProvider {
2885
+ constructor(options) {
2886
+ this.authHandler = options.authHandler;
2887
+ this.signInResolver = options.signInResolver;
2888
+ this.tokenValidator = options.tokenValidator;
2889
+ this.tokenIssuer = options.tokenIssuer;
2890
+ this.catalogIdentityClient = options.catalogIdentityClient;
2891
+ this.logger = options.logger;
2685
2892
  }
2686
- static async verifyConnection(keyStore, logger) {
2687
- try {
2688
- await keyStore.verify();
2689
- } catch (error) {
2690
- if (process.env.NODE_ENV !== "development") {
2691
- throw new Error(`Failed to connect to database: ${error.message}`);
2692
- }
2693
- logger == null ? void 0 : logger.warn(`Failed to connect to database: ${error.message}`);
2694
- }
2893
+ async start() {
2695
2894
  }
2696
- async addKey(key) {
2697
- await this.withTimeout(this.database.collection(this.path).doc(key.kid).set({
2698
- kid: key.kid,
2699
- key: JSON.stringify(key)
2700
- }));
2895
+ async frameHandler() {
2701
2896
  }
2702
- async listKeys() {
2703
- const keys = await this.withTimeout(this.database.collection(this.path).get());
2704
- return {
2705
- items: keys.docs.map((key) => ({
2706
- key: key.data(),
2707
- createdAt: key.createTime.toDate()
2708
- }))
2897
+ async refresh(req, res) {
2898
+ const result = await parseRequestToken(req.header(IAP_JWT_HEADER), this.tokenValidator);
2899
+ const context = {
2900
+ logger: this.logger,
2901
+ catalogIdentityClient: this.catalogIdentityClient,
2902
+ tokenIssuer: this.tokenIssuer
2709
2903
  };
2710
- }
2711
- async removeKeys(kids) {
2712
- for (const kid of kids) {
2713
- await this.withTimeout(this.database.collection(this.path).doc(kid).delete());
2714
- }
2715
- }
2716
- async withTimeout(operation) {
2717
- const timer = new Promise((_, reject) => setTimeout(() => {
2718
- reject(new Error(`Operation timed out after ${this.timeout}ms`));
2719
- }, this.timeout));
2720
- return Promise.race([operation, timer]);
2721
- }
2722
- async verify() {
2723
- await this.withTimeout(this.database.collection(this.path).limit(1).get());
2904
+ const { profile } = await this.authHandler(result, context);
2905
+ const backstageIdentity = await this.signInResolver({ profile, result }, context);
2906
+ const response = {
2907
+ providerInfo: { iapToken: result.iapToken },
2908
+ profile,
2909
+ backstageIdentity: prepareBackstageIdentityResponse(backstageIdentity)
2910
+ };
2911
+ res.json(response);
2724
2912
  }
2725
2913
  }
2726
-
2727
- class KeyStores {
2728
- static async fromConfig(config, options) {
2914
+ function createGcpIapProvider(options) {
2915
+ return ({ config, tokenIssuer, catalogApi, logger }) => {
2729
2916
  var _a;
2730
- const { logger, database } = options != null ? options : {};
2731
- const ks = config.getOptionalConfig("auth.keyStore");
2732
- const provider = (_a = ks == null ? void 0 : ks.getOptionalString("provider")) != null ? _a : "database";
2733
- logger == null ? void 0 : logger.info(`Configuring "${provider}" as KeyStore provider`);
2734
- if (provider === "database") {
2735
- if (!database) {
2736
- throw new Error("This KeyStore provider requires a database");
2737
- }
2738
- return await DatabaseKeyStore.create({
2739
- database: await database.getClient()
2740
- });
2741
- }
2742
- if (provider === "memory") {
2743
- return new MemoryKeyStore();
2744
- }
2745
- if (provider === "firestore") {
2746
- const settings = ks == null ? void 0 : ks.getConfig(provider);
2747
- const keyStore = await FirestoreKeyStore.create(lodash.pickBy({
2748
- projectId: settings == null ? void 0 : settings.getOptionalString("projectId"),
2749
- keyFilename: settings == null ? void 0 : settings.getOptionalString("keyFilename"),
2750
- host: settings == null ? void 0 : settings.getOptionalString("host"),
2751
- port: settings == null ? void 0 : settings.getOptionalNumber("port"),
2752
- ssl: settings == null ? void 0 : settings.getOptionalBoolean("ssl"),
2753
- path: settings == null ? void 0 : settings.getOptionalString("path"),
2754
- timeout: settings == null ? void 0 : settings.getOptionalNumber("timeout")
2755
- }, (value) => value !== void 0));
2756
- await FirestoreKeyStore.verifyConnection(keyStore, logger);
2757
- return keyStore;
2758
- }
2759
- throw new Error(`Unknown KeyStore provider: ${provider}`);
2760
- }
2917
+ const audience = config.getString("audience");
2918
+ const authHandler = (_a = options.authHandler) != null ? _a : defaultAuthHandler;
2919
+ const signInResolver = options.signIn.resolver;
2920
+ const tokenValidator = createTokenValidator(audience);
2921
+ const catalogIdentityClient = new CatalogIdentityClient({
2922
+ catalogApi,
2923
+ tokenIssuer
2924
+ });
2925
+ return new GcpIapProvider({
2926
+ authHandler,
2927
+ signInResolver,
2928
+ tokenValidator,
2929
+ tokenIssuer,
2930
+ catalogIdentityClient,
2931
+ logger
2932
+ });
2933
+ };
2761
2934
  }
2762
2935
 
2936
+ const factories = {
2937
+ google: createGoogleProvider(),
2938
+ github: createGithubProvider(),
2939
+ gitlab: createGitlabProvider(),
2940
+ saml: createSamlProvider(),
2941
+ okta: createOktaProvider(),
2942
+ auth0: createAuth0Provider(),
2943
+ microsoft: createMicrosoftProvider(),
2944
+ oauth2: createOAuth2Provider(),
2945
+ oidc: createOidcProvider(),
2946
+ onelogin: createOneLoginProvider(),
2947
+ awsalb: createAwsAlbProvider(),
2948
+ bitbucket: createBitbucketProvider(),
2949
+ atlassian: createAtlassianProvider()
2950
+ };
2951
+
2763
2952
  async function createRouter(options) {
2764
2953
  const { logger, config, discovery, database, providerFactories } = options;
2765
2954
  const router = Router__default["default"]();
@@ -2782,7 +2971,7 @@ async function createRouter(options) {
2782
2971
  secret,
2783
2972
  saveUninitialized: false,
2784
2973
  resave: false,
2785
- cookie: { secure: enforceCookieSSL }
2974
+ cookie: { secure: enforceCookieSSL ? "auto" : false }
2786
2975
  }));
2787
2976
  router.use(passport__default["default"].initialize());
2788
2977
  router.use(passport__default["default"].session());
@@ -2872,11 +3061,13 @@ exports.createAtlassianProvider = createAtlassianProvider;
2872
3061
  exports.createAuth0Provider = createAuth0Provider;
2873
3062
  exports.createAwsAlbProvider = createAwsAlbProvider;
2874
3063
  exports.createBitbucketProvider = createBitbucketProvider;
3064
+ exports.createGcpIapProvider = createGcpIapProvider;
2875
3065
  exports.createGithubProvider = createGithubProvider;
2876
3066
  exports.createGitlabProvider = createGitlabProvider;
2877
3067
  exports.createGoogleProvider = createGoogleProvider;
2878
3068
  exports.createMicrosoftProvider = createMicrosoftProvider;
2879
3069
  exports.createOAuth2Provider = createOAuth2Provider;
3070
+ exports.createOauth2ProxyProvider = createOauth2ProxyProvider;
2880
3071
  exports.createOidcProvider = createOidcProvider;
2881
3072
  exports.createOktaProvider = createOktaProvider;
2882
3073
  exports.createOneLoginProvider = createOneLoginProvider;