@backstage/plugin-auth-backend 0.9.0-next.0 → 0.9.0-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 +9 -0
- package/dist/index.cjs.js +74 -48
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +4 -2
- package/package.json +5 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# @backstage/plugin-auth-backend
|
|
2
2
|
|
|
3
|
+
## 0.9.0-next.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 9d75a939b6: Fixed a bug where providers that tracked the granted scopes through a cookie would not take failed authentication attempts into account.
|
|
8
|
+
- 648606b3ac: Added support for storing static GitHub access tokens in cookies and using them to refresh the Backstage session.
|
|
9
|
+
- Updated dependencies
|
|
10
|
+
- @backstage/backend-common@0.10.6-next.0
|
|
11
|
+
|
|
3
12
|
## 0.9.0-next.0
|
|
4
13
|
|
|
5
14
|
### Minor Changes
|
package/dist/index.cjs.js
CHANGED
|
@@ -281,46 +281,38 @@ class OAuthAdapter {
|
|
|
281
281
|
this.setNonceCookie = (res, nonce) => {
|
|
282
282
|
res.cookie(`${this.options.providerId}-nonce`, nonce, {
|
|
283
283
|
maxAge: TEN_MINUTES_MS,
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
domain: this.options.cookieDomain,
|
|
287
|
-
path: `${this.options.cookiePath}/handler`,
|
|
288
|
-
httpOnly: true
|
|
284
|
+
...this.baseCookieOptions,
|
|
285
|
+
path: `${this.options.cookiePath}/handler`
|
|
289
286
|
});
|
|
290
287
|
};
|
|
291
|
-
this.
|
|
292
|
-
res.cookie(`${this.options.providerId}-scope`, scope, {
|
|
293
|
-
maxAge:
|
|
294
|
-
|
|
295
|
-
sameSite: "lax",
|
|
296
|
-
domain: this.options.cookieDomain,
|
|
297
|
-
path: `${this.options.cookiePath}/handler`,
|
|
298
|
-
httpOnly: true
|
|
288
|
+
this.setGrantedScopeCookie = (res, scope) => {
|
|
289
|
+
res.cookie(`${this.options.providerId}-granted-scope`, scope, {
|
|
290
|
+
maxAge: THOUSAND_DAYS_MS,
|
|
291
|
+
...this.baseCookieOptions
|
|
299
292
|
});
|
|
300
293
|
};
|
|
301
|
-
this.
|
|
302
|
-
return req.cookies[`${providerId}-scope`];
|
|
294
|
+
this.getGrantedScopeFromCookie = (req) => {
|
|
295
|
+
return req.cookies[`${this.options.providerId}-granted-scope`];
|
|
303
296
|
};
|
|
304
297
|
this.setRefreshTokenCookie = (res, refreshToken) => {
|
|
305
298
|
res.cookie(`${this.options.providerId}-refresh-token`, refreshToken, {
|
|
306
299
|
maxAge: THOUSAND_DAYS_MS,
|
|
307
|
-
|
|
308
|
-
sameSite: "lax",
|
|
309
|
-
domain: this.options.cookieDomain,
|
|
310
|
-
path: this.options.cookiePath,
|
|
311
|
-
httpOnly: true
|
|
300
|
+
...this.baseCookieOptions
|
|
312
301
|
});
|
|
313
302
|
};
|
|
314
303
|
this.removeRefreshTokenCookie = (res) => {
|
|
315
304
|
res.cookie(`${this.options.providerId}-refresh-token`, "", {
|
|
316
305
|
maxAge: 0,
|
|
317
|
-
|
|
318
|
-
sameSite: "lax",
|
|
319
|
-
domain: this.options.cookieDomain,
|
|
320
|
-
path: this.options.cookiePath,
|
|
321
|
-
httpOnly: true
|
|
306
|
+
...this.baseCookieOptions
|
|
322
307
|
});
|
|
323
308
|
};
|
|
309
|
+
this.baseCookieOptions = {
|
|
310
|
+
httpOnly: true,
|
|
311
|
+
sameSite: "lax",
|
|
312
|
+
secure: this.options.secure,
|
|
313
|
+
path: this.options.cookiePath,
|
|
314
|
+
domain: this.options.cookieDomain
|
|
315
|
+
};
|
|
324
316
|
}
|
|
325
317
|
static fromConfig(config, handlers, options) {
|
|
326
318
|
var _a;
|
|
@@ -344,12 +336,12 @@ class OAuthAdapter {
|
|
|
344
336
|
if (!env) {
|
|
345
337
|
throw new errors.InputError("No env provided in request query parameters");
|
|
346
338
|
}
|
|
347
|
-
if (this.options.persistScopes) {
|
|
348
|
-
this.setScopesCookie(res, scope);
|
|
349
|
-
}
|
|
350
339
|
const nonce = crypto__default["default"].randomBytes(16).toString("base64");
|
|
351
340
|
this.setNonceCookie(res, nonce);
|
|
352
341
|
const state = { nonce, env, origin };
|
|
342
|
+
if (this.options.persistScopes) {
|
|
343
|
+
state.scope = scope;
|
|
344
|
+
}
|
|
353
345
|
const forwardReq = Object.assign(req, { scope, state });
|
|
354
346
|
const { url, status } = await this.handlers.start(forwardReq);
|
|
355
347
|
res.statusCode = status || 302;
|
|
@@ -374,9 +366,9 @@ class OAuthAdapter {
|
|
|
374
366
|
}
|
|
375
367
|
verifyNonce(req, this.options.providerId);
|
|
376
368
|
const { response, refreshToken } = await this.handlers.handler(req);
|
|
377
|
-
if (this.options.persistScopes) {
|
|
378
|
-
|
|
379
|
-
response.providerInfo.scope =
|
|
369
|
+
if (this.options.persistScopes && state.scope) {
|
|
370
|
+
this.setGrantedScopeCookie(res, state.scope);
|
|
371
|
+
response.providerInfo.scope = state.scope;
|
|
380
372
|
}
|
|
381
373
|
if (refreshToken && !this.options.disableRefresh) {
|
|
382
374
|
this.setRefreshTokenCookie(res, refreshToken);
|
|
@@ -414,7 +406,10 @@ class OAuthAdapter {
|
|
|
414
406
|
if (!refreshToken) {
|
|
415
407
|
throw new errors.InputError("Missing session cookie");
|
|
416
408
|
}
|
|
417
|
-
|
|
409
|
+
let scope = (_b = (_a = req.query.scope) == null ? void 0 : _a.toString()) != null ? _b : "";
|
|
410
|
+
if (this.options.persistScopes) {
|
|
411
|
+
scope = this.getGrantedScopeFromCookie(req);
|
|
412
|
+
}
|
|
418
413
|
const forwardReq = Object.assign(req, { scope, refreshToken });
|
|
419
414
|
const { response, refreshToken: newRefreshToken } = await this.handlers.refresh(forwardReq);
|
|
420
415
|
const backstageIdentity = await this.populateIdentity(response.backstageIdentity);
|
|
@@ -1154,6 +1149,8 @@ const createBitbucketProvider = (options) => {
|
|
|
1154
1149
|
});
|
|
1155
1150
|
};
|
|
1156
1151
|
|
|
1152
|
+
const ACCESS_TOKEN_PREFIX = "access-token.";
|
|
1153
|
+
const BACKSTAGE_SESSION_EXPIRATION = 3600;
|
|
1157
1154
|
class GithubAuthProvider {
|
|
1158
1155
|
constructor(options) {
|
|
1159
1156
|
this.signInResolver = options.signInResolver;
|
|
@@ -1181,21 +1178,43 @@ class GithubAuthProvider {
|
|
|
1181
1178
|
}
|
|
1182
1179
|
async handler(req) {
|
|
1183
1180
|
const { result, privateInfo } = await executeFrameHandlerStrategy(req, this._strategy);
|
|
1181
|
+
let refreshToken = privateInfo.refreshToken;
|
|
1182
|
+
if (!refreshToken && !result.params.expires_in) {
|
|
1183
|
+
refreshToken = ACCESS_TOKEN_PREFIX + result.accessToken;
|
|
1184
|
+
}
|
|
1184
1185
|
return {
|
|
1185
1186
|
response: await this.handleResult(result),
|
|
1186
|
-
refreshToken
|
|
1187
|
+
refreshToken
|
|
1187
1188
|
};
|
|
1188
1189
|
}
|
|
1189
1190
|
async refresh(req) {
|
|
1190
|
-
const {
|
|
1191
|
-
|
|
1191
|
+
const { scope, refreshToken } = req;
|
|
1192
|
+
if (refreshToken == null ? void 0 : refreshToken.startsWith(ACCESS_TOKEN_PREFIX)) {
|
|
1193
|
+
const accessToken = refreshToken.slice(ACCESS_TOKEN_PREFIX.length);
|
|
1194
|
+
const fullProfile = await executeFetchUserProfileStrategy(this._strategy, accessToken).catch((error) => {
|
|
1195
|
+
var _a;
|
|
1196
|
+
if (((_a = error.oauthError) == null ? void 0 : _a.statusCode) === 401) {
|
|
1197
|
+
throw new Error("Invalid access token");
|
|
1198
|
+
}
|
|
1199
|
+
throw error;
|
|
1200
|
+
});
|
|
1201
|
+
return {
|
|
1202
|
+
response: await this.handleResult({
|
|
1203
|
+
fullProfile,
|
|
1204
|
+
params: { scope },
|
|
1205
|
+
accessToken
|
|
1206
|
+
}),
|
|
1207
|
+
refreshToken
|
|
1208
|
+
};
|
|
1209
|
+
}
|
|
1210
|
+
const result = await executeRefreshTokenStrategy(this._strategy, refreshToken, scope);
|
|
1192
1211
|
return {
|
|
1193
1212
|
response: await this.handleResult({
|
|
1194
|
-
fullProfile,
|
|
1195
|
-
params,
|
|
1196
|
-
accessToken
|
|
1213
|
+
fullProfile: await executeFetchUserProfileStrategy(this._strategy, result.accessToken),
|
|
1214
|
+
params: { ...result.params, scope },
|
|
1215
|
+
accessToken: result.accessToken
|
|
1197
1216
|
}),
|
|
1198
|
-
refreshToken
|
|
1217
|
+
refreshToken: result.refreshToken
|
|
1199
1218
|
};
|
|
1200
1219
|
}
|
|
1201
1220
|
async handleResult(result) {
|
|
@@ -1206,21 +1225,28 @@ class GithubAuthProvider {
|
|
|
1206
1225
|
};
|
|
1207
1226
|
const { profile } = await this.authHandler(result, context);
|
|
1208
1227
|
const expiresInStr = result.params.expires_in;
|
|
1209
|
-
|
|
1228
|
+
let expiresInSeconds = expiresInStr === void 0 ? void 0 : Number(expiresInStr);
|
|
1229
|
+
let backstageIdentity = void 0;
|
|
1230
|
+
if (this.signInResolver) {
|
|
1231
|
+
backstageIdentity = await this.signInResolver({
|
|
1232
|
+
result,
|
|
1233
|
+
profile
|
|
1234
|
+
}, context);
|
|
1235
|
+
if (expiresInSeconds) {
|
|
1236
|
+
expiresInSeconds = Math.min(expiresInSeconds, BACKSTAGE_SESSION_EXPIRATION);
|
|
1237
|
+
} else {
|
|
1238
|
+
expiresInSeconds = BACKSTAGE_SESSION_EXPIRATION;
|
|
1239
|
+
}
|
|
1240
|
+
}
|
|
1241
|
+
return {
|
|
1242
|
+
backstageIdentity,
|
|
1210
1243
|
providerInfo: {
|
|
1211
1244
|
accessToken: result.accessToken,
|
|
1212
1245
|
scope: result.params.scope,
|
|
1213
|
-
expiresInSeconds
|
|
1246
|
+
expiresInSeconds
|
|
1214
1247
|
},
|
|
1215
1248
|
profile
|
|
1216
1249
|
};
|
|
1217
|
-
if (this.signInResolver) {
|
|
1218
|
-
response.backstageIdentity = await this.signInResolver({
|
|
1219
|
-
result,
|
|
1220
|
-
profile
|
|
1221
|
-
}, context);
|
|
1222
|
-
}
|
|
1223
|
-
return response;
|
|
1224
1250
|
}
|
|
1225
1251
|
}
|
|
1226
1252
|
const githubDefaultSignInResolver = async (info, ctx) => {
|