@backstage/plugin-auth-backend 0.4.5 → 0.4.9

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 CHANGED
@@ -1,5 +1,63 @@
1
1
  # @backstage/plugin-auth-backend
2
2
 
3
+ ## 0.4.9
4
+
5
+ ### Patch Changes
6
+
7
+ - 9312572360: Switched to using the standardized JSON error responses for all provider endpoints.
8
+ - bab752e2b3: Change default port of backend from 7000 to 7007.
9
+
10
+ This is due to the AirPlay Receiver process occupying port 7000 and preventing local Backstage instances on MacOS to start.
11
+
12
+ You can change the port back to 7000 or any other value by providing an `app-config.yaml` with the following values:
13
+
14
+ ```
15
+ backend:
16
+ listen: 0.0.0.0:7123
17
+ baseUrl: http://localhost:7123
18
+ ```
19
+
20
+ More information can be found here: https://backstage.io/docs/conf/writing
21
+
22
+ - Updated dependencies
23
+ - @backstage/errors@0.1.5
24
+ - @backstage/backend-common@0.9.11
25
+ - @backstage/test-utils@0.1.23
26
+
27
+ ## 0.4.8
28
+
29
+ ### Patch Changes
30
+
31
+ - 892c1d9202: Update OAuthAdapter to create identity.token from identity.idToken if it does not exist, and prevent overwrites to identity.toke. Update login page commonProvider to prefer .token over .idToken
32
+ - Updated dependencies
33
+ - @backstage/catalog-client@0.5.2
34
+ - @backstage/catalog-model@0.9.7
35
+ - @backstage/backend-common@0.9.10
36
+ - @backstage/test-utils@0.1.22
37
+
38
+ ## 0.4.7
39
+
40
+ ### Patch Changes
41
+
42
+ - 5ee31f860b: Only use settings that have a value when creating a new FirestoreKeyStore instance
43
+ - 3e0e2f09d5: Added forwarding of the `audience` option for the SAML provider, making it possible to enable `audience` verification.
44
+ - Updated dependencies
45
+ - @backstage/backend-common@0.9.9
46
+ - @backstage/test-utils@0.1.21
47
+ - @backstage/catalog-client@0.5.1
48
+
49
+ ## 0.4.6
50
+
51
+ ### Patch Changes
52
+
53
+ - 3b767f19c9: Allow OAuth state to be encoded by a stateEncoder.
54
+ - Updated dependencies
55
+ - @backstage/test-utils@0.1.20
56
+ - @backstage/config@0.1.11
57
+ - @backstage/errors@0.1.4
58
+ - @backstage/backend-common@0.9.8
59
+ - @backstage/catalog-model@0.9.6
60
+
3
61
  ## 0.4.5
4
62
 
5
63
  ### Patch Changes
