@backstage/plugin-auth-backend 0.15.0-next.3 → 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 +32 -0
- package/config.d.ts +3 -0
- package/dist/index.cjs.js +138 -22
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +102 -1
- package/package.json +10 -10
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,37 @@
|
|
|
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
|
+
|
|
3
35
|
## 0.15.0-next.3
|
|
4
36
|
|
|
5
37
|
### Minor Changes
|
package/config.d.ts
CHANGED
package/dist/index.cjs.js
CHANGED
|
@@ -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;
|
|
@@ -2221,6 +2336,7 @@ const providers = Object.freeze({
|
|
|
2221
2336
|
auth0,
|
|
2222
2337
|
awsAlb,
|
|
2223
2338
|
bitbucket,
|
|
2339
|
+
cfAccess,
|
|
2224
2340
|
gcpIap,
|
|
2225
2341
|
github,
|
|
2226
2342
|
gitlab,
|