@backstage/plugin-auth-backend 0.22.3 → 0.22.4-next.1
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 +54 -0
- package/dist/index.cjs.js +82 -9
- package/dist/index.cjs.js.map +1 -1
- package/package.json +20 -20
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,59 @@
|
|
|
1
1
|
# @backstage/plugin-auth-backend
|
|
2
2
|
|
|
3
|
+
## 0.22.4-next.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- d62bc51: Added token type header parameter and user identity proof to issued user tokens.
|
|
8
|
+
- bf4d71a: Initial implementation of the `/v1/userinfo` endpoint, which is now able to parse and return the `sub` and `ent` claims from a Backstage user token.
|
|
9
|
+
- Updated dependencies
|
|
10
|
+
- @backstage/backend-common@0.21.7-next.1
|
|
11
|
+
- @backstage/backend-plugin-api@0.6.17-next.1
|
|
12
|
+
- @backstage/plugin-auth-node@0.4.12-next.1
|
|
13
|
+
- @backstage/plugin-auth-backend-module-aws-alb-provider@0.1.9-next.1
|
|
14
|
+
- @backstage/plugin-auth-backend-module-oidc-provider@0.1.8-next.1
|
|
15
|
+
- @backstage/catalog-client@1.6.4-next.0
|
|
16
|
+
- @backstage/catalog-model@1.4.5
|
|
17
|
+
- @backstage/config@1.2.0
|
|
18
|
+
- @backstage/errors@1.2.4
|
|
19
|
+
- @backstage/types@1.1.1
|
|
20
|
+
- @backstage/plugin-auth-backend-module-atlassian-provider@0.1.9-next.1
|
|
21
|
+
- @backstage/plugin-auth-backend-module-gcp-iap-provider@0.2.12-next.1
|
|
22
|
+
- @backstage/plugin-auth-backend-module-github-provider@0.1.14-next.1
|
|
23
|
+
- @backstage/plugin-auth-backend-module-gitlab-provider@0.1.14-next.1
|
|
24
|
+
- @backstage/plugin-auth-backend-module-google-provider@0.1.14-next.1
|
|
25
|
+
- @backstage/plugin-auth-backend-module-microsoft-provider@0.1.12-next.1
|
|
26
|
+
- @backstage/plugin-auth-backend-module-oauth2-provider@0.1.14-next.1
|
|
27
|
+
- @backstage/plugin-auth-backend-module-oauth2-proxy-provider@0.1.10-next.1
|
|
28
|
+
- @backstage/plugin-auth-backend-module-okta-provider@0.0.10-next.1
|
|
29
|
+
- @backstage/plugin-catalog-node@1.11.1-next.1
|
|
30
|
+
|
|
31
|
+
## 0.22.4-next.0
|
|
32
|
+
|
|
33
|
+
### Patch Changes
|
|
34
|
+
|
|
35
|
+
- Updated dependencies
|
|
36
|
+
- @backstage/backend-common@0.21.7-next.0
|
|
37
|
+
- @backstage/backend-plugin-api@0.6.17-next.0
|
|
38
|
+
- @backstage/catalog-client@1.6.3
|
|
39
|
+
- @backstage/catalog-model@1.4.5
|
|
40
|
+
- @backstage/config@1.2.0
|
|
41
|
+
- @backstage/errors@1.2.4
|
|
42
|
+
- @backstage/types@1.1.1
|
|
43
|
+
- @backstage/plugin-auth-backend-module-atlassian-provider@0.1.9-next.0
|
|
44
|
+
- @backstage/plugin-auth-backend-module-aws-alb-provider@0.1.9-next.0
|
|
45
|
+
- @backstage/plugin-auth-backend-module-gcp-iap-provider@0.2.12-next.0
|
|
46
|
+
- @backstage/plugin-auth-backend-module-github-provider@0.1.14-next.0
|
|
47
|
+
- @backstage/plugin-auth-backend-module-gitlab-provider@0.1.14-next.0
|
|
48
|
+
- @backstage/plugin-auth-backend-module-google-provider@0.1.14-next.0
|
|
49
|
+
- @backstage/plugin-auth-backend-module-microsoft-provider@0.1.12-next.0
|
|
50
|
+
- @backstage/plugin-auth-backend-module-oauth2-provider@0.1.14-next.0
|
|
51
|
+
- @backstage/plugin-auth-backend-module-oauth2-proxy-provider@0.1.10-next.0
|
|
52
|
+
- @backstage/plugin-auth-backend-module-oidc-provider@0.1.8-next.0
|
|
53
|
+
- @backstage/plugin-auth-backend-module-okta-provider@0.0.10-next.0
|
|
54
|
+
- @backstage/plugin-auth-node@0.4.12-next.0
|
|
55
|
+
- @backstage/plugin-catalog-node@1.11.1-next.0
|
|
56
|
+
|
|
3
57
|
## 0.22.3
|
|
4
58
|
|
|
5
59
|
### Patch Changes
|
package/dist/index.cjs.js
CHANGED
|
@@ -2156,7 +2156,7 @@ function createOriginFilter(config) {
|
|
|
2156
2156
|
}
|
|
2157
2157
|
|
|
2158
2158
|
function bindOidcRouter(targetRouter, options) {
|
|
2159
|
-
const { baseUrl, tokenIssuer } = options;
|
|
2159
|
+
const { baseUrl, auth, tokenIssuer } = options;
|
|
2160
2160
|
const router = Router__default.default();
|
|
2161
2161
|
targetRouter.use(router);
|
|
2162
2162
|
const config = {
|
|
@@ -2180,7 +2180,7 @@ function bindOidcRouter(targetRouter, options) {
|
|
|
2180
2180
|
],
|
|
2181
2181
|
scopes_supported: ["openid"],
|
|
2182
2182
|
token_endpoint_auth_methods_supported: [],
|
|
2183
|
-
claims_supported: ["sub"],
|
|
2183
|
+
claims_supported: ["sub", "ent"],
|
|
2184
2184
|
grant_types_supported: []
|
|
2185
2185
|
};
|
|
2186
2186
|
router.get("/.well-known/openid-configuration", (_req, res) => {
|
|
@@ -2193,8 +2193,31 @@ function bindOidcRouter(targetRouter, options) {
|
|
|
2193
2193
|
router.get("/v1/token", (_req, res) => {
|
|
2194
2194
|
res.status(501).send("Not Implemented");
|
|
2195
2195
|
});
|
|
2196
|
-
router.get("/v1/userinfo", (
|
|
2197
|
-
|
|
2196
|
+
router.get("/v1/userinfo", async (req, res) => {
|
|
2197
|
+
var _a;
|
|
2198
|
+
const matches = (_a = req.headers.authorization) == null ? void 0 : _a.match(/^Bearer[ ]+(\S+)$/i);
|
|
2199
|
+
const token = matches == null ? void 0 : matches[1];
|
|
2200
|
+
if (!token) {
|
|
2201
|
+
throw new errors.AuthenticationError("No token provided");
|
|
2202
|
+
}
|
|
2203
|
+
const credentials = await auth.authenticate(token, {
|
|
2204
|
+
allowLimitedAccess: true
|
|
2205
|
+
});
|
|
2206
|
+
if (!auth.isPrincipal(credentials, "user")) {
|
|
2207
|
+
throw new errors.InputError(
|
|
2208
|
+
"Userinfo endpoint must be called with a token that represents a user principal"
|
|
2209
|
+
);
|
|
2210
|
+
}
|
|
2211
|
+
const { sub: userEntityRef, ent: ownershipEntityRefs = [] } = jose.decodeJwt(token);
|
|
2212
|
+
if (typeof userEntityRef !== "string") {
|
|
2213
|
+
throw new Error("Invalid user token, user entity ref must be a string");
|
|
2214
|
+
}
|
|
2215
|
+
if (!Array.isArray(ownershipEntityRefs) || ownershipEntityRefs.some((ref) => typeof ref !== "string")) {
|
|
2216
|
+
throw new Error(
|
|
2217
|
+
"Invalid user token, ownership entity refs must be an array of strings"
|
|
2218
|
+
);
|
|
2219
|
+
}
|
|
2220
|
+
res.json({ sub: userEntityRef, ent: ownershipEntityRefs });
|
|
2198
2221
|
});
|
|
2199
2222
|
}
|
|
2200
2223
|
|
|
@@ -2225,8 +2248,8 @@ class TokenFactory {
|
|
|
2225
2248
|
async issueToken(params) {
|
|
2226
2249
|
const key = await this.getKey();
|
|
2227
2250
|
const iss = this.issuer;
|
|
2228
|
-
const { sub, ent, ...additionalClaims } = params.claims;
|
|
2229
|
-
const aud =
|
|
2251
|
+
const { sub, ent = [sub], ...additionalClaims } = params.claims;
|
|
2252
|
+
const aud = pluginAuthNode.tokenTypes.user.audClaim;
|
|
2230
2253
|
const iat = Math.floor(Date.now() / MS_IN_S$1);
|
|
2231
2254
|
const exp = iat + this.keyDurationSeconds;
|
|
2232
2255
|
try {
|
|
@@ -2236,12 +2259,35 @@ class TokenFactory {
|
|
|
2236
2259
|
'"sub" claim provided by the auth resolver is not a valid EntityRef.'
|
|
2237
2260
|
);
|
|
2238
2261
|
}
|
|
2239
|
-
this.logger.info(`Issuing token for ${sub}, with entities ${ent != null ? ent : []}`);
|
|
2240
2262
|
if (!key.alg) {
|
|
2241
2263
|
throw new errors.AuthenticationError("No algorithm was provided in the key");
|
|
2242
2264
|
}
|
|
2243
|
-
|
|
2244
|
-
const
|
|
2265
|
+
this.logger.info(`Issuing token for ${sub}, with entities ${ent}`);
|
|
2266
|
+
const signingKey = await jose.importJWK(key);
|
|
2267
|
+
const uip = await this.createUserIdentityClaim({
|
|
2268
|
+
header: {
|
|
2269
|
+
typ: pluginAuthNode.tokenTypes.limitedUser.typParam,
|
|
2270
|
+
alg: key.alg,
|
|
2271
|
+
kid: key.kid
|
|
2272
|
+
},
|
|
2273
|
+
payload: { sub, ent, iat, exp },
|
|
2274
|
+
key: signingKey
|
|
2275
|
+
});
|
|
2276
|
+
const claims = {
|
|
2277
|
+
...additionalClaims,
|
|
2278
|
+
iss,
|
|
2279
|
+
sub,
|
|
2280
|
+
ent,
|
|
2281
|
+
aud,
|
|
2282
|
+
iat,
|
|
2283
|
+
exp,
|
|
2284
|
+
uip
|
|
2285
|
+
};
|
|
2286
|
+
const token = await new jose.SignJWT(claims).setProtectedHeader({
|
|
2287
|
+
typ: pluginAuthNode.tokenTypes.user.typParam,
|
|
2288
|
+
alg: key.alg,
|
|
2289
|
+
kid: key.kid
|
|
2290
|
+
}).sign(signingKey);
|
|
2245
2291
|
if (token.length > MAX_TOKEN_LENGTH) {
|
|
2246
2292
|
throw new Error(
|
|
2247
2293
|
`Failed to issue a new user token. The resulting token is excessively large, with either too many ownership claims or too large custom claims. You likely have a bug either in the sign-in resolver or catalog data. The following claims were requested: '${JSON.stringify(
|
|
@@ -2308,6 +2354,26 @@ class TokenFactory {
|
|
|
2308
2354
|
}
|
|
2309
2355
|
return promise;
|
|
2310
2356
|
}
|
|
2357
|
+
// Creates a string claim that can be used as part of reconstructing a limited
|
|
2358
|
+
// user token. The output of this function is only the signature part of a
|
|
2359
|
+
// JWS.
|
|
2360
|
+
async createUserIdentityClaim(options) {
|
|
2361
|
+
const header = {
|
|
2362
|
+
typ: options.header.typ,
|
|
2363
|
+
alg: options.header.alg,
|
|
2364
|
+
...options.header.kid ? { kid: options.header.kid } : {}
|
|
2365
|
+
};
|
|
2366
|
+
const payload = {
|
|
2367
|
+
sub: options.payload.sub,
|
|
2368
|
+
ent: options.payload.ent,
|
|
2369
|
+
iat: options.payload.iat,
|
|
2370
|
+
exp: options.payload.exp
|
|
2371
|
+
};
|
|
2372
|
+
const jws = await new jose.GeneralSign(
|
|
2373
|
+
new TextEncoder().encode(JSON.stringify(payload))
|
|
2374
|
+
).addSignature(options.key).setProtectedHeader(header).done().sign();
|
|
2375
|
+
return jws.signatures[0].signature;
|
|
2376
|
+
}
|
|
2311
2377
|
}
|
|
2312
2378
|
|
|
2313
2379
|
const TABLE = "signing_keys";
|
|
@@ -2807,6 +2873,7 @@ async function createRouter(options) {
|
|
|
2807
2873
|
httpAuth
|
|
2808
2874
|
});
|
|
2809
2875
|
bindOidcRouter(router, {
|
|
2876
|
+
auth,
|
|
2810
2877
|
tokenIssuer,
|
|
2811
2878
|
baseUrl: authUrl
|
|
2812
2879
|
});
|
|
@@ -2839,6 +2906,8 @@ const authPlugin = backendPluginApi.createBackendPlugin({
|
|
|
2839
2906
|
database: backendPluginApi.coreServices.database,
|
|
2840
2907
|
discovery: backendPluginApi.coreServices.discovery,
|
|
2841
2908
|
tokenManager: backendPluginApi.coreServices.tokenManager,
|
|
2909
|
+
auth: backendPluginApi.coreServices.auth,
|
|
2910
|
+
httpAuth: backendPluginApi.coreServices.httpAuth,
|
|
2842
2911
|
catalogApi: alpha.catalogServiceRef
|
|
2843
2912
|
},
|
|
2844
2913
|
async init({
|
|
@@ -2848,6 +2917,8 @@ const authPlugin = backendPluginApi.createBackendPlugin({
|
|
|
2848
2917
|
database,
|
|
2849
2918
|
discovery,
|
|
2850
2919
|
tokenManager,
|
|
2920
|
+
auth,
|
|
2921
|
+
httpAuth,
|
|
2851
2922
|
catalogApi
|
|
2852
2923
|
}) {
|
|
2853
2924
|
const router = await createRouter({
|
|
@@ -2856,6 +2927,8 @@ const authPlugin = backendPluginApi.createBackendPlugin({
|
|
|
2856
2927
|
database,
|
|
2857
2928
|
discovery,
|
|
2858
2929
|
tokenManager,
|
|
2930
|
+
auth,
|
|
2931
|
+
httpAuth,
|
|
2859
2932
|
catalogApi,
|
|
2860
2933
|
providerFactories: Object.fromEntries(providers),
|
|
2861
2934
|
disableDefaultProviderFactories: true
|