@backstage/plugin-auth-backend 0.15.0-next.1 → 0.15.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 +59 -0
- package/config.d.ts +3 -0
- package/dist/index.cjs.js +147 -23
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +119 -1
- package/package.json +13 -13
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,64 @@
|
|
|
1
1
|
# @backstage/plugin-auth-backend
|
|
2
2
|
|
|
3
|
+
## 0.15.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 9d4040777e: **BREAKING**: Removed all directly exported auth provider factories, option types, and sign-in resolvers. For example: `AwsAlbProviderOptions`, `bitbucketUserIdSignInResolver`, `createGithubProvider`. These are all still accessible via the `providers` export. For example, use `providers.github.create()` rather than `createGithubProvider()`, and `providers.bitbucket.resolvers.userIdMatchingUserEntityAnnotation()` rather than `bitbucketUserIdSignInResolver`.
|
|
8
|
+
|
|
9
|
+
**BREAKING**: Removed the exported `AuthProviderFactoryOptions` type as well as the deprecated option fields of the `AuthProviderFactory` callback. This includes the `tokenManager`, `tokenIssuer`, `discovery`, and `catalogApi` fields. Existing usage of these should be replaced with the new utilities in the `resolverContext` field. The deprecated `TokenIssuer` type is now also removed, since it is no longer used.
|
|
10
|
+
|
|
11
|
+
**BREAKING**: Removed `getEntityClaims`, use `getDefaultOwnershipEntityRefs` instead.
|
|
12
|
+
|
|
13
|
+
**DEPRECATION**: Deprecated `AtlassianAuthProvider` as it was unintentionally exported.
|
|
14
|
+
|
|
15
|
+
- fe8e025af5: Allowed post method on /refresh path
|
|
16
|
+
|
|
17
|
+
### Patch Changes
|
|
18
|
+
|
|
19
|
+
- 3cedfd8365: add Cloudflare Access auth provider to auth-backend
|
|
20
|
+
- f2cf79d62e: Added an option for the auth backend router to select the algorithm for the JWT token signing keys
|
|
21
|
+
- 8e03db907a: Auth provider now also export createAuthProviderIntegration
|
|
22
|
+
- a70869e775: Updated dependency `msw` to `^0.43.0`.
|
|
23
|
+
- 4e9a90e307: Updated dependency `luxon` to `^3.0.0`.
|
|
24
|
+
- 8006d0f9bf: Updated dependency `msw` to `^0.44.0`.
|
|
25
|
+
- 679b32172e: Updated dependency `knex` to `^2.0.0`.
|
|
26
|
+
- 859346bfbb: Updated dependency `google-auth-library` to `^8.0.0`.
|
|
27
|
+
- 3a014730dc: Add new config option for okta auth server and IDP
|
|
28
|
+
- Updated dependencies
|
|
29
|
+
- @backstage/backend-common@0.14.1
|
|
30
|
+
- @backstage/catalog-model@1.1.0
|
|
31
|
+
- @backstage/catalog-client@1.0.4
|
|
32
|
+
- @backstage/plugin-auth-node@0.2.3
|
|
33
|
+
- @backstage/errors@1.1.0
|
|
34
|
+
|
|
35
|
+
## 0.15.0-next.3
|
|
36
|
+
|
|
37
|
+
### Minor Changes
|
|
38
|
+
|
|
39
|
+
- fe8e025af5: Allowed post method on /refresh path
|
|
40
|
+
|
|
41
|
+
### Patch Changes
|
|
42
|
+
|
|
43
|
+
- a70869e775: Updated dependency `msw` to `^0.43.0`.
|
|
44
|
+
- 4e9a90e307: Updated dependency `luxon` to `^3.0.0`.
|
|
45
|
+
- 3a014730dc: Add new config option for okta auth server and IDP
|
|
46
|
+
- Updated dependencies
|
|
47
|
+
- @backstage/backend-common@0.14.1-next.3
|
|
48
|
+
- @backstage/catalog-client@1.0.4-next.2
|
|
49
|
+
- @backstage/plugin-auth-node@0.2.3-next.2
|
|
50
|
+
- @backstage/catalog-model@1.1.0-next.3
|
|
51
|
+
|
|
52
|
+
## 0.15.0-next.2
|
|
53
|
+
|
|
54
|
+
### Patch Changes
|
|
55
|
+
|
|
56
|
+
- 8e03db907a: Auth provider now also export createAuthProviderIntegration
|
|
57
|
+
- 679b32172e: Updated dependency `knex` to `^2.0.0`.
|
|
58
|
+
- Updated dependencies
|
|
59
|
+
- @backstage/catalog-model@1.1.0-next.2
|
|
60
|
+
- @backstage/backend-common@0.14.1-next.2
|
|
61
|
+
|
|
3
62
|
## 0.15.0-next.1
|
|
4
63
|
|
|
5
64
|
### Minor Changes
|
package/config.d.ts
CHANGED
package/dist/index.cjs.js
CHANGED
|
@@ -22,7 +22,7 @@ var passportGoogleOauth20 = require('passport-google-oauth20');
|
|
|
22
22
|
var passportMicrosoft = require('passport-microsoft');
|
|
23
23
|
var pluginAuthNode = require('@backstage/plugin-auth-node');
|
|
24
24
|
var openidClient = require('openid-client');
|
|
25
|
-
var passportOktaOauth = require('passport-okta-oauth');
|
|
25
|
+
var passportOktaOauth = require('@davidzemon/passport-okta-oauth');
|
|
26
26
|
var passportOneloginOauth = require('passport-onelogin-oauth');
|
|
27
27
|
var passportSaml = require('passport-saml');
|
|
28
28
|
var catalogClient = require('@backstage/catalog-client');
|
|
@@ -995,6 +995,143 @@ const bitbucket = createAuthProviderIntegration({
|
|
|
995
995
|
}
|
|
996
996
|
});
|
|
997
997
|
|
|
998
|
+
const commonByEmailLocalPartResolver = async (info, ctx) => {
|
|
999
|
+
const { profile } = info;
|
|
1000
|
+
if (!profile.email) {
|
|
1001
|
+
throw new Error("Login failed, user profile does not contain an email");
|
|
1002
|
+
}
|
|
1003
|
+
const [localPart] = profile.email.split("@");
|
|
1004
|
+
return ctx.signInWithCatalogUser({
|
|
1005
|
+
entityRef: { name: localPart }
|
|
1006
|
+
});
|
|
1007
|
+
};
|
|
1008
|
+
const commonByEmailResolver = async (info, ctx) => {
|
|
1009
|
+
const { profile } = info;
|
|
1010
|
+
if (!profile.email) {
|
|
1011
|
+
throw new Error("Login failed, user profile does not contain an email");
|
|
1012
|
+
}
|
|
1013
|
+
return ctx.signInWithCatalogUser({
|
|
1014
|
+
filter: {
|
|
1015
|
+
"spec.profile.email": profile.email
|
|
1016
|
+
}
|
|
1017
|
+
});
|
|
1018
|
+
};
|
|
1019
|
+
|
|
1020
|
+
const CF_JWT_HEADER = "cf-access-jwt-assertion";
|
|
1021
|
+
const COOKIE_AUTH_NAME = "CF_Authorization";
|
|
1022
|
+
const CACHE_PREFIX = "providers/cloudflare-access/profile-v1";
|
|
1023
|
+
class CloudflareAccessAuthProvider {
|
|
1024
|
+
constructor(options) {
|
|
1025
|
+
this.teamName = options.teamName;
|
|
1026
|
+
this.authHandler = options.authHandler;
|
|
1027
|
+
this.signInResolver = options.signInResolver;
|
|
1028
|
+
this.resolverContext = options.resolverContext;
|
|
1029
|
+
this.jwtKeySet = jose.createRemoteJWKSet(new URL(`https://${this.teamName}.cloudflareaccess.com/cdn-cgi/access/certs`));
|
|
1030
|
+
this.cache = options.cache;
|
|
1031
|
+
}
|
|
1032
|
+
frameHandler() {
|
|
1033
|
+
return Promise.resolve();
|
|
1034
|
+
}
|
|
1035
|
+
async refresh(req, res) {
|
|
1036
|
+
const result = await this.getResult(req);
|
|
1037
|
+
const response = await this.handleResult(result);
|
|
1038
|
+
res.json(response);
|
|
1039
|
+
}
|
|
1040
|
+
start() {
|
|
1041
|
+
return Promise.resolve();
|
|
1042
|
+
}
|
|
1043
|
+
async getIdentityProfile(jwt) {
|
|
1044
|
+
const headers = new fetch.Headers();
|
|
1045
|
+
headers.set(CF_JWT_HEADER, jwt);
|
|
1046
|
+
headers.set("cookie", `${COOKIE_AUTH_NAME}=${jwt}`);
|
|
1047
|
+
try {
|
|
1048
|
+
const res = await fetch__default["default"](`https://${this.teamName}.cloudflareaccess.com/cdn-cgi/access/get-identity`, { headers });
|
|
1049
|
+
if (!res.ok) {
|
|
1050
|
+
throw errors.ResponseError.fromResponse(res);
|
|
1051
|
+
}
|
|
1052
|
+
const cfIdentity = await res.json();
|
|
1053
|
+
return cfIdentity;
|
|
1054
|
+
} catch (err) {
|
|
1055
|
+
throw new errors.ForwardedError("getIdentityProfile failed", err);
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
async getResult(req) {
|
|
1059
|
+
var _a, _b;
|
|
1060
|
+
let jwt = req.header(CF_JWT_HEADER);
|
|
1061
|
+
if (!jwt) {
|
|
1062
|
+
jwt = req.cookies.CF_Authorization;
|
|
1063
|
+
}
|
|
1064
|
+
if (!jwt) {
|
|
1065
|
+
throw new errors.AuthenticationError(`Missing ${CF_JWT_HEADER} from Cloudflare Access`);
|
|
1066
|
+
}
|
|
1067
|
+
const verifyResult = await jose.jwtVerify(jwt, this.jwtKeySet, {
|
|
1068
|
+
issuer: `https://${this.teamName}.cloudflareaccess.com`
|
|
1069
|
+
});
|
|
1070
|
+
const sub = verifyResult.payload.sub;
|
|
1071
|
+
const cfAccessResultStr = await ((_a = this.cache) == null ? void 0 : _a.get(`${CACHE_PREFIX}/${sub}`));
|
|
1072
|
+
if (typeof cfAccessResultStr === "string") {
|
|
1073
|
+
return JSON.parse(cfAccessResultStr);
|
|
1074
|
+
}
|
|
1075
|
+
const claims = verifyResult.payload;
|
|
1076
|
+
try {
|
|
1077
|
+
const cfIdentity = await this.getIdentityProfile(jwt);
|
|
1078
|
+
const cfAccessResult = {
|
|
1079
|
+
claims,
|
|
1080
|
+
cfIdentity,
|
|
1081
|
+
expiresInSeconds: claims.exp - claims.iat
|
|
1082
|
+
};
|
|
1083
|
+
(_b = this.cache) == null ? void 0 : _b.set(`${CACHE_PREFIX}/${sub}`, JSON.stringify(cfAccessResult));
|
|
1084
|
+
return cfAccessResult;
|
|
1085
|
+
} catch (err) {
|
|
1086
|
+
throw new errors.ForwardedError("Failed to populate access identity information", err);
|
|
1087
|
+
}
|
|
1088
|
+
}
|
|
1089
|
+
async handleResult(result) {
|
|
1090
|
+
const { profile } = await this.authHandler(result, this.resolverContext);
|
|
1091
|
+
const backstageIdentity = await this.signInResolver({
|
|
1092
|
+
result,
|
|
1093
|
+
profile
|
|
1094
|
+
}, this.resolverContext);
|
|
1095
|
+
return {
|
|
1096
|
+
providerInfo: {
|
|
1097
|
+
expiresInSeconds: result.expiresInSeconds,
|
|
1098
|
+
claims: result.claims,
|
|
1099
|
+
cfAccessIdentityProfile: result.cfIdentity
|
|
1100
|
+
},
|
|
1101
|
+
backstageIdentity: prepareBackstageIdentityResponse(backstageIdentity),
|
|
1102
|
+
profile
|
|
1103
|
+
};
|
|
1104
|
+
}
|
|
1105
|
+
}
|
|
1106
|
+
const cfAccess = createAuthProviderIntegration({
|
|
1107
|
+
create(options) {
|
|
1108
|
+
return ({ config, resolverContext }) => {
|
|
1109
|
+
const teamName = config.getString("teamName");
|
|
1110
|
+
if (!options.signIn.resolver) {
|
|
1111
|
+
throw new Error("SignInResolver is required to use this authentication provider");
|
|
1112
|
+
}
|
|
1113
|
+
const authHandler = (options == null ? void 0 : options.authHandler) ? options.authHandler : async ({ claims, cfIdentity }) => {
|
|
1114
|
+
return {
|
|
1115
|
+
profile: {
|
|
1116
|
+
email: claims.email,
|
|
1117
|
+
displayName: cfIdentity.name
|
|
1118
|
+
}
|
|
1119
|
+
};
|
|
1120
|
+
};
|
|
1121
|
+
return new CloudflareAccessAuthProvider({
|
|
1122
|
+
teamName,
|
|
1123
|
+
signInResolver: options == null ? void 0 : options.signIn.resolver,
|
|
1124
|
+
authHandler,
|
|
1125
|
+
resolverContext,
|
|
1126
|
+
...options.cache && { cache: options.cache }
|
|
1127
|
+
});
|
|
1128
|
+
};
|
|
1129
|
+
},
|
|
1130
|
+
resolvers: {
|
|
1131
|
+
emailMatchingUserEntityProfileEmail: () => commonByEmailResolver
|
|
1132
|
+
}
|
|
1133
|
+
});
|
|
1134
|
+
|
|
998
1135
|
const IAP_JWT_HEADER = "x-goog-iap-jwt-assertion";
|
|
999
1136
|
|
|
1000
1137
|
function createTokenValidator(audience, mockClient) {
|
|
@@ -1314,28 +1451,6 @@ const gitlab = createAuthProviderIntegration({
|
|
|
1314
1451
|
}
|
|
1315
1452
|
});
|
|
1316
1453
|
|
|
1317
|
-
const commonByEmailLocalPartResolver = async (info, ctx) => {
|
|
1318
|
-
const { profile } = info;
|
|
1319
|
-
if (!profile.email) {
|
|
1320
|
-
throw new Error("Login failed, user profile does not contain an email");
|
|
1321
|
-
}
|
|
1322
|
-
const [localPart] = profile.email.split("@");
|
|
1323
|
-
return ctx.signInWithCatalogUser({
|
|
1324
|
-
entityRef: { name: localPart }
|
|
1325
|
-
});
|
|
1326
|
-
};
|
|
1327
|
-
const commonByEmailResolver = async (info, ctx) => {
|
|
1328
|
-
const { profile } = info;
|
|
1329
|
-
if (!profile.email) {
|
|
1330
|
-
throw new Error("Login failed, user profile does not contain an email");
|
|
1331
|
-
}
|
|
1332
|
-
return ctx.signInWithCatalogUser({
|
|
1333
|
-
filter: {
|
|
1334
|
-
"spec.profile.email": profile.email
|
|
1335
|
-
}
|
|
1336
|
-
});
|
|
1337
|
-
};
|
|
1338
|
-
|
|
1339
1454
|
class GoogleAuthProvider {
|
|
1340
1455
|
constructor(options) {
|
|
1341
1456
|
this.authHandler = options.authHandler;
|
|
@@ -1911,6 +2026,8 @@ class OktaAuthProvider {
|
|
|
1911
2026
|
clientSecret: options.clientSecret,
|
|
1912
2027
|
callbackURL: options.callbackUrl,
|
|
1913
2028
|
audience: options.audience,
|
|
2029
|
+
authServerID: options.authServerId,
|
|
2030
|
+
idp: options.idp,
|
|
1914
2031
|
passReqToCallback: false,
|
|
1915
2032
|
store: this.store,
|
|
1916
2033
|
response_type: "code"
|
|
@@ -1979,6 +2096,8 @@ const okta = createAuthProviderIntegration({
|
|
|
1979
2096
|
const clientId = envConfig.getString("clientId");
|
|
1980
2097
|
const clientSecret = envConfig.getString("clientSecret");
|
|
1981
2098
|
const audience = envConfig.getString("audience");
|
|
2099
|
+
const authServerId = envConfig.getOptionalString("authServerId");
|
|
2100
|
+
const idp = envConfig.getOptionalString("idp");
|
|
1982
2101
|
const customCallbackUrl = envConfig.getOptionalString("callbackUrl");
|
|
1983
2102
|
const callbackUrl = customCallbackUrl || `${globalConfig.baseUrl}/${providerId}/handler/frame`;
|
|
1984
2103
|
if (!audience.startsWith("https://")) {
|
|
@@ -1989,6 +2108,8 @@ const okta = createAuthProviderIntegration({
|
|
|
1989
2108
|
});
|
|
1990
2109
|
const provider = new OktaAuthProvider({
|
|
1991
2110
|
audience,
|
|
2111
|
+
authServerId,
|
|
2112
|
+
idp,
|
|
1992
2113
|
clientId,
|
|
1993
2114
|
clientSecret,
|
|
1994
2115
|
callbackUrl,
|
|
@@ -2215,6 +2336,7 @@ const providers = Object.freeze({
|
|
|
2215
2336
|
auth0,
|
|
2216
2337
|
awsAlb,
|
|
2217
2338
|
bitbucket,
|
|
2339
|
+
cfAccess,
|
|
2218
2340
|
gcpIap,
|
|
2219
2341
|
github,
|
|
2220
2342
|
gitlab,
|
|
@@ -2724,6 +2846,7 @@ async function createRouter(options) {
|
|
|
2724
2846
|
}
|
|
2725
2847
|
if (provider.refresh) {
|
|
2726
2848
|
r.get("/refresh", provider.refresh.bind(provider));
|
|
2849
|
+
r.post("/refresh", provider.refresh.bind(provider));
|
|
2727
2850
|
}
|
|
2728
2851
|
router.use(`/${providerId}`, r);
|
|
2729
2852
|
} catch (e) {
|
|
@@ -2770,6 +2893,7 @@ exports.AtlassianAuthProvider = AtlassianAuthProvider;
|
|
|
2770
2893
|
exports.CatalogIdentityClient = CatalogIdentityClient;
|
|
2771
2894
|
exports.OAuthAdapter = OAuthAdapter;
|
|
2772
2895
|
exports.OAuthEnvironmentHandler = OAuthEnvironmentHandler;
|
|
2896
|
+
exports.createAuthProviderIntegration = createAuthProviderIntegration;
|
|
2773
2897
|
exports.createOriginFilter = createOriginFilter;
|
|
2774
2898
|
exports.createRouter = createRouter;
|
|
2775
2899
|
exports.defaultAuthProviderFactories = defaultAuthProviderFactories;
|