@backstage/plugin-auth-backend 0.22.6-next.2 → 0.22.6

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,75 @@
1
1
  # @backstage/plugin-auth-backend
2
2
 
3
+ ## 0.22.6
4
+
5
+ ### Patch Changes
6
+
7
+ - 3e823d3: Limited user tokens will no longer include the `ent` field in its payload. Ownership claims will now be fetched from the user info service.
8
+
9
+ NOTE: Limited tokens issued prior to this change will no longer be valid. Users may have to clear their browser cookies in order to refresh their auth tokens.
10
+
11
+ - 8869b8e: Updated local development setup.
12
+ - 78a0b08: Internal refactor to handle `BackendFeature` contract change.
13
+ - d44a20a: Added additional plugin metadata to `package.json`.
14
+ - 3e1bb15: Updated to use the new `@backstage/plugin-auth-backend-module-onelogin-provider` implementation
15
+ - Updated dependencies
16
+ - @backstage/backend-common@0.23.0
17
+ - @backstage/plugin-auth-backend-module-onelogin-provider@0.1.0
18
+ - @backstage/backend-plugin-api@0.6.19
19
+ - @backstage/plugin-auth-node@0.4.14
20
+ - @backstage/plugin-auth-backend-module-cloudflare-access-provider@0.1.2
21
+ - @backstage/plugin-auth-backend-module-azure-easyauth-provider@0.1.2
22
+ - @backstage/plugin-auth-backend-module-oauth2-proxy-provider@0.1.12
23
+ - @backstage/plugin-auth-backend-module-atlassian-provider@0.2.0
24
+ - @backstage/plugin-auth-backend-module-bitbucket-provider@0.1.2
25
+ - @backstage/plugin-auth-backend-module-microsoft-provider@0.1.14
26
+ - @backstage/plugin-auth-backend-module-aws-alb-provider@0.1.11
27
+ - @backstage/plugin-auth-backend-module-gcp-iap-provider@0.2.14
28
+ - @backstage/plugin-auth-backend-module-github-provider@0.1.16
29
+ - @backstage/plugin-auth-backend-module-gitlab-provider@0.1.16
30
+ - @backstage/plugin-auth-backend-module-google-provider@0.1.16
31
+ - @backstage/plugin-auth-backend-module-oauth2-provider@0.2.0
32
+ - @backstage/plugin-auth-backend-module-oidc-provider@0.2.0
33
+ - @backstage/plugin-auth-backend-module-okta-provider@0.0.12
34
+ - @backstage/plugin-catalog-node@1.12.1
35
+ - @backstage/catalog-client@1.6.5
36
+ - @backstage/catalog-model@1.5.0
37
+ - @backstage/config@1.2.0
38
+ - @backstage/errors@1.2.4
39
+ - @backstage/types@1.1.1
40
+
41
+ ## 0.22.6-next.3
42
+
43
+ ### Patch Changes
44
+
45
+ - d44a20a: Added additional plugin metadata to `package.json`.
46
+ - 3e1bb15: Updated to use the new `@backstage/plugin-auth-backend-module-onelogin-provider` implementation
47
+ - Updated dependencies
48
+ - @backstage/plugin-auth-backend-module-onelogin-provider@0.1.0-next.0
49
+ - @backstage/backend-plugin-api@0.6.19-next.3
50
+ - @backstage/plugin-auth-node@0.4.14-next.3
51
+ - @backstage/plugin-auth-backend-module-atlassian-provider@0.2.0-next.2
52
+ - @backstage/plugin-auth-backend-module-bitbucket-provider@0.1.2-next.2
53
+ - @backstage/plugin-auth-backend-module-github-provider@0.1.16-next.2
54
+ - @backstage/plugin-auth-backend-module-gitlab-provider@0.1.16-next.2
55
+ - @backstage/plugin-auth-backend-module-google-provider@0.1.16-next.2
56
+ - @backstage/plugin-auth-backend-module-microsoft-provider@0.1.14-next.2
57
+ - @backstage/plugin-auth-backend-module-oauth2-provider@0.2.0-next.2
58
+ - @backstage/plugin-auth-backend-module-oidc-provider@0.2.0-next.3
59
+ - @backstage/plugin-auth-backend-module-okta-provider@0.0.12-next.2
60
+ - @backstage/plugin-auth-backend-module-cloudflare-access-provider@0.1.2-next.3
61
+ - @backstage/plugin-auth-backend-module-azure-easyauth-provider@0.1.2-next.2
62
+ - @backstage/plugin-auth-backend-module-oauth2-proxy-provider@0.1.12-next.2
63
+ - @backstage/plugin-auth-backend-module-aws-alb-provider@0.1.11-next.3
64
+ - @backstage/plugin-auth-backend-module-gcp-iap-provider@0.2.14-next.2
65
+ - @backstage/plugin-catalog-node@1.12.1-next.2
66
+ - @backstage/backend-common@0.23.0-next.3
67
+ - @backstage/catalog-client@1.6.5
68
+ - @backstage/catalog-model@1.5.0
69
+ - @backstage/config@1.2.0
70
+ - @backstage/errors@1.2.4
71
+ - @backstage/types@1.1.1
72
+
3
73
  ## 0.22.6-next.2
4
74
 
5
75
  ### Patch Changes
package/dist/index.cjs.js CHANGED
@@ -26,7 +26,7 @@ var pluginAuthBackendModuleOauth2Provider = require('@backstage/plugin-auth-back
26
26
  var pluginAuthBackendModuleOauth2ProxyProvider = require('@backstage/plugin-auth-backend-module-oauth2-proxy-provider');
27
27
  var pluginAuthBackendModuleOidcProvider = require('@backstage/plugin-auth-backend-module-oidc-provider');
28
28
  var pluginAuthBackendModuleOktaProvider = require('@backstage/plugin-auth-backend-module-okta-provider');
29
- var passportOneloginOauth = require('passport-onelogin-oauth');
29
+ var pluginAuthBackendModuleOneloginProvider = require('@backstage/plugin-auth-backend-module-onelogin-provider');
30
30
  var passportSaml = require('@node-saml/passport-saml');
31
31
  var passportOauth2 = require('passport-oauth2');
32
32
  var fetch = require('node-fetch');
@@ -35,10 +35,10 @@ var catalogClient = require('@backstage/catalog-client');
35
35
  var minimatch = require('minimatch');
36
36
  var catalogModel = require('@backstage/catalog-model');
37
37
  var backendCommon = require('@backstage/backend-common');
38
+ var lodash = require('lodash');
38
39
  var luxon = require('luxon');
39
40
  var uuid = require('uuid');
40
41
  var firestore = require('@google-cloud/firestore');
41
- var lodash = require('lodash');
42
42
  var fs = require('fs');
43
43
  var session = require('express-session');
44
44
  var connectSessionKnex = require('connect-session-knex');
@@ -952,120 +952,12 @@ const okta = createAuthProviderIntegration({
952
952
  }
953
953
  });
954
954
 
