diff --git a/.changeset/cold-boats-sell.md b/.changeset/cold-boats-sell.md index 112d9022bc..e47517140d 100644 --- a/.changeset/cold-boats-sell.md +++ b/.changeset/cold-boats-sell.md @@ -2,4 +2,9 @@ '@backstage/plugin-auth-backend-module-guest-provider': patch --- -Adds a new guest provider that maps guest users to actual tokens. +Adds a new guest provider that maps guest users to actual tokens. This also shifts the default guest login to `user:development/guest` to reduce overlap with your production/real data. To change that (or set it back to the old default, use the new `auth.guestEntityRef` config key) like so, + +```yaml title=app-config.yaml +auth: + guestEntityRef: user:default/guest +``` diff --git a/.changeset/selfish-glasses-cheer.md b/.changeset/selfish-glasses-cheer.md index ffd9bb74e0..4d880f0523 100644 --- a/.changeset/selfish-glasses-cheer.md +++ b/.changeset/selfish-glasses-cheer.md @@ -2,12 +2,4 @@ '@backstage/core-components': minor --- -**BREAKING** `SignInPage`'s `'guest'` provider now uses `@backstage/plugin-auth-backend-module-guest-provider` to generate tokens. You must install that provider into your backend to continue using the `'guest'` option. - -```diff -const backend = createBackend(); - -+backend.add(import('@backstage/plugin-auth-backend-module-guest-provider')); - -backend.start(); -``` +`SignInPage`'s `'guest'` provider now supports the `@backstage/plugin-auth-backend-module-guest-provider` package to generate tokens. It will continue to use the old frontend-only auth as a fallback. diff --git a/app-config.yaml b/app-config.yaml index e18c45aefa..4b92687653 100644 --- a/app-config.yaml +++ b/app-config.yaml @@ -242,7 +242,7 @@ catalog: - Domain - Location providers: - openapi: + backstageOpenapi: plugins: - catalog - search diff --git a/docs/auth/guest/provider.md b/docs/auth/guest/provider.md index 09c52fcd3b..ca5bee1d09 100644 --- a/docs/auth/guest/provider.md +++ b/docs/auth/guest/provider.md @@ -59,7 +59,9 @@ Similar to the other authentication providers, you have to enable the provider i auth: providers: + guest: -+ development: {} ++ development: + // new optional property to override the default value. ++ loginAs: user:default/guest ``` We need to specify that the provider is enabled for the given environment, and as there are no config values for this provider yet, you can just specify an empty object. diff --git a/packages/catalog-model/examples/acme/team-a-group.yaml b/packages/catalog-model/examples/acme/team-a-group.yaml index 7fe0e7b3f3..eb95c47093 100644 --- a/packages/catalog-model/examples/acme/team-a-group.yaml +++ b/packages/catalog-model/examples/acme/team-a-group.yaml @@ -57,3 +57,17 @@ spec: displayName: Guest User email: guest@example.com memberOf: [team-a] +--- +# This user is added as an example, to make it more easy for the "Guest" +# sign-in option to demonstrate some entities being owned. In a regular org, +# a guest user would probably not be registered like this. +apiVersion: backstage.io/v1alpha1 +kind: User +metadata: + name: guest + namespace: development +spec: + profile: + displayName: Guest User + email: guest@example.com + memberOf: [group:default/team-a] diff --git a/packages/create-app/templates/default-app/examples/org.yaml b/packages/create-app/templates/default-app/examples/org.yaml index a10e81fc7f..1c4fb91a1e 100644 --- a/packages/create-app/templates/default-app/examples/org.yaml +++ b/packages/create-app/templates/default-app/examples/org.yaml @@ -7,6 +7,15 @@ metadata: spec: memberOf: [guests] --- +# https://backstage.io/docs/features/software-catalog/descriptor-format#kind-user +apiVersion: backstage.io/v1alpha1 +kind: User +metadata: + name: guest + namespace: development +spec: + memberOf: [guests] +--- # https://backstage.io/docs/features/software-catalog/descriptor-format#kind-group apiVersion: backstage.io/v1alpha1 kind: Group diff --git a/plugins/auth-backend-module-guest-provider/config.d.ts b/plugins/auth-backend-module-guest-provider/config.d.ts new file mode 100644 index 0000000000..d6de29bed6 --- /dev/null +++ b/plugins/auth-backend-module-guest-provider/config.d.ts @@ -0,0 +1,27 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export interface Config { + /** Configuration options for the auth plugin */ + auth?: { + /** + * EXPERIMENTAL value: Allow users to configure what the guest provider logs in as. + * @visibility frontend + * @default user:default/guest + */ + guestEntityRef?: string; + }; +} diff --git a/plugins/auth-backend-module-guest-provider/src/module.ts b/plugins/auth-backend-module-guest-provider/src/module.ts index a52b8a4ac2..75eb4f849c 100644 --- a/plugins/auth-backend-module-guest-provider/src/module.ts +++ b/plugins/auth-backend-module-guest-provider/src/module.ts @@ -33,18 +33,21 @@ export const authModuleGuestProvider = createBackendModule({ deps: { logger: coreServices.logger, providers: authProvidersExtensionPoint, + config: coreServices.rootConfig, }, - async init({ providers }) { + async init({ providers, logger, config }) { if (process.env.NODE_ENV !== 'development') { - throw new Error( - 'Guest provider does not support authenticating production workloads.', + logger.warn( + 'You should NOT be using the guest provider outside of a development environment.', ); } providers.registerProvider({ providerId: 'guest', factory: createProxyAuthProviderFactory({ authenticator: guestAuthenticator, - signInResolver: signInAsGuestUser, + signInResolver: signInAsGuestUser( + config.getOptionalString('auth.guestEntityRef'), + ), }), }); }, diff --git a/plugins/auth-backend-module-guest-provider/src/resolvers.ts b/plugins/auth-backend-module-guest-provider/src/resolvers.ts index 05acf676ec..b2c2f8cf7d 100644 --- a/plugins/auth-backend-module-guest-provider/src/resolvers.ts +++ b/plugins/auth-backend-module-guest-provider/src/resolvers.ts @@ -19,24 +19,28 @@ import { SignInResolver } from '@backstage/plugin-auth-node'; /** * Provide a default implementation of the user to resolve to. By default, this - * is `user:default/guest`. We will attempt to get that user if they're in the + * is `user:development/guest`. We will attempt to get that user if they're in the * catalog. If that user doesn't exist in the catalog, we will still create a * token for them so they can keep viewing. */ -export const signInAsGuestUser: SignInResolver<{}> = async (_, ctx) => { - const userRef = stringifyEntityRef({ - kind: 'user', - name: 'guest', - }); - try { - return ctx.signInWithCatalogUser({ entityRef: userRef }); - } catch (err) { - // We can't guarantee that a guest user exists in the catalog, so we issue a token directly, - return ctx.issueToken({ - claims: { - sub: userRef, - ent: [userRef], - }, - }); - } -}; +export const signInAsGuestUser: (entityRef?: string) => SignInResolver<{}> = + (entityRef?: string) => async (_, ctx) => { + const userRef = + entityRef ?? + stringifyEntityRef({ + kind: 'user', + namespace: 'development', + name: 'guest', + }); + try { + return ctx.signInWithCatalogUser({ entityRef: userRef }); + } catch (err) { + // We can't guarantee that a guest user exists in the catalog, so we issue a token directly, + return ctx.issueToken({ + claims: { + sub: userRef, + ent: [userRef], + }, + }); + } + };