package/README.md CHANGED
@@ -34,7 +34,7 @@ Follow this link, [Create new OAuth App](https://github.com/settings/application
34
34
  1. Set Application Name to `backstage-dev` or something along those lines.
35
35
  1. You can set the Homepage URL to whatever you want to.
36
36
  1. The Authorization Callback URL should match the redirect URI set in Backstage.
37
- 1. Set this to `http://localhost:7000/api/auth/github` for local development.
37
+ 1. Set this to `http://localhost:7007/api/auth/github` for local development.
38
38
  1. Set this to `http://{APP_FQDN}:{APP_BACKEND_PORT}/api/auth/github` for non-local deployments.
39
39
 
40
40
  ```bash
@@ -58,7 +58,7 @@ Follow this link, [Add new application](https://gitlab.com/-/profile/application
58
58
 
59
59
  1. Set Application Name to `backstage-dev` or something along those lines.
60
60
  1. The Authorization Callback URL should match the redirect URI set in Backstage.
61
- 1. Set this to `http://localhost:7000/api/auth/gitlab/handler/frame` for local development.
61
+ 1. Set this to `http://localhost:7007/api/auth/gitlab/handler/frame` for local development.
62
62
  1. Set this to `http://{APP_FQDN}:{APP_BACKEND_PORT}/api/auth/gitlab/handler/frame` for non-local deployments.
63
63
  1. Select the following scopes from the list:
64
64
  - [x] `read_user` Grants read-only access to the authenticated user's profile through the /user API endpoint, which includes username, public email, and full name. Also grants access to read-only API endpoints under /users.
@@ -91,9 +91,9 @@ export AUTH_GITLAB_CLIENT_SECRET=x
91
91
 
92
92
  Add a new Okta application using the following URI conventions:
93
93
 
94
- Login redirect URI's: `http://localhost:7000/api/auth/okta/handler/frame`
95
- Logout redirect URI's: `http://localhost:7000/api/auth/okta/logout`
96
- Initiate login URI's: `http://localhost:7000/api/auth/okta/start`
94
+ Login redirect URI's: `http://localhost:7007/api/auth/okta/handler/frame`
95
+ Logout redirect URI's: `http://localhost:7007/api/auth/okta/logout`
96
+ Initiate login URI's: `http://localhost:7007/api/auth/okta/start`
97
97
 
98
98
  Then configure the following environment variables to be used in the `app-config.yaml` file:
99
99
 
@@ -122,7 +122,7 @@ Click [here](https://portal.azure.com/#blade/Microsoft_AAD_IAM/ActiveDirectoryMe
122
122
  - Give the app a name. e.g. `backstage-dev`
123
123
  - Select `Accounts in this organizational directory only` under supported account types.
124
124
  - Enter the callback URL for your backstage backend instance:
125
- - For local development, this is likely `http://localhost:7000/api/auth/microsoft/handler/frame`
125
+ - For local development, this is likely `http://localhost:7007/api/auth/microsoft/handler/frame`
126
126
  - For non-local deployments, this will be `https://{APP_FQDN}:{APP_BACKEND_PORT}/auth/microsoft/handler/frame`
127
127
  - Click `Register`.
128
128
 
package/config.d.ts CHANGED
@@ -75,6 +75,7 @@ export interface Config {
75
75
  logoutUrl?: string;
76
76
  issuer: string;
77
77
  cert: string;
78
+ audience?: string;
78
79
  privateKey?: string;
79
80
  authnContext?: string[];
80
81
  identifierFormat?: string;
package/dist/index.cjs.js CHANGED
@@ -30,6 +30,7 @@ var uuid = require('uuid');
30
30
  var luxon = require('luxon');
31
31
  var backendCommon = require('@backstage/backend-common');
32
32
  var firestore = require('@google-cloud/firestore');
33
+ var lodash = require('lodash');
33
34
  var session = require('express-session');
34
35
  var passport = require('passport');
35
36
  var minimatch = require('minimatch');
@@ -226,22 +227,22 @@ class OAuthEnvironmentHandler {
226
227
  return new OAuthEnvironmentHandler(handlers);
227
228
  }
228
229
  async start(req, res) {
229
- const provider = this.getProviderForEnv(req, res);
230
- await (provider == null ? void 0 : provider.start(req, res));
230
+ const provider = this.getProviderForEnv(req);
231
+ await provider.start(req, res);
231
232
  }
232
233
  async frameHandler(req, res) {
233
- const provider = this.getProviderForEnv(req, res);
234
- await (provider == null ? void 0 : provider.frameHandler(req, res));
234
+ const provider = this.getProviderForEnv(req);
235
+ await provider.frameHandler(req, res);
235
236
  }
236
237
  async refresh(req, res) {
237
238
  var _a;
238
- const provider = this.getProviderForEnv(req, res);
239
- await ((_a = provider == null ? void 0 : provider.refresh) == null ? void 0 : _a.call(provider, req, res));
239
+ const provider = this.getProviderForEnv(req);
240
+ await ((_a = provider.refresh) == null ? void 0 : _a.call(provider, req, res));
240
241
  }
241
242
  async logout(req, res) {
242
243
  var _a;
243
- const provider = this.getProviderForEnv(req, res);
244
- await ((_a = provider == null ? void 0 : provider.logout) == null ? void 0 : _a.call(provider, req, res));
244
+ const provider = this.getProviderForEnv(req);
245
+ await ((_a = provider.logout) == null ? void 0 : _a.call(provider, req, res));
245
246
  }
246
247
  getRequestFromEnv(req) {
247
248
  var _a, _b;
@@ -256,19 +257,16 @@ class OAuthEnvironmentHandler {
256
257
  const env = readState(stateParams).env;
257
258
  return env;
258
259
  }
259
- getProviderForEnv(req, res) {
260
+ getProviderForEnv(req) {
260
261
  const env = this.getRequestFromEnv(req);
261
262
  if (!env) {
262
263
  throw new errors.InputError(`Must specify 'env' query to select environment`);
263
264
  }
264
- if (!this.handlers.has(env)) {
265
- res.status(404).send(`Missing configuration.
266
- <br>
267
- <br>
268
- For this flow to work you need to supply a valid configuration for the "${env}" environment of provider.`);
269
- return void 0;
265
+ const handler = this.handlers.get(env);
266
+ if (!handler) {
267
+ throw new errors.NotFoundError(`No configuration available for the '${env}' environment of this provider.`);
270
268
  }
271
- return this.handlers.get(env);
269
+ return handler;
272
270
  }
273
271
  }
274
272
 
@@ -427,26 +425,23 @@ class OAuthAdapter {
427
425
  }
428
426
  async logout(req, res) {
429
427
  if (!ensuresXRequestedWith(req)) {
430
- res.status(401).send("Invalid X-Requested-With header");
431
- return;
428
+ throw new errors.AuthenticationError("Invalid X-Requested-With header");
432
429
  }
433
430
  this.removeRefreshTokenCookie(res);
434
- res.status(200).send("logout!");
431
+ res.status(200).end();
435
432
  }
436
433
  async refresh(req, res) {
437
434
  var _a, _b;
438
435
  if (!ensuresXRequestedWith(req)) {
439
- res.status(401).send("Invalid X-Requested-With header");
440
- return;
436
+ throw new errors.AuthenticationError("Invalid X-Requested-With header");
441
437
  }
442
438
  if (!this.handlers.refresh || this.options.disableRefresh) {
443
- res.status(400).send(`Refresh token not supported for provider: ${this.options.providerId}`);
444
- return;
439
+ throw new errors.InputError(`Refresh token is not supported for provider ${this.options.providerId}`);
445
440
  }
446
441
  try {
447
442
  const refreshToken = req.cookies[`${this.options.providerId}-refresh-token`];
448
443
  if (!refreshToken) {
449
- throw new Error("Missing session cookie");
444
+ throw new errors.InputError("Missing session cookie");
450
445
  }
451
446
  const scope = (_b = (_a = req.query.scope) == null ? void 0 : _a.toString()) != null ? _b : "";
452
447
  const forwardReq = Object.assign(req, {scope, refreshToken});
@@ -457,17 +452,19 @@ class OAuthAdapter {
457
452
  }
458
453
  res.status(200).json(response);
459
454
  } catch (error) {
460
- res.status(401).send(String(error));
455
+ throw new errors.AuthenticationError("Refresh failed", error);
461
456
  }
462
457
  }
463
458
  async populateIdentity(identity) {
464
459
  if (!identity) {
465
460
  return;
466
461
  }
467
- if (!identity.idToken) {
468
- identity.idToken = await this.options.tokenIssuer.issueToken({
462
+ if (!(identity.token || identity.idToken)) {
463
+ identity.token = await this.options.tokenIssuer.issueToken({
469
464
  claims: {sub: identity.id}
470
465
  });
466
+ } else if (!identity.token && identity.idToken) {
467
+ identity.token = identity.idToken;
471
468
  }
472
469
  }
473
470
  }
@@ -550,6 +547,7 @@ class GithubAuthProvider {
550
547
  constructor(options) {
551
548
  this.signInResolver = options.signInResolver;
552
549
  this.authHandler = options.authHandler;
550
+ this.stateEncoder = options.stateEncoder;
553
551
  this.tokenIssuer = options.tokenIssuer;
554
552
  this.catalogIdentityClient = options.catalogIdentityClient;
555
553
  this.logger = options.logger;
@@ -567,7 +565,7 @@ class GithubAuthProvider {
567
565
  async start(req) {
568
566
  return await executeRedirectStrategy(req, this._strategy, {
569
567
  scope: req.scope,
570
- state: encodeState(req.state)
568
+ state: (await this.stateEncoder(req)).encodedState
571
569
  });
572
570
  }
573
571
  async handler(req) {
@@ -633,7 +631,7 @@ const createGithubProvider = (options) => {
633
631
  catalogApi,
634
632
  logger
635
633
  }) => OAuthEnvironmentHandler.mapConfig(config, (envConfig) => {
636
- var _a, _b;
634
+ var _a, _b, _c;
637
635
  const clientId = envConfig.getString("clientId");
638
636
  const clientSecret = envConfig.getString("clientSecret");
639
637
  const enterpriseInstanceUrl = envConfig.getOptionalString("enterpriseInstanceUrl");
@@ -655,6 +653,9 @@ const createGithubProvider = (options) => {
655
653
  tokenIssuer,
656
654
  logger
657
655
  });
656
+ const stateEncoder = (_c = options == null ? void 0 : options.stateEncoder) != null ? _c : async (req) => {
657
+ return {encodedState: encodeState(req.state)};
658
+ };
658
659
  const provider = new GithubAuthProvider({
659
660
  clientId,
660
661
  clientSecret,
@@ -666,6 +667,7 @@ const createGithubProvider = (options) => {
666
667
  authHandler,
667
668
  tokenIssuer,
668
669
  catalogIdentityClient,
670
+ stateEncoder,
669
671
  logger
670
672
  });
671
673
  return OAuthAdapter.fromConfig(globalConfig, provider, {
@@ -2028,6 +2030,7 @@ const createSamlProvider = (_options) => {
2028
2030
  callbackUrl: `${globalConfig.baseUrl}/${providerId}/handler/frame`,
2029
2031
  entryPoint: config.getString("entryPoint"),
2030
2032
  logoutUrl: config.getOptionalString("logoutUrl"),
2033
+ audience: config.getOptionalString("audience"),
2031
2034
  issuer: config.getString("issuer"),
2032
2035
  cert: config.getString("cert"),
2033
2036
  privateCert: config.getOptionalString("privateKey"),
@@ -2560,7 +2563,7 @@ class KeyStores {
2560
2563
  }
2561
2564
  if (provider === "firestore") {
2562
2565
  const settings = ks == null ? void 0 : ks.getConfig(provider);
2563
- const keyStore = await FirestoreKeyStore.create({
2566
+ const keyStore = await FirestoreKeyStore.create(lodash.pickBy({
2564
2567
  projectId: settings == null ? void 0 : settings.getOptionalString("projectId"),
2565
2568
  keyFilename: settings == null ? void 0 : settings.getOptionalString("keyFilename"),
2566
2569
  host: settings == null ? void 0 : settings.getOptionalString("host"),
@@ -2568,7 +2571,7 @@ class KeyStores {
2568
2571
  ssl: settings == null ? void 0 : settings.getOptionalBoolean("ssl"),
2569
2572
  path: settings == null ? void 0 : settings.getOptionalString("path"),
2570
2573
  timeout: settings == null ? void 0 : settings.getOptionalNumber("timeout")
2571
- });
2574
+ }, (value) => value !== void 0));
2572
2575
  await FirestoreKeyStore.verifyConnection(keyStore, logger);
2573
2576
  return keyStore;
2574
2577
  }