diff --git a/.changeset/cold-boats-sell.md b/.changeset/cold-boats-sell.md new file mode 100644 index 0000000000..5b0eba7301 --- /dev/null +++ b/.changeset/cold-boats-sell.md @@ -0,0 +1,23 @@ +--- +'@backstage/plugin-auth-backend-module-guest-provider': minor +--- + +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.providers.guest.userEntityRef` config key) like so, + +```yaml title=app-config.yaml +auth: + providers: + guest: + userEntityRef: user:default/guest +``` + +This also adds a new property to control the ownership entity refs, + +```yaml title=app-config.yaml +auth: + providers: + guest: + ownershipEntityRefs: + - guests + - development/custom +``` diff --git a/.changeset/selfish-glasses-cheer.md b/.changeset/selfish-glasses-cheer.md new file mode 100644 index 0000000000..be267eb0f3 --- /dev/null +++ b/.changeset/selfish-glasses-cheer.md @@ -0,0 +1,5 @@ +--- +'@backstage/core-components': patch +--- + +`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 9b059da216..4b92687653 100644 --- a/app-config.yaml +++ b/app-config.yaml @@ -242,7 +242,7 @@ catalog: - Domain - Location providers: - openapi: + backstageOpenapi: plugins: - catalog - search @@ -399,6 +399,9 @@ auth: scopes: ${AUTH_ATLASSIAN_SCOPES} myproxy: development: {} + guest: + development: {} + costInsights: engineerCost: 200000 engineerThreshold: 0.5 diff --git a/docs/auth/guest/provider.md b/docs/auth/guest/provider.md new file mode 100644 index 0000000000..c730877a47 --- /dev/null +++ b/docs/auth/guest/provider.md @@ -0,0 +1,66 @@ +--- +id: provider +title: Guest Authentication Provider +sidebar_label: Guest +description: Adding a guest authentication provider in Backstage +--- + +Audience: Admins or developers + +## Summary + +The goal of this guide is to get you set up with a guest authentication provider that emits tokens. This is different than the old guest authentication that is purely stored on the frontend and does not have tokens. The main reason you'd want to use this provider is to use permissioned plugins. + +:::caution +This provider should only ever be enabled for `development`. To prevent unauthorized access to your data, this package is _explicitly_ disabled for non-development environments. +::: + +## Installation + +### Backend + +:::note +This will only work with the new backend system. There is no support for this in the old backend. +::: + +Add the `@backstage/plugin-auth-backend-module-guest-provider` to your backend installation. + +``` +yarn --cwd packages/backend add @backstage/plugin-auth-backend-module-guest-provider +``` + +Then, add it to your backend's `index.ts` file, + +```diff +const backend = createBackend(); + +backend.add('@backstage/plugin-auth-backend'); ++backend.add('@backstage/plugin-auth-backend-module-guest-provider'); + +await backend.start(); +``` + +### Frontend + +Add the following to your `SignInPage` providers, + +```diff +const providers = [ ++ 'guest', + ... +] +``` + +### Config + +Similar to the other authentication providers, you have to enable the provider in config. Add the following to your `app-config.local.yaml`, + +```diff +auth: + providers: ++ guest: ++ userEntityRef: user:default/guest ++ development: {} +``` + +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/microsite/sidebars.json b/microsite/sidebars.json index 4f2724c0b6..fd13fcb57b 100644 --- a/microsite/sidebars.json +++ b/microsite/sidebars.json @@ -307,6 +307,7 @@ "auth/gitlab/provider", "auth/google/provider", "auth/google/gcp-iap-auth", + "auth/guest/provider", "auth/okta/provider", "auth/oauth2-proxy/provider", "auth/onelogin/provider", diff --git a/mkdocs.yml b/mkdocs.yml index 2a17613672..a2b2749233 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -161,6 +161,7 @@ nav: - GitLab: 'auth/gitlab/provider.md' - Google: 'auth/google/provider.md' - Google IAP: 'auth/google/gcp-iap-auth.md' + - Guest: 'auth/guest/provider.md' - OAuth2Proxy: 'auth/oauth2-proxy/provider.md' - Okta: 'auth/okta/provider.md' - OneLogin: 'auth/onelogin/provider.md' diff --git a/packages/backend-next/package.json b/packages/backend-next/package.json index 74016539b0..333484051b 100644 --- a/packages/backend-next/package.json +++ b/packages/backend-next/package.json @@ -33,6 +33,7 @@ "@backstage/plugin-app-backend": "workspace:^", "@backstage/plugin-auth-backend": "workspace:^", "@backstage/plugin-auth-backend-module-github-provider": "workspace:^", + "@backstage/plugin-auth-backend-module-guest-provider": "workspace:^", "@backstage/plugin-auth-node": "workspace:^", "@backstage/plugin-azure-devops-backend": "workspace:^", "@backstage/plugin-badges-backend": "workspace:^", diff --git a/packages/backend-next/src/index.ts b/packages/backend-next/src/index.ts index e4dd127208..403b122ddf 100644 --- a/packages/backend-next/src/index.ts +++ b/packages/backend-next/src/index.ts @@ -20,6 +20,7 @@ const backend = createBackend(); backend.add(import('@backstage/plugin-auth-backend')); backend.add(import('./authModuleGithubProvider')); +backend.add(import('@backstage/plugin-auth-backend-module-guest-provider')); backend.add(import('@backstage/plugin-adr-backend')); backend.add(import('@backstage/plugin-app-backend/alpha')); 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/core-components/src/layout/SignInPage/GuestUserIdentity.ts b/packages/core-components/src/layout/SignInPage/GuestUserIdentity.ts index db4731704c..6abb412090 100644 --- a/packages/core-components/src/layout/SignInPage/GuestUserIdentity.ts +++ b/packages/core-components/src/layout/SignInPage/GuestUserIdentity.ts @@ -20,6 +20,9 @@ import { BackstageUserIdentity, } from '@backstage/core-plugin-api'; +/** + * @deprecated Use `@backstage/plugin-auth-backend-module-guest-provider` instead. + */ export class GuestUserIdentity implements IdentityApi { getUserId(): string { return 'guest'; diff --git a/packages/core-components/src/layout/SignInPage/guestProvider.tsx b/packages/core-components/src/layout/SignInPage/guestProvider.tsx index 5393c1ea89..563d1cc124 100644 --- a/packages/core-components/src/layout/SignInPage/guestProvider.tsx +++ b/packages/core-components/src/layout/SignInPage/guestProvider.tsx @@ -20,39 +20,108 @@ import Button from '@material-ui/core/Button'; import { InfoCard } from '../InfoCard/InfoCard'; import { GridItem } from './styles'; import { ProviderComponent, ProviderLoader, SignInProvider } from './types'; +import { ProxiedSignInIdentity } from '../ProxiedSignInPage/ProxiedSignInIdentity'; +import { discoveryApiRef, useApi } from '@backstage/core-plugin-api'; import { GuestUserIdentity } from './GuestUserIdentity'; +import useLocalStorage from 'react-use/lib/useLocalStorage'; +import { ResponseError } from '@backstage/errors'; -const Component: ProviderComponent = ({ onSignInStarted, onSignInSuccess }) => ( - - { - onSignInStarted(); - onSignInSuccess(new GuestUserIdentity()); - }} - > - Enter - +const getIdentity = async (identity: ProxiedSignInIdentity) => { + try { + const identityResponse = await identity.getBackstageIdentity(); + return identityResponse; + } catch (error) { + if ( + error.name === 'ResponseError' && + (error as ResponseError).cause.name === 'NotFoundError' + ) { + return undefined; + } + throw error; + } +}; + +const Component: ProviderComponent = ({ + onSignInStarted, + onSignInSuccess, + onSignInFailure, +}) => { + const discoveryApi = useApi(discoveryApiRef); + const [_, setUseLegacyGuestToken] = useLocalStorage('enableLegacyGuestToken'); + + const handle = async () => { + onSignInStarted(); + + const identity = new ProxiedSignInIdentity({ + provider: 'guest', + discoveryApi, + }); + + const identityResponse = await getIdentity(identity); + + if (!identityResponse) { + // eslint-disable-next-line no-alert + const useLegacyGuestTokenResponse = confirm( + 'Failed to sign in as a guest using the auth backend. Do you want to fallback to the legacy guest token?', + ); + if (useLegacyGuestTokenResponse) { + setUseLegacyGuestToken(true); + onSignInSuccess(new GuestUserIdentity()); + return; } - > - - Enter as a Guest User. -
- You will not have a verified identity, -
- meaning some features might be unavailable. -
-
-
-); + onSignInFailure(); + throw new Error( + `You cannot sign in as a guest, you must either enable the legacy guest token or configure the auth backend to support guest sign in.`, + ); + } -const loader: ProviderLoader = async () => { - return new GuestUserIdentity(); + onSignInSuccess(identity); + }; + + return ( + + + Enter + + } + > + Sign in as a Guest. + + + ); +}; + +const loader: ProviderLoader = async apis => { + const useLegacyGuestToken = + localStorage.getItem('enableLegacyGuestToken') === 'true'; + + const identity = new ProxiedSignInIdentity({ + provider: 'guest', + discoveryApi: apis.get(discoveryApiRef)!, + }); + const identityResponse = await getIdentity(identity); + + if (!identityResponse && !useLegacyGuestToken) { + return undefined; + } else if (identityResponse && useLegacyGuestToken) { + // eslint-disable-next-line no-alert + const switchToNewGuestToken = confirm( + 'You are currently using the legacy guest token, but you have the new guest backend module installed. Do you want to use the new module?', + ); + if (switchToNewGuestToken) { + localStorage.removeItem('enableLegacyGuestToken'); + } else { + return new GuestUserIdentity(); + } + } else if (useLegacyGuestToken) { + return new GuestUserIdentity(); + } + + return identity; }; export const guestProvider: SignInProvider = { Component, loader }; diff --git a/plugins/auth-backend-module-guest-provider/.eslintrc.js b/plugins/auth-backend-module-guest-provider/.eslintrc.js new file mode 100644 index 0000000000..e2a53a6ad2 --- /dev/null +++ b/plugins/auth-backend-module-guest-provider/.eslintrc.js @@ -0,0 +1 @@ +module.exports = require('@backstage/cli/config/eslint-factory')(__dirname); diff --git a/plugins/auth-backend-module-guest-provider/README.md b/plugins/auth-backend-module-guest-provider/README.md new file mode 100644 index 0000000000..79591c987b --- /dev/null +++ b/plugins/auth-backend-module-guest-provider/README.md @@ -0,0 +1,8 @@ +# Auth Module: Guest Provider + +This module provides a guest auth provider implementation for `@backstage/plugin-auth-backend`. This is meant to supersede the existing `'guest'` option for authentication that does not emit tokens and is completely stored as frontend state. + +## Links + +- [Backstage](https://backstage.io) +- [Repository](https://github.com/backstage/backstage/tree/master/plugins/auth-backend-module-guest-provider) diff --git a/plugins/auth-backend-module-guest-provider/api-report.md b/plugins/auth-backend-module-guest-provider/api-report.md new file mode 100644 index 0000000000..773b80b9ba --- /dev/null +++ b/plugins/auth-backend-module-guest-provider/api-report.md @@ -0,0 +1,11 @@ +## API Report File for "@backstage/plugin-auth-backend-module-guest-provider" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts +import { BackendFeature } from '@backstage/backend-plugin-api'; + +// @public (undocumented) +const authModuleGuestProvider: () => BackendFeature; +export default authModuleGuestProvider; +``` diff --git a/plugins/auth-backend-module-guest-provider/catalog-info.yaml b/plugins/auth-backend-module-guest-provider/catalog-info.yaml new file mode 100644 index 0000000000..5d3513296b --- /dev/null +++ b/plugins/auth-backend-module-guest-provider/catalog-info.yaml @@ -0,0 +1,10 @@ +apiVersion: backstage.io/v1alpha1 +kind: Component +metadata: + name: backstage-plugin-auth-backend-module-guest-provider + title: '@backstage/plugin-auth-backend-module-guest-provider' + description: The guest-provider backend module for the auth plugin. +spec: + lifecycle: experimental + type: backstage-backend-plugin-module + owner: maintainers 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..eb60492393 --- /dev/null +++ b/plugins/auth-backend-module-guest-provider/config.d.ts @@ -0,0 +1,42 @@ +/* + * 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?: { + providers: { + guest?: { + /** + * The entity reference to use for the guest user. + * @default user:development/guest + */ + userEntityRef?: string; + + /** + * A list of entity references to user for ownership of the guest user if the user + * is not found in the catalog. + * @default [userEntityRef] + */ + ownershipEntityRefs?: string[]; + + /** + * Allow users to sign in with the guest provider outside of their development environments. + */ + dangerouslyAllowOutsideDevelopment?: boolean; + }; + }; + }; +} diff --git a/plugins/auth-backend-module-guest-provider/package.json b/plugins/auth-backend-module-guest-provider/package.json new file mode 100644 index 0000000000..bab85c2bd0 --- /dev/null +++ b/plugins/auth-backend-module-guest-provider/package.json @@ -0,0 +1,47 @@ +{ + "name": "@backstage/plugin-auth-backend-module-guest-provider", + "description": "The guest-provider backend module for the auth plugin.", + "version": "0.0.0", + "main": "src/index.ts", + "types": "src/index.ts", + "license": "Apache-2.0", + "publishConfig": { + "access": "public", + "main": "dist/index.cjs.js", + "types": "dist/index.d.ts" + }, + "repository": { + "type": "git", + "url": "https://github.com/backstage/backstage", + "directory": "plugins/auth-backend-module-guest-provider" + }, + "backstage": { + "role": "backend-plugin-module" + }, + "scripts": { + "start": "backstage-cli package start", + "build": "backstage-cli package build", + "lint": "backstage-cli package lint", + "test": "backstage-cli package test", + "clean": "backstage-cli package clean", + "prepack": "backstage-cli package prepack", + "postpack": "backstage-cli package postpack" + }, + "dependencies": { + "@backstage/backend-common": "workspace:^", + "@backstage/backend-plugin-api": "workspace:^", + "@backstage/catalog-model": "workspace:^", + "@backstage/errors": "workspace:^", + "@backstage/plugin-auth-node": "workspace:^", + "passport-oauth2": "^1.7.0" + }, + "devDependencies": { + "@backstage/backend-test-utils": "workspace:^", + "@backstage/cli": "workspace:^", + "@backstage/config": "workspace:^", + "express": "^4.18.2" + }, + "files": [ + "dist" + ] +} diff --git a/plugins/auth-backend-module-guest-provider/src/authenticator.ts b/plugins/auth-backend-module-guest-provider/src/authenticator.ts new file mode 100644 index 0000000000..436b72617d --- /dev/null +++ b/plugins/auth-backend-module-guest-provider/src/authenticator.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. + */ + +import { createProxyAuthenticator } from '@backstage/plugin-auth-node'; + +export const guestAuthenticator = createProxyAuthenticator({ + defaultProfileTransform: async () => { + return { profile: {} }; + }, + initialize() {}, + async authenticate() { + return { result: {} }; + }, +}); diff --git a/plugins/auth-backend-module-guest-provider/src/index.ts b/plugins/auth-backend-module-guest-provider/src/index.ts new file mode 100644 index 0000000000..c6ada31c3a --- /dev/null +++ b/plugins/auth-backend-module-guest-provider/src/index.ts @@ -0,0 +1,23 @@ +/* + * 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. + */ + +/** + * The guest-provider backend module for the auth plugin. + * + * @packageDocumentation + */ + +export { authModuleGuestProvider as default } from './module'; diff --git a/plugins/auth-backend-module-guest-provider/src/module.ts b/plugins/auth-backend-module-guest-provider/src/module.ts new file mode 100644 index 0000000000..7eac99b0a3 --- /dev/null +++ b/plugins/auth-backend-module-guest-provider/src/module.ts @@ -0,0 +1,56 @@ +/* + * 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. + */ +import { + coreServices, + createBackendModule, +} from '@backstage/backend-plugin-api'; +import { + authProvidersExtensionPoint, + createProxyAuthProviderFactory, +} from '@backstage/plugin-auth-node'; +import { guestAuthenticator } from './authenticator'; +import { signInAsGuestUser } from './resolvers'; + +/** @public */ +export const authModuleGuestProvider = createBackendModule({ + pluginId: 'auth', + moduleId: 'guest-provider', + register(reg) { + reg.registerInit({ + deps: { + logger: coreServices.logger, + providers: authProvidersExtensionPoint, + config: coreServices.rootConfig, + }, + async init({ providers, logger, config }) { + if (process.env.NODE_ENV !== 'development') { + 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( + config.getConfig('auth.providers.guest'), + ), + }), + }); + }, + }); + }, +}); diff --git a/plugins/auth-backend-module-guest-provider/src/resolvers.ts b/plugins/auth-backend-module-guest-provider/src/resolvers.ts new file mode 100644 index 0000000000..35f724f746 --- /dev/null +++ b/plugins/auth-backend-module-guest-provider/src/resolvers.ts @@ -0,0 +1,59 @@ +/* + * 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. + */ + +import { stringifyEntityRef } from '@backstage/catalog-model'; +import type { Config } from '@backstage/config'; +import { SignInResolver } from '@backstage/plugin-auth-node'; +import { NotImplementedError } from '@backstage/errors'; + +/** + * Provide a default implementation of the user to resolve to. By default, this + * 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: (config: Config) => SignInResolver<{}> = + (config: Config) => async (_, ctx) => { + if ( + process.env.NODE_ENV !== 'development' && + config.getOptionalBoolean('dangerouslyAllowOutsideDevelopment') !== true + ) { + throw new NotImplementedError( + 'The guest provider is NOT recommended for use outside of a development environment. If you want to enable this, set `auth.providers.guest.dangerouslyAllowOutsideDevelopment: true` in your app config.', + ); + } + const userRef = + config.getOptionalString('userEntityRef') ?? + stringifyEntityRef({ + kind: 'user', + namespace: 'development', + name: 'guest', + }); + const ownershipRefs = config.getOptionalStringArray( + 'ownershipEntityRefs', + ) ?? [userRef]; + 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: ownershipRefs, + }, + }); + } + }; diff --git a/plugins/auth-backend-module-guest-provider/src/types.ts b/plugins/auth-backend-module-guest-provider/src/types.ts new file mode 100644 index 0000000000..c831014cb5 --- /dev/null +++ b/plugins/auth-backend-module-guest-provider/src/types.ts @@ -0,0 +1,21 @@ +/* + * 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. + */ + +import { ProfileTransform } from '@backstage/plugin-auth-node'; + +export interface GuestAuthenticator { + defaultProfileTransform: ProfileTransform<{}>; +} diff --git a/yarn.lock b/yarn.lock index 500419db19..3b404940a9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4682,6 +4682,23 @@ __metadata: languageName: unknown linkType: soft +"@backstage/plugin-auth-backend-module-guest-provider@workspace:^, @backstage/plugin-auth-backend-module-guest-provider@workspace:plugins/auth-backend-module-guest-provider": + version: 0.0.0-use.local + resolution: "@backstage/plugin-auth-backend-module-guest-provider@workspace:plugins/auth-backend-module-guest-provider" + dependencies: + "@backstage/backend-common": "workspace:^" + "@backstage/backend-plugin-api": "workspace:^" + "@backstage/backend-test-utils": "workspace:^" + "@backstage/catalog-model": "workspace:^" + "@backstage/cli": "workspace:^" + "@backstage/config": "workspace:^" + "@backstage/errors": "workspace:^" + "@backstage/plugin-auth-node": "workspace:^" + express: ^4.18.2 + passport-oauth2: ^1.7.0 + languageName: unknown + linkType: soft + "@backstage/plugin-auth-backend-module-microsoft-provider@workspace:^, @backstage/plugin-auth-backend-module-microsoft-provider@workspace:plugins/auth-backend-module-microsoft-provider": version: 0.0.0-use.local resolution: "@backstage/plugin-auth-backend-module-microsoft-provider@workspace:plugins/auth-backend-module-microsoft-provider" @@ -27400,6 +27417,7 @@ __metadata: "@backstage/plugin-app-backend": "workspace:^" "@backstage/plugin-auth-backend": "workspace:^" "@backstage/plugin-auth-backend-module-github-provider": "workspace:^" + "@backstage/plugin-auth-backend-module-guest-provider": "workspace:^" "@backstage/plugin-auth-node": "workspace:^" "@backstage/plugin-azure-devops-backend": "workspace:^" "@backstage/plugin-badges-backend": "workspace:^" @@ -37336,7 +37354,7 @@ __metadata: languageName: node linkType: hard -"passport-oauth2@npm:1.x.x, passport-oauth2@npm:^1.1.2, passport-oauth2@npm:^1.4.0, passport-oauth2@npm:^1.6.0, passport-oauth2@npm:^1.6.1": +"passport-oauth2@npm:1.x.x, passport-oauth2@npm:^1.1.2, passport-oauth2@npm:^1.4.0, passport-oauth2@npm:^1.6.0, passport-oauth2@npm:^1.6.1, passport-oauth2@npm:^1.7.0": version: 1.8.0 resolution: "passport-oauth2@npm:1.8.0" dependencies: