@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/CHANGELOG.md +36 -41
- package/config.d.ts +5 -0
- package/dist/index.cjs.js +362 -284
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +17 -11
- package/package.json +43 -43
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
|
|
994
|
-
const
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
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$
|
|
1879
|
-
var __defNormalProp$
|
|
1880
|
-
var __publicField$
|
|
1881
|
-
__defNormalProp$
|
|
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$
|
|
1889
|
-
__publicField$
|
|
1890
|
-
__publicField$
|
|
1891
|
-
__publicField$
|
|
1892
|
-
__publicField$
|
|
1893
|
-
__publicField$
|
|
1894
|
-
__publicField$
|
|
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$
|
|
2025
|
-
var __defNormalProp$
|
|
2026
|
-
var __publicField$
|
|
2027
|
-
__defNormalProp$
|
|
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$
|
|
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$
|
|
2138
|
-
var __defNormalProp$
|
|
2139
|
-
var __publicField$
|
|
2140
|
-
__defNormalProp$
|
|
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$
|
|
2147
|
-
__publicField$
|
|
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
|
|
2785
|
+
const providers = options.disableDefaultProviderFactories ? providerFactories : {
|
|
2641
2786
|
...defaultAuthProviderFactories,
|
|
2642
2787
|
...providerFactories
|
|
2643
2788
|
};
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
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
|
});
|