955
- class OneLoginProvider {
956
- _strategy;
957
- signInResolver;
958
- authHandler;
959
- resolverContext;
960
- constructor(options) {
961
- this.signInResolver = options.signInResolver;
962
- this.authHandler = options.authHandler;
963
- this.resolverContext = options.resolverContext;
964
- this._strategy = new passportOneloginOauth.Strategy(
965
- {
966
- issuer: options.issuer,
967
- clientID: options.clientId,
968
- clientSecret: options.clientSecret,
969
- callbackURL: options.callbackUrl,
970
- passReqToCallback: false
971
- },
972
- (accessToken, refreshToken, params, fullProfile, done) => {
973
- done(
974
- void 0,
975
- {
976
- accessToken,
977
- refreshToken,
978
- params,
979
- fullProfile
980
- },
981
- {
982
- refreshToken
983
- }
984
- );
985
- }
986
- );
987
- }
988
- async start(req) {
989
- return await executeRedirectStrategy(req, this._strategy, {
990
- accessType: "offline",
991
- prompt: "consent",
992
- scope: "openid",
993
- state: encodeState(req.state)
994
- });
995
- }
996
- async handler(req) {
997
- const { result, privateInfo } = await executeFrameHandlerStrategy(req, this._strategy);
998
- return {
999
- response: await this.handleResult(result),
1000
- refreshToken: privateInfo.refreshToken
1001
- };
1002
- }
1003
- async refresh(req) {
1004
- const { accessToken, refreshToken, params } = await executeRefreshTokenStrategy(
1005
- this._strategy,
1006
- req.refreshToken,
1007
- "openid"
1008
- );
1009
- const fullProfile = await executeFetchUserProfileStrategy(
1010
- this._strategy,
1011
- accessToken
1012
- );
1013
- return {
1014
- response: await this.handleResult({
1015
- fullProfile,
1016
- params,
1017
- accessToken
1018
- }),
1019
- refreshToken
1020
- };
1021
- }
1022
- async handleResult(result) {
1023
- const { profile } = await this.authHandler(result, this.resolverContext);
1024
- const response = {
1025
- providerInfo: {
1026
- idToken: result.params.id_token,
1027
- accessToken: result.accessToken,
1028
- scope: result.params.scope,
1029
- expiresInSeconds: result.params.expires_in
1030
- },
1031
- profile
1032
- };
1033
- if (this.signInResolver) {
1034
- response.backstageIdentity = await this.signInResolver(
1035
- {
1036
- result,
1037
- profile
1038
- },
1039
- this.resolverContext
1040
- );
1041
- }
1042
- return response;
1043
- }
1044
- }
1045
955
  const onelogin = createAuthProviderIntegration({
1046
956
  create(options) {
1047
- return ({ providerId, globalConfig, config, resolverContext }) => OAuthEnvironmentHandler.mapConfig(config, (envConfig) => {
1048
- const clientId = envConfig.getString("clientId");
1049
- const clientSecret = envConfig.getString("clientSecret");
1050
- const issuer = envConfig.getString("issuer");
1051
- const customCallbackUrl = envConfig.getOptionalString("callbackUrl");
1052
- const callbackUrl = customCallbackUrl || `${globalConfig.baseUrl}/${providerId}/handler/frame`;
1053
- const authHandler = options?.authHandler ? options.authHandler : async ({ fullProfile, params }) => ({
1054
- profile: makeProfileInfo(fullProfile, params.id_token)
1055
- });
1056
- const provider = new OneLoginProvider({
1057
- clientId,
1058
- clientSecret,
1059
- callbackUrl,
1060
- issuer,
1061
- authHandler,
1062
- signInResolver: options?.signIn?.resolver,
1063
- resolverContext
1064
- });
1065
- return OAuthAdapter.fromConfig(globalConfig, provider, {
1066
- providerId,
1067
- callbackUrl
1068
- });
957
+ return pluginAuthNode.createOAuthProviderFactory({
958
+ authenticator: pluginAuthBackendModuleOneloginProvider.oneLoginAuthenticator,
959
+ profileTransform: adaptLegacyOAuthHandler(options?.authHandler),
960
+ signInResolver: adaptLegacyOAuthSignInResolver(options?.signIn?.resolver)
1069
961
  });
1070
962
  }
1071
963
  });
@@ -1687,7 +1579,7 @@ function createOriginFilter(config) {
1687
1579
  }
1688
1580
 
1689
1581
  function bindOidcRouter(targetRouter, options) {
1690
- const { baseUrl, auth, tokenIssuer } = options;
1582
+ const { baseUrl, auth, tokenIssuer, userInfoDatabaseHandler } = options;
1691
1583
  const router = Router__default.default();
1692
1584
  targetRouter.use(router);
1693
1585
  const config = {
@@ -1738,16 +1630,16 @@ function bindOidcRouter(targetRouter, options) {
1738
1630
  "Userinfo endpoint must be called with a token that represents a user principal"
1739
1631
  );
1740
1632
  }
1741
- const { sub: userEntityRef, ent: ownershipEntityRefs = [] } = jose.decodeJwt(token);
1633
+ const { sub: userEntityRef } = jose.decodeJwt(token);
1742
1634
  if (typeof userEntityRef !== "string") {
1743
1635
  throw new Error("Invalid user token, user entity ref must be a string");
1744
1636
  }
1745
- if (!Array.isArray(ownershipEntityRefs) || ownershipEntityRefs.some((ref) => typeof ref !== "string")) {
1746
- throw new Error(
1747
- "Invalid user token, ownership entity refs must be an array of strings"
1748
- );
1637
+ const userInfo = await userInfoDatabaseHandler.getUserInfo(userEntityRef);
1638
+ if (!userInfo) {
1639
+ res.status(404).send("User info not found");
1640
+ return;
1749
1641
  }
1750
- res.json({ sub: userEntityRef, ent: ownershipEntityRefs });
1642
+ res.json(userInfo);
1751
1643
  });
1752
1644
  }
1753
1645
 
@@ -1759,6 +1651,7 @@ class TokenFactory {
1759
1651
  keyStore;
1760
1652
  keyDurationSeconds;
1761
1653
  algorithm;
1654
+ userInfoDatabaseHandler;
1762
1655
  keyExpiry;
1763
1656
  privateKeyPromise;
1764
1657
  constructor(options) {
@@ -1767,6 +1660,7 @@ class TokenFactory {
1767
1660
  this.keyStore = options.keyStore;
1768
1661
  this.keyDurationSeconds = options.keyDurationSeconds;
1769
1662
  this.algorithm = options.algorithm ?? "ES256";
1663
+ this.userInfoDatabaseHandler = options.userInfoDatabaseHandler;
1770
1664
  }
1771
1665
  async issueToken(params) {
1772
1666
  const key = await this.getKey();
@@ -1793,7 +1687,7 @@ class TokenFactory {
1793
1687
  alg: key.alg,
1794
1688
  kid: key.kid
1795
1689
  },
1796
- payload: { sub, ent, iat, exp },
1690
+ payload: { sub, iat, exp },
1797
1691
  key: signingKey
1798
1692
  });
1799
1693
  const claims = {
@@ -1818,6 +1712,9 @@ class TokenFactory {
1818
1712
  )}'`
1819
1713
  );
1820
1714
  }
