@backstage/plugin-auth-backend 0.21.2 → 0.22.0-next.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
@@ -30,7 +30,9 @@ var passportOneloginOauth = require('passport-onelogin-oauth');
30
30
  var passportSaml = require('@node-saml/passport-saml');
31
31
  var passportOauth2 = require('passport-oauth2');
32
32
  var catalogClient = require('@backstage/catalog-client');
33
+ var minimatch = require('minimatch');
33
34
  var catalogModel = require('@backstage/catalog-model');
35
+ var backendCommon = require('@backstage/backend-common');
34
36
  var luxon = require('luxon');
35
37
  var uuid = require('uuid');
36
38
  var firestore = require('@google-cloud/firestore');
@@ -39,8 +41,6 @@ var fs = require('fs');
39
41
  var session = require('express-session');
40
42
  var connectSessionKnex = require('connect-session-knex');
41
43
  var passport = require('passport');
42
- var minimatch = require('minimatch');
43
- var backendCommon = require('@backstage/backend-common');
44
44
  var config = require('@backstage/config');
45
45
  var types = require('@backstage/types');
46
46
 
@@ -931,12 +931,14 @@ const CACHE_PREFIX = "providers/cloudflare-access/profile-v1";
931
931
  class CloudflareAccessAuthProvider {
932
932
  constructor(options) {
933
933
  __publicField$9(this, "teamName");
934
+ __publicField$9(this, "serviceTokens");
934
935
  __publicField$9(this, "resolverContext");
935
936
  __publicField$9(this, "authHandler");
936
937
  __publicField$9(this, "signInResolver");
937
938
  __publicField$9(this, "jwtKeySet");
938
939
  __publicField$9(this, "cache");
939
940
  this.teamName = options.teamName;
941
+ this.serviceTokens = options.serviceTokens;
940
942
  this.authHandler = options.authHandler;
941
943
  this.signInResolver = options.signInResolver;
942
944
  this.resolverContext = options.resolverContext;
@@ -990,8 +992,21 @@ class CloudflareAccessAuthProvider {
990
992
  const verifyResult = await jose.jwtVerify(jwt, this.jwtKeySet, {
991
993
  issuer: `https://${this.teamName}.cloudflareaccess.com`
992
994
  });
993
- const sub = verifyResult.payload.sub;
994
- const cfAccessResultStr = await ((_a = this.cache) == null ? void 0 : _a.get(`${CACHE_PREFIX}/${sub}`));
995
+ const isServiceToken = !verifyResult.payload.sub;
996
+ const subject = isServiceToken ? verifyResult.payload.common_name : verifyResult.payload.sub;
997
+ if (!subject) {
998
+ throw new errors.AuthenticationError(
999
+ `Missing both sub and common_name from Cloudflare Access JWT`
1000
+ );
1001
+ }
1002
+ const serviceToken = this.serviceTokens.find((st) => st.token === subject);
1003
+ if (isServiceToken && !serviceToken) {
1004
+ throw new errors.AuthenticationError(
1005
+ `${subject} is not a permitted Service Token.`
1006
+ );
1007
+ }
1008
+ const cacheKey = `${CACHE_PREFIX}/${subject}`;
1009
+ const cfAccessResultStr = await ((_a = this.cache) == null ? void 0 : _a.get(cacheKey));
995
1010
  if (typeof cfAccessResultStr === "string") {
996
1011
  const result = JSON.parse(cfAccessResultStr);
997
1012
  return {
@@ -1001,13 +1016,23 @@ class CloudflareAccessAuthProvider {
1001
1016
  }
1002
1017
  const claims = verifyResult.payload;
1003
1018
  try {
1004
- const cfIdentity = await this.getIdentityProfile(jwt);
1019
+ let cfIdentity;
1020
+ if (serviceToken) {
1021
+ cfIdentity = {
1022
+ id: subject,
1023
+ name: "Bot",
1024
+ email: serviceToken.subject,
1025
+ groups: []
1026
+ };
1027
+ } else {
1028
+ cfIdentity = await this.getIdentityProfile(jwt);
1029
+ }
1005
1030
  const cfAccessResult = {
1006
1031
  claims,
1007
1032
  cfIdentity,
1008
1033
  expiresInSeconds: claims.exp - claims.iat
1009
1034
  };
1010
- (_b = this.cache) == null ? void 0 : _b.set(`${CACHE_PREFIX}/${sub}`, JSON.stringify(cfAccessResult));
1035
+ (_b = this.cache) == null ? void 0 : _b.set(cacheKey, JSON.stringify(cfAccessResult));
1011
1036
  return {
1012
1037
  ...cfAccessResult,
1013
1038
  token: jwt
@@ -1043,6 +1068,13 @@ const cfAccess = createAuthProviderIntegration({
1043
1068
  create(options) {
1044
1069
  return ({ config, resolverContext }) => {
1045
1070
  const teamName = config.getString("teamName");
1071
+ const serviceTokensConfig = config.getOptionalConfigArray("serviceTokens");
1072
+ const serviceTokens = (serviceTokensConfig == null ? void 0 : serviceTokensConfig.map((cfg) => {
1073
+ return {
1074
+ token: cfg.getString("token"),
1075
+ subject: cfg.getString("subject")
1076
+ };
1077
+ })) || [];
1046
1078
  if (!options.signIn.resolver) {
1047
1079
  throw new Error(
1048
1080
  "SignInResolver is required to use this authentication provider"
@@ -1058,6 +1090,7 @@ const cfAccess = createAuthProviderIntegration({
1058
1090
  };
1059
1091
  return new CloudflareAccessAuthProvider({
1060
1092
  teamName,
1093
+ serviceTokens,
1061
1094
  signInResolver: options == null ? void 0 : options.signIn.resolver,
1062
1095
  authHandler,
1063
1096
  resolverContext,
@@ -1832,9 +1865,289 @@ const defaultAuthProviderFactories = {
1832
1865
  atlassian: atlassian.create()
1833
1866
  };
1834
1867
 
1835
- function createOidcRouter(options) {
1868
+ var __defProp$4 = Object.defineProperty;
1869
+ var __defNormalProp$4 = (obj, key, value) => key in obj ? __defProp$4(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
1870
+ var __publicField$4 = (obj, key, value) => {
1871
+ __defNormalProp$4(obj, typeof key !== "symbol" ? key + "" : key, value);
1872
+ return value;
1873
+ };
1874
+ class CatalogIdentityClient {
1875
+ constructor(options) {
1876
+ __publicField$4(this, "catalogApi");
1877
+ __publicField$4(this, "auth");
1878
+ this.catalogApi = options.catalogApi;
1879
+ const { auth } = backendCommon.createLegacyAuthAdapters({
1880
+ auth: options.auth,
1881
+ httpAuth: options.httpAuth,
1882
+ discovery: options.discovery,
1883
+ tokenManager: options.tokenManager
1884
+ });
1885
+ this.auth = auth;
1886
+ }
1887
+ /**
1888
+ * Looks up a single user using a query.
1889
+ *
1890
+ * Throws a NotFoundError or ConflictError if 0 or multiple users are found.
1891
+ */
1892
+ async findUser(query) {
1893
+ const filter = {
1894
+ kind: "user"
1895
+ };
1896
+ for (const [key, value] of Object.entries(query.annotations)) {
1897
+ filter[`metadata.annotations.${key}`] = value;
1898
+ }
1899
+ const { token } = await this.auth.getPluginRequestToken({
1900
+ onBehalfOf: await this.auth.getOwnServiceCredentials(),
1901
+ targetPluginId: "catalog"
1902
+ });
1903
+ const { items } = await this.catalogApi.getEntities({ filter }, { token });
1904
+ if (items.length !== 1) {
1905
+ if (items.length > 1) {
1906
+ throw new errors.ConflictError("User lookup resulted in multiple matches");
1907
+ } else {
1908
+ throw new errors.NotFoundError("User not found");
1909
+ }
1910
+ }
1911
+ return items[0];
1912
+ }
1913
+ /**
1914
+ * Resolve additional entity claims from the catalog, using the passed-in entity names. Designed
1915
+ * to be used within a `signInResolver` where additional entity claims might be provided, but
1916
+ * group membership and transient group membership lean on imported catalog relations.
1917
+ *
1918
+ * Returns a superset of the entity names that can be passed directly to `issueToken` as `ent`.
1919
+ */
1920
+ async resolveCatalogMembership(query) {
1921
+ const { entityRefs, logger } = query;
1922
+ const resolvedEntityRefs = entityRefs.map((ref) => {
1923
+ try {
1924
+ const parsedRef = catalogModel.parseEntityRef(ref.toLocaleLowerCase("en-US"), {
1925
+ defaultKind: "user",
1926
+ defaultNamespace: "default"
1927
+ });
1928
+ return parsedRef;
1929
+ } catch {
1930
+ logger == null ? void 0 : logger.warn(`Failed to parse entityRef from ${ref}, ignoring`);
1931
+ return null;
1932
+ }
1933
+ }).filter((ref) => ref !== null);
1934
+ const filter = resolvedEntityRefs.map((ref) => ({
1935
+ kind: ref.kind,
1936
+ "metadata.namespace": ref.namespace,
1937
+ "metadata.name": ref.name
1938
+ }));
1939
+ const { token } = await this.auth.getPluginRequestToken({
1940
+ onBehalfOf: await this.auth.getOwnServiceCredentials(),
1941
+ targetPluginId: "catalog"
1942
+ });
1943
+ const entities = await this.catalogApi.getEntities({ filter }, { token }).then((r) => r.items);
1944
+ if (entityRefs.length !== entities.length) {
1945
+ const foundEntityNames = entities.map(catalogModel.stringifyEntityRef);
1946
+ const missingEntityNames = resolvedEntityRefs.map(catalogModel.stringifyEntityRef).filter((s) => !foundEntityNames.includes(s));
1947
+ logger == null ? void 0 : logger.debug(`Entities not found for refs ${missingEntityNames.join()}`);
1948
+ }
1949
+ const memberOf = entities.flatMap(
1950
+ (e) => {
1951
+ var _a, _b;
1952
+ return (_b = (_a = e.relations) == null ? void 0 : _a.filter((r) => r.type === catalogModel.RELATION_MEMBER_OF).map((r) => r.targetRef)) != null ? _b : [];
1953
+ }
1954
+ );
1955
+ const newEntityRefs = [
1956
+ ...new Set(resolvedEntityRefs.map(catalogModel.stringifyEntityRef).concat(memberOf))
1957
+ ];
1958
+ logger == null ? void 0 : logger.debug(`Found catalog membership: ${newEntityRefs.join()}`);
1959
+ return newEntityRefs;
1960
+ }
1961
+ }
1962
+
1963
+ function getDefaultOwnershipEntityRefs(entity) {
1964
+ var _a, _b;
1965
+ const membershipRefs = (_b = (_a = entity.relations) == null ? void 0 : _a.filter(
1966
+ (r) => r.type === catalogModel.RELATION_MEMBER_OF && r.targetRef.startsWith("group:")
1967
+ ).map((r) => r.targetRef)) != null ? _b : [];
1968
+ return Array.from(/* @__PURE__ */ new Set([catalogModel.stringifyEntityRef(entity), ...membershipRefs]));
1969
+ }
1970
+ class CatalogAuthResolverContext {
1971
+ constructor(logger, tokenIssuer, catalogIdentityClient, catalogApi, auth) {
1972
+ this.logger = logger;
1973
+ this.tokenIssuer = tokenIssuer;
1974
+ this.catalogIdentityClient = catalogIdentityClient;
1975
+ this.catalogApi = catalogApi;
1976
+ this.auth = auth;
1977
+ }
1978
+ static create(options) {
1979
+ const catalogIdentityClient = new CatalogIdentityClient({
1980
+ catalogApi: options.catalogApi,
1981
+ tokenManager: options.tokenManager,
1982
+ discovery: options.discovery,
1983
+ auth: options.auth,
1984
+ httpAuth: options.httpAuth
1985
+ });
1986
+ return new CatalogAuthResolverContext(
1987
+ options.logger,
1988
+ options.tokenIssuer,
1989
+ catalogIdentityClient,
1990
+ options.catalogApi,
1991
+ options.auth
1992
+ );
1993
+ }
1994
+ async issueToken(params) {
1995
+ const token = await this.tokenIssuer.issueToken(params);
1996
+ return { token };
1997
+ }
1998
+ async findCatalogUser(query) {
1999
+ let result = void 0;
2000
+ const { token } = await this.auth.getPluginRequestToken({
2001
+ onBehalfOf: await this.auth.getOwnServiceCredentials(),
2002
+ targetPluginId: "catalog"
2003
+ });
2004
+ if ("entityRef" in query) {
2005
+ const entityRef = catalogModel.parseEntityRef(query.entityRef, {
2006
+ defaultKind: "User",
2007
+ defaultNamespace: catalogModel.DEFAULT_NAMESPACE
2008
+ });
2009
+ result = await this.catalogApi.getEntityByRef(entityRef, { token });
2010
+ } else if ("annotations" in query) {
2011
+ const filter = {
2012
+ kind: "user"
2013
+ };
2014
+ for (const [key, value] of Object.entries(query.annotations)) {
2015
+ filter[`metadata.annotations.${key}`] = value;
2016
+ }
2017
+ const res = await this.catalogApi.getEntities({ filter }, { token });
2018
+ result = res.items;
2019
+ } else if ("filter" in query) {
2020
+ const res = await this.catalogApi.getEntities(
2021
+ { filter: query.filter },
2022
+ { token }
2023
+ );
2024
+ result = res.items;
2025
+ } else {
2026
+ throw new errors.InputError("Invalid user lookup query");
2027
+ }
2028
+ if (Array.isArray(result)) {
2029
+ if (result.length > 1) {
2030
+ throw new errors.ConflictError("User lookup resulted in multiple matches");
2031
+ }
2032
+ result = result[0];
2033
+ }
2034
+ if (!result) {
2035
+ throw new errors.NotFoundError("User not found");
2036
+ }
2037
+ return { entity: result };
2038
+ }
2039
+ async signInWithCatalogUser(query) {
2040
+ const { entity } = await this.findCatalogUser(query);
2041
+ const ownershipRefs = getDefaultOwnershipEntityRefs(entity);
2042
+ const token = await this.tokenIssuer.issueToken({
2043
+ claims: {
2044
+ sub: catalogModel.stringifyEntityRef(entity),
2045
+ ent: ownershipRefs
2046
+ }
2047
+ });
2048
+ return { token };
2049
+ }
2050
+ }
2051
+
2052
+ function bindProviderRouters(targetRouter, options) {
2053
+ const {
2054
+ providers,
2055
+ appUrl,
2056
+ baseUrl,
2057
+ config,
2058
+ logger,
2059
+ discovery,
2060
+ auth,
2061
+ httpAuth,
2062
+ tokenManager,
2063
+ tokenIssuer,
2064
+ catalogApi
2065
+ } = options;
2066
+ const providersConfig = config.getOptionalConfig("auth.providers");
2067
+ const isOriginAllowed = createOriginFilter(config);
2068
+ for (const [providerId, providerFactory] of Object.entries(providers)) {
2069
+ if (providersConfig == null ? void 0 : providersConfig.has(providerId)) {
2070
+ logger.info(`Configuring auth provider: ${providerId}`);
2071
+ try {
2072
+ const provider = providerFactory({
2073
+ providerId,
2074
+ appUrl,
2075
+ baseUrl,
2076
+ isOriginAllowed,
2077
+ globalConfig: {
2078
+ baseUrl,
2079
+ appUrl,
2080
+ isOriginAllowed
2081
+ },
2082
+ config: providersConfig.getConfig(providerId),
2083
+ logger,
2084
+ resolverContext: CatalogAuthResolverContext.create({
2085
+ logger,
2086
+ catalogApi: catalogApi != null ? catalogApi : new catalogClient.CatalogClient({ discoveryApi: discovery }),
2087
+ tokenIssuer,
2088
+ tokenManager,
2089
+ discovery,
2090
+ auth,
2091
+ httpAuth
2092
+ })
2093
+ });
2094
+ const r = Router__default["default"]();
2095
+ r.get("/start", provider.start.bind(provider));
2096
+ r.get("/handler/frame", provider.frameHandler.bind(provider));
2097
+ r.post("/handler/frame", provider.frameHandler.bind(provider));
2098
+ if (provider.logout) {
2099
+ r.post("/logout", provider.logout.bind(provider));
2100
+ }
2101
+ if (provider.refresh) {
2102
+ r.get("/refresh", provider.refresh.bind(provider));
2103
+ r.post("/refresh", provider.refresh.bind(provider));
2104
+ }
2105
+ targetRouter.use(`/${providerId}`, r);
2106
+ } catch (e) {
2107
+ errors.assertError(e);
2108
+ if (process.env.NODE_ENV !== "development") {
2109
+ throw new Error(
2110
+ `Failed to initialize ${providerId} auth provider, ${e.message}`
2111
+ );
2112
+ }
2113
+ logger.warn(`Skipping ${providerId} auth provider, ${e.message}`);
2114
+ targetRouter.use(`/${providerId}`, () => {
2115
+ throw new errors.NotFoundError(
2116
+ `Auth provider registered for '${providerId}' is misconfigured. This could mean the configs under auth.providers.${providerId} are missing or the environment variables used are not defined. Check the auth backend plugin logs when the backend starts to see more details.`
2117
+ );
2118
+ });
2119
+ }
2120
+ } else {
2121
+ targetRouter.use(`/${providerId}`, () => {
2122
+ throw new errors.NotFoundError(
2123
+ `No auth provider registered for '${providerId}'`
2124
+ );
2125
+ });
2126
+ }
2127
+ }
2128
+ }
2129
+ function createOriginFilter(config) {
2130
+ var _a;
2131
+ const appUrl = config.getString("app.baseUrl");
2132
+ const { origin: appOrigin } = new URL(appUrl);
2133
+ const allowedOrigins = config.getOptionalStringArray(
2134
+ "auth.experimentalExtraAllowedOrigins"
2135
+ );
2136
+ const allowedOriginPatterns = (_a = allowedOrigins == null ? void 0 : allowedOrigins.map(
2137
+ (pattern) => new minimatch.Minimatch(pattern, { nocase: true, noglobstar: true })
2138
+ )) != null ? _a : [];
2139
+ return (origin) => {
2140
+ if (origin === appOrigin) {
2141
+ return true;
2142
+ }
2143
+ return allowedOriginPatterns.some((pattern) => pattern.match(origin));
2144
+ };
2145
+ }
2146
+
2147
+ function bindOidcRouter(targetRouter, options) {
1836
2148
  const { baseUrl, tokenIssuer } = options;
1837
2149
  const router = Router__default["default"]();
2150
+ targetRouter.use(router);
1838
2151
  const config = {
1839
2152
  issuer: baseUrl,
1840
2153
  token_endpoint: `${baseUrl}/v1/token`,
@@ -1872,26 +2185,25 @@ function createOidcRouter(options) {
1872
2185
  router.get("/v1/userinfo", (_req, res) => {
1873
2186
  res.status(501).send("Not Implemented");
1874
2187
  });
1875
- return router;
1876
2188
  }
1877
2189
 
1878
- var __defProp$4 = Object.defineProperty;
1879
- var __defNormalProp$4 = (obj, key, value) => key in obj ? __defProp$4(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
1880
- var __publicField$4 = (obj, key, value) => {
1881
- __defNormalProp$4(obj, typeof key !== "symbol" ? key + "" : key, value);
2190
+ var __defProp$3 = Object.defineProperty;
2191
+ var __defNormalProp$3 = (obj, key, value) => key in obj ? __defProp$3(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
2192
+ var __publicField$3 = (obj, key, value) => {
2193
+ __defNormalProp$3(obj, typeof key !== "symbol" ? key + "" : key, value);
1882
2194
  return value;
1883
2195
  };
1884
2196
  const MS_IN_S$1 = 1e3;
1885
2197
  const MAX_TOKEN_LENGTH = 32768;
1886
2198
  class TokenFactory {
1887
2199
  constructor(options) {
1888
- __publicField$4(this, "issuer");
1889
- __publicField$4(this, "logger");
1890
- __publicField$4(this, "keyStore");
1891
- __publicField$4(this, "keyDurationSeconds");
1892
- __publicField$4(this, "algorithm");
1893
- __publicField$4(this, "keyExpiry");
1894
- __publicField$4(this, "privateKeyPromise");
2200
+ __publicField$3(this, "issuer");
2201
+ __publicField$3(this, "logger");
2202
+ __publicField$3(this, "keyStore");
2203
+ __publicField$3(this, "keyDurationSeconds");
2204
+ __publicField$3(this, "algorithm");
2205
+ __publicField$3(this, "keyExpiry");
2206
+ __publicField$3(this, "privateKeyPromise");
1895
2207
  var _a;
1896
2208
  this.issuer = options.issuer;
1897
2209
  this.logger = options.logger;
@@ -2021,15 +2333,15 @@ class DatabaseKeyStore {
2021
2333
  }
2022
2334
  }
2023
2335
 
2024
- var __defProp$3 = Object.defineProperty;
2025
- var __defNormalProp$3 = (obj, key, value) => key in obj ? __defProp$3(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
2026
- var __publicField$3 = (obj, key, value) => {
2027
- __defNormalProp$3(obj, typeof key !== "symbol" ? key + "" : key, value);
2336
+ var __defProp$2 = Object.defineProperty;
2337
+ var __defNormalProp$2 = (obj, key, value) => key in obj ? __defProp$2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
2338
+ var __publicField$2 = (obj, key, value) => {
2339
+ __defNormalProp$2(obj, typeof key !== "symbol" ? key + "" : key, value);
2028
2340
  return value;
2029
2341
  };
2030
2342
  class MemoryKeyStore {
2031
2343
  constructor() {
2032
- __publicField$3(this, "keys", /* @__PURE__ */ new Map());
2344
+ __publicField$2(this, "keys", /* @__PURE__ */ new Map());
2033
2345
  }
2034
2346
  async addKey(key) {
2035
2347
  this.keys.set(key.kid, {
@@ -2134,17 +2446,17 @@ class FirestoreKeyStore {
2134
2446
  }
2135
2447
  }
2136
2448
 
2137
- var __defProp$2 = Object.defineProperty;
2138
- var __defNormalProp$2 = (obj, key, value) => key in obj ? __defProp$2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
2139
- var __publicField$2 = (obj, key, value) => {
2140
- __defNormalProp$2(obj, typeof key !== "symbol" ? key + "" : key, value);
2449
+ var __defProp$1 = Object.defineProperty;
2450
+ var __defNormalProp$1 = (obj, key, value) => key in obj ? __defProp$1(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
2451
+ var __publicField$1 = (obj, key, value) => {
2452
+ __defNormalProp$1(obj, typeof key !== "symbol" ? key + "" : key, value);
2141
2453
  return value;
2142
2454
  };
2143
2455
  const DEFAULT_ALGORITHM = "ES256";
2144
2456
  class StaticKeyStore {
2145
2457
  constructor(keyPairs) {
2146
- __publicField$2(this, "keyPairs");
2147
- __publicField$2(this, "createdAt");
2458
+ __publicField$1(this, "keyPairs");
2459
+ __publicField$1(this, "createdAt");
2148
2460
  if (keyPairs.length === 0) {
2149
2461
  throw new Error("Should provide at least one key pair");
2150
2462
  }
@@ -2270,172 +2582,6 @@ class KeyStores {
2270
2582
  }
2271
2583
  }
2272
2584
 
2273
- var __defProp$1 = Object.defineProperty;
2274
- var __defNormalProp$1 = (obj, key, value) => key in obj ? __defProp$1(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
2275
- var __publicField$1 = (obj, key, value) => {
2276
- __defNormalProp$1(obj, typeof key !== "symbol" ? key + "" : key, value);
2277
- return value;
2278
- };
2279
- class CatalogIdentityClient {
2280
- constructor(options) {
2281
- __publicField$1(this, "catalogApi");
2282
- __publicField$1(this, "tokenManager");
2283
- this.catalogApi = options.catalogApi;
2284
- this.tokenManager = options.tokenManager;
2285
- }
2286
- /**
2287
- * Looks up a single user using a query.
2288
- *
2289
- * Throws a NotFoundError or ConflictError if 0 or multiple users are found.
2290
- */
2291
- async findUser(query) {
2292
- const filter = {
2293
- kind: "user"
2294
- };
2295
- for (const [key, value] of Object.entries(query.annotations)) {
2296
- filter[`metadata.annotations.${key}`] = value;
2297
- }
2298
- const { token } = await this.tokenManager.getToken();
2299
- const { items } = await this.catalogApi.getEntities({ filter }, { token });
2300
- if (items.length !== 1) {
2301
- if (items.length > 1) {
2302
- throw new errors.ConflictError("User lookup resulted in multiple matches");
2303
- } else {
2304
- throw new errors.NotFoundError("User not found");
2305
- }
2306
- }
2307
- return items[0];
2308
- }
2309
- /**
2310
- * Resolve additional entity claims from the catalog, using the passed-in entity names. Designed
2311
- * to be used within a `signInResolver` where additional entity claims might be provided, but
2312
- * group membership and transient group membership lean on imported catalog relations.
2313
- *
2314
- * Returns a superset of the entity names that can be passed directly to `issueToken` as `ent`.
2315
- */
2316
- async resolveCatalogMembership(query) {
2317
- const { entityRefs, logger } = query;
2318
- const resolvedEntityRefs = entityRefs.map((ref) => {
2319
- try {
2320
- const parsedRef = catalogModel.parseEntityRef(ref.toLocaleLowerCase("en-US"), {
2321
- defaultKind: "user",
2322
- defaultNamespace: "default"
2323
- });
2324
- return parsedRef;
2325
- } catch {
2326
- logger == null ? void 0 : logger.warn(`Failed to parse entityRef from ${ref}, ignoring`);
2327
- return null;
2328
- }
2329
- }).filter((ref) => ref !== null);
2330
- const filter = resolvedEntityRefs.map((ref) => ({
2331
- kind: ref.kind,
2332
- "metadata.namespace": ref.namespace,
2333
- "metadata.name": ref.name
2334
- }));
2335
- const { token } = await this.tokenManager.getToken();
2336
- const entities = await this.catalogApi.getEntities({ filter }, { token }).then((r) => r.items);
2337
- if (entityRefs.length !== entities.length) {
2338
- const foundEntityNames = entities.map(catalogModel.stringifyEntityRef);
2339
- const missingEntityNames = resolvedEntityRefs.map(catalogModel.stringifyEntityRef).filter((s) => !foundEntityNames.includes(s));
2340
- logger == null ? void 0 : logger.debug(`Entities not found for refs ${missingEntityNames.join()}`);
2341
- }
2342
- const memberOf = entities.flatMap(
2343
- (e) => {
2344
- var _a, _b;
2345
- return (_b = (_a = e.relations) == null ? void 0 : _a.filter((r) => r.type === catalogModel.RELATION_MEMBER_OF).map((r) => r.targetRef)) != null ? _b : [];
2346
- }
2347
- );
2348
- const newEntityRefs = [
2349
- ...new Set(resolvedEntityRefs.map(catalogModel.stringifyEntityRef).concat(memberOf))
2350
- ];
2351
- logger == null ? void 0 : logger.debug(`Found catalog membership: ${newEntityRefs.join()}`);
2352
- return newEntityRefs;
2353
- }
2354
- }
2355
-
2356
- function getDefaultOwnershipEntityRefs(entity) {
2357
- var _a, _b;
2358
- const membershipRefs = (_b = (_a = entity.relations) == null ? void 0 : _a.filter(
2359
- (r) => r.type === catalogModel.RELATION_MEMBER_OF && r.targetRef.startsWith("group:")
2360
- ).map((r) => r.targetRef)) != null ? _b : [];
2361
- return Array.from(/* @__PURE__ */ new Set([catalogModel.stringifyEntityRef(entity), ...membershipRefs]));
2362
- }
2363
- class CatalogAuthResolverContext {
2364
- constructor(logger, tokenIssuer, catalogIdentityClient, catalogApi, tokenManager) {
2365
- this.logger = logger;
2366
- this.tokenIssuer = tokenIssuer;
2367
- this.catalogIdentityClient = catalogIdentityClient;
2368
- this.catalogApi = catalogApi;
2369
- this.tokenManager = tokenManager;
2370
- }
2371
- static create(options) {
2372
- const catalogIdentityClient = new CatalogIdentityClient({
2373
- catalogApi: options.catalogApi,
2374
- tokenManager: options.tokenManager
2375
- });
2376
- return new CatalogAuthResolverContext(
2377
- options.logger,
2378
- options.tokenIssuer,
2379
- catalogIdentityClient,
2380
- options.catalogApi,
2381
- options.tokenManager
2382
- );
2383
- }
2384
- async issueToken(params) {
2385
- const token = await this.tokenIssuer.issueToken(params);
2386
- return { token };
2387
- }
2388
- async findCatalogUser(query) {
2389
- let result = void 0;
2390
- const { token } = await this.tokenManager.getToken();
2391
- if ("entityRef" in query) {
2392
- const entityRef = catalogModel.parseEntityRef(query.entityRef, {
2393
- defaultKind: "User",
2394
- defaultNamespace: catalogModel.DEFAULT_NAMESPACE
2395
- });
2396
- result = await this.catalogApi.getEntityByRef(entityRef, { token });
2397
- } else if ("annotations" in query) {
2398
- const filter = {
2399
- kind: "user"
2400
- };
2401
- for (const [key, value] of Object.entries(query.annotations)) {
2402
- filter[`metadata.annotations.${key}`] = value;
2403
- }
2404
- const res = await this.catalogApi.getEntities({ filter }, { token });
2405
- result = res.items;
2406
- } else if ("filter" in query) {
2407
- const res = await this.catalogApi.getEntities(
2408
- { filter: query.filter },
2409
- { token }
2410
- );
2411
- result = res.items;
2412
- } else {
2413
- throw new errors.InputError("Invalid user lookup query");
2414
- }
2415
- if (Array.isArray(result)) {
2416
- if (result.length > 1) {
2417
- throw new errors.ConflictError("User lookup resulted in multiple matches");
2418
- }
2419
- result = result[0];
2420
- }
2421
- if (!result) {
2422
- throw new errors.NotFoundError("User not found");
2423
- }
2424
- return { entity: result };
2425
- }
2426
- async signInWithCatalogUser(query) {
2427
- const { entity } = await this.findCatalogUser(query);
2428
- const ownershipRefs = getDefaultOwnershipEntityRefs(entity);
2429
- const token = await this.tokenIssuer.issueToken({
2430
- claims: {
2431
- sub: catalogModel.stringifyEntityRef(entity),
2432
- ent: ownershipRefs
2433
- }
2434
- });
2435
- return { token };
2436
- }
2437
- }
2438
-
2439
2585
  var __accessCheck = (obj, member, msg) => {
2440
2586
  if (!member.has(obj))
2441
2587
  throw TypeError("Cannot " + msg);
@@ -2580,11 +2726,10 @@ async function createRouter(options) {
2580
2726
  config,
2581
2727
  discovery,
2582
2728
  database,
2583
- tokenManager,
2584
2729
  tokenFactoryAlgorithm,
2585
- providerFactories = {},
2586
- catalogApi
2730
+ providerFactories = {}
2587
2731
  } = options;
2732
+ const { auth, httpAuth } = backendCommon.createLegacyAuthAdapters(options);
2588
2733
  const router = Router__default["default"]();
2589
2734
  const appUrl = config.getString("app.baseUrl");
2590
2735
  const authUrl = await discovery.getExternalBaseUrl("auth");
@@ -2637,100 +2782,29 @@ async function createRouter(options) {
2637
2782
  }
2638
2783
  router.use(express__default["default"].urlencoded({ extended: false }));
2639
2784
  router.use(express__default["default"].json());
2640
- const allProviderFactories = options.disableDefaultProviderFactories ? providerFactories : {
2785
+ const providers = options.disableDefaultProviderFactories ? providerFactories : {
2641
2786
  ...defaultAuthProviderFactories,
2642
2787
  ...providerFactories
2643
2788
  };
2644
- const providersConfig = config.getOptionalConfig("auth.providers");
2645
- const isOriginAllowed = createOriginFilter(config);
2646
- for (const [providerId, providerFactory] of Object.entries(
2647
- allProviderFactories
2648
- )) {
2649
- if (providersConfig == null ? void 0 : providersConfig.has(providerId)) {
2650
- logger.info(`Configuring auth provider: ${providerId}`);
2651
- try {
2652
- const provider = providerFactory({
2653
- providerId,
2654
- appUrl,
2655
- baseUrl: authUrl,
2656
- isOriginAllowed,
2657
- globalConfig: {
2658
- baseUrl: authUrl,
2659
- appUrl,
2660
- isOriginAllowed
2661
- },
2662
- config: providersConfig.getConfig(providerId),
2663
- logger,
2664
- resolverContext: CatalogAuthResolverContext.create({
2665
- logger,
2666
- catalogApi: catalogApi != null ? catalogApi : new catalogClient.CatalogClient({ discoveryApi: discovery }),
2667
- tokenIssuer,
2668
- tokenManager
2669
- })
2670
- });
2671
- const r = Router__default["default"]();
2672
- r.get("/start", provider.start.bind(provider));
2673
- r.get("/handler/frame", provider.frameHandler.bind(provider));
2674
- r.post("/handler/frame", provider.frameHandler.bind(provider));
2675
- if (provider.logout) {
2676
- r.post("/logout", provider.logout.bind(provider));
2677
- }
2678
- if (provider.refresh) {
2679
- r.get("/refresh", provider.refresh.bind(provider));
2680
- r.post("/refresh", provider.refresh.bind(provider));
2681
- }
2682
- router.use(`/${providerId}`, r);
2683
- } catch (e) {
2684
- errors.assertError(e);
2685
- if (process.env.NODE_ENV !== "development") {
2686
- throw new Error(
2687
- `Failed to initialize ${providerId} auth provider, ${e.message}`
2688
- );
2689
- }
2690
- logger.warn(`Skipping ${providerId} auth provider, ${e.message}`);
2691
- router.use(`/${providerId}`, () => {
2692
- throw new errors.NotFoundError(
2693
- `Auth provider registered for '${providerId}' is misconfigured. This could mean the configs under auth.providers.${providerId} are missing or the environment variables used are not defined. Check the auth backend plugin logs when the backend starts to see more details.`
2694
- );
2695
- });
2696
- }
2697
- } else {
2698
- router.use(`/${providerId}`, () => {
2699
- throw new errors.NotFoundError(
2700
- `No auth provider registered for '${providerId}'`
2701
- );
2702
- });
2703
- }
2704
- }
2705
- router.use(
2706
- createOidcRouter({
2707
- tokenIssuer,
2708
- baseUrl: authUrl
2709
- })
2710
- );
2789
+ bindProviderRouters(router, {
2790
+ providers,
2791
+ appUrl,
2792
+ baseUrl: authUrl,
2793
+ tokenIssuer,
2794
+ ...options,
2795
+ auth,
2796
+ httpAuth
2797
+ });
2798
+ bindOidcRouter(router, {
2799
+ tokenIssuer,
2800
+ baseUrl: authUrl
2801
+ });
2711
2802
  router.use("/:provider/", (req) => {
2712
2803
  const { provider } = req.params;
2713
2804
  throw new errors.NotFoundError(`Unknown auth provider '${provider}'`);
2714
2805
  });
2715
2806
  return router;
2716
2807
  }
2717
- function createOriginFilter(config) {
2718
- var _a;
2719
- const appUrl = config.getString("app.baseUrl");
2720
- const { origin: appOrigin } = new URL(appUrl);
2721
- const allowedOrigins = config.getOptionalStringArray(
2722
- "auth.experimentalExtraAllowedOrigins"
2723
- );
2724
- const allowedOriginPatterns = (_a = allowedOrigins == null ? void 0 : allowedOrigins.map(
2725
- (pattern) => new minimatch.Minimatch(pattern, { nocase: true, noglobstar: true })
2726
- )) != null ? _a : [];
2727
- return (origin) => {
2728
- if (origin === appOrigin) {
2729
- return true;
2730
- }
2731
- return allowedOriginPatterns.some((pattern) => pattern.match(origin));
2732
- };
2733
- }
2734
2808
 
2735
2809
  const authPlugin = backendPluginApi.createBackendPlugin({
2736
2810
  pluginId: "auth",
@@ -2775,6 +2849,10 @@ const authPlugin = backendPluginApi.createBackendPlugin({
2775
2849
  providerFactories: Object.fromEntries(providers),
2776
2850
  disableDefaultProviderFactories: true
2777
2851
  });
2852
+ httpRouter.addAuthPolicy({
2853
+ path: "/",
2854
+ allow: "unauthenticated"
2855
+ });
2778
2856
  httpRouter.use(router);
2779
2857
  }
2780
2858
  });