1715
+ await this.userInfoDatabaseHandler.addUserInfo({
1716
+ claims: lodash.omit(claims, ["aud", "iat", "iss", "uip"])
1717
+ });
1821
1718
  return token;
1822
1719
  }
1823
1720
  // This will be called by other services that want to verify ID tokens.
@@ -1888,7 +1785,6 @@ class TokenFactory {
1888
1785
  };
1889
1786
  const payload = {
1890
1787
  sub: options.payload.sub,
1891
- ent: options.payload.ent,
1892
1788
  iat: options.payload.iat,
1893
1789
  exp: options.payload.exp
1894
1790
  };
@@ -1899,7 +1795,7 @@ class TokenFactory {
1899
1795
  }
1900
1796
  }
1901
1797
 
1902
- const TABLE = "signing_keys";
1798
+ const TABLE$1 = "signing_keys";
1903
1799
  const parseDate = (date) => {
1904
1800
  const parsedDate = typeof date === "string" ? luxon.DateTime.fromSQL(date, { zone: "UTC" }) : luxon.DateTime.fromJSDate(date);
1905
1801
  if (!parsedDate.isValid) {
@@ -1914,13 +1810,13 @@ class DatabaseKeyStore {
1914
1810
  this.client = client;
1915
1811
  }
1916
1812
  async addKey(key) {
1917
- await this.client(TABLE).insert({
1813
+ await this.client(TABLE$1).insert({
1918
1814
  kid: key.kid,
1919
1815
  key: JSON.stringify(key)
1920
1816
  });
1921
1817
  }
1922
1818
  async listKeys() {
1923
- const rows = await this.client(TABLE).select();
1819
+ const rows = await this.client(TABLE$1).select();
1924
1820
  return {
1925
1821
  items: rows.map((row) => ({
1926
1822
  key: JSON.parse(row.key),
@@ -1929,7 +1825,7 @@ class DatabaseKeyStore {
1929
1825
  };
1930
1826
  }
1931
1827
  async removeKeys(kids) {
1932
- await this.client(TABLE).delete().whereIn("kid", kids);
1828
+ await this.client(TABLE$1).delete().whereIn("kid", kids);
1933
1829
  }
1934
1830
  }
1935
1831
 
@@ -2166,6 +2062,30 @@ class KeyStores {
2166
2062
  }
2167
2063
  }
2168
2064
 
2065
+ const TABLE = "user_info";
2066
+ class UserInfoDatabaseHandler {
2067
+ constructor(client) {
2068
+ this.client = client;
2069
+ }
2070
+ async addUserInfo(userInfo) {
2071
+ await this.client(TABLE).insert({
2072
+ user_entity_ref: userInfo.claims.sub,
2073
+ user_info: JSON.stringify(userInfo),
2074
+ exp: luxon.DateTime.fromSeconds(userInfo.claims.exp, {
2075
+ zone: "utc"
2076
+ }).toSQL({ includeOffset: false })
2077
+ }).onConflict("user_entity_ref").merge();
2078
+ }
2079
+ async getUserInfo(userEntityRef) {
2080
+ const info = await this.client(TABLE).where({ user_entity_ref: userEntityRef }).first();
2081
+ if (!info) {
2082
+ return void 0;
2083
+ }
2084
+ const userInfo = JSON.parse(info.user_info);
2085
+ return userInfo;
2086
+ }
2087
+ }
2088
+
2169
2089
  const migrationsDir = backendPluginApi.resolvePackagePath(
2170
2090
  "@backstage/plugin-auth-backend",
2171
2091
  "migrations"
@@ -2293,6 +2213,9 @@ async function createRouter(options) {
2293
2213
  logger,
2294
2214
  database: authDb
2295
2215
  });
2216
+ const userInfoDatabaseHandler = new UserInfoDatabaseHandler(
2217
+ await authDb.get()
2218
+ );
2296
2219
  let tokenIssuer;
2297
2220
  if (keyStore instanceof StaticKeyStore) {
2298
2221
  tokenIssuer = new StaticTokenIssuer(
@@ -2309,7 +2232,8 @@ async function createRouter(options) {
2309
2232
  keyStore,
2310
2233
  keyDurationSeconds: backstageTokenExpiration,
2311
2234
  logger: logger.child({ component: "token-factory" }),
2312
- algorithm: tokenFactoryAlgorithm ?? config.getOptionalString("auth.identityTokenAlgorithm")
2235
+ algorithm: tokenFactoryAlgorithm ?? config.getOptionalString("auth.identityTokenAlgorithm"),
2236
+ userInfoDatabaseHandler
2313
2237
  });
2314
2238
  }
2315
2239
  const secret = config.getOptionalString("auth.session.secret");
@@ -2352,7 +2276,8 @@ async function createRouter(options) {
2352
2276
  bindOidcRouter(router, {
2353
2277
  auth,
2354
2278
  tokenIssuer,
2355
- baseUrl: authUrl
2279
+ baseUrl: authUrl,
2280
+ userInfoDatabaseHandler
2356
2281
  });
2357
2282
  router.use("/:provider/", (req) => {
2358
2283
  const { provider } = req.params;