From 23f731118d1df47594c49a08e3a81bab1ba0df4a Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sun, 29 Mar 2026 21:32:52 +0200 Subject: [PATCH 1/6] docs: migrate OIDC auth docs to new frontend system Signed-off-by: Patrik Oldsberg Made-with: Cursor --- docs/auth/oidc--old.md | 293 +++++++++++++++++++++++++++++++++ docs/auth/oidc.md | 365 ++++++++++++++++++++--------------------- 2 files changed, 472 insertions(+), 186 deletions(-) create mode 100644 docs/auth/oidc--old.md diff --git a/docs/auth/oidc--old.md b/docs/auth/oidc--old.md new file mode 100644 index 0000000000..a3df5cef3d --- /dev/null +++ b/docs/auth/oidc--old.md @@ -0,0 +1,293 @@ +--- +id: oidc--old +title: OIDC provider from scratch (Old Frontend System) +description: This section shows how to enable and use the Backstage OIDC provider. +--- + +::::info +This documentation is for Backstage apps that still use the old frontend +system. If your app uses the new frontend system, read the +[current guide](./oidc.md) instead. +:::: + +This section shows how to enable and use the Backstage OIDC provider. + +## Summary + +OIDC is a protocol which has numerous implementations. It's likely that many of your users won't know what the OIDC **protocol** is, but they will recognise your OIDC **implementation**. Backstage supplies a generic `oidc` authorization strategy. You should re-badge this with the name and branding of your OIDC implementation, so that your users will recognise it on the Backstage sign-in page. + +For example, if your organization uses [Keycloak](https://www.keycloak.org), you would re-badge the OIDC provider as `Keycloak` and tell users to `Sign In using Keycloak`. + +## Steps + +The Backstage OIDC provider is not enabled by default. You need to manually enable the provider, and tell it which OIDC server you want to use. + +To enable the Backstage OIDC provider: + +- Create an API reference to identify the provider. +- Create the API factory that will handle the authentication. +- Add or reuse an auth provider so you can authenticate. +- Add or reuse a resolver to handle the result from the authentication. +- Configure the provider to access your 3rd party auth solution. +- Add the provider to the Backstage sign-in page. + +For simplicity, we assume that you only have a single OIDC provider in your Backstage installation. (If you need to have multiple OIDC providers in Backstage, the steps will be different.) + +We'll explain each step more in detail next. + +### The API Reference + +An API reference exists to enable **Dependency Injection**. (See [Utility APIs][4] for an extended explanation.) + +In this example, we'll create the API ref directly in the `packages/app/src/apis.ts` file. It is not a requirement to put the ref in this file. Any location will do as long as it's available to be imported to where the API factory is, as well as easily accessible to the rest of the application so any package and plugin can inject the API instance when necessary. + +```ts +export const keycloakAuthApiRef: ApiRef< + OpenIdConnectApi & ProfileInfoApi & BackstageIdentityApi & SessionApi +> = createApiRef({ + id: 'auth.keycloak', +}); +``` + +The `id` of the API ref can be anything you want, as long as it doesn't conflict with other refs. Backstage recommends to use a custom name that references your custom provider. + +:::note TypeScript Note +As we're exporting this API reference, as well as the TypeScript types, we need to +be able to import this reference anywhere in the app. The types will tell TypeScript +what instance we're getting from DI when injecting the API. In this case we are defining +an API for authentication, so we tell TS that this instance complies with 4 API +interfaces: + +- The OIDC API that will handle authentication. +- Profile API for requesting user profile info from the auth provider in question. +- Backstage identity API to handle and associate the user profile with backstage identity. +- Session API, to handle the session the user will have while signed in. + ::: + +### The API Factory (and auth provider) + +The Backstage API factories are part of the Backstage Dependency Injection system. The factory function runs once, when something in your Backstage app first attempts to use an instance of the API it provides. The instance is then cached by the DI system for subsequent lookups. + +Let's add a new API factory to the `apis` array in the `packages/app/src/apis.ts` file. We will tell it to use the OIDC auth provider internally. + +```ts title="packages/app/src/apis.ts" +/* highlight-add-next-line */ +import { OAuth2 } from '@backstage/core-app-api'; + +export const apis: AnyApiFactory[] = [ + /* highlight-add-start */ + createApiFactory({ + api: keycloakAuthApiRef, + deps: { + discoveryApi: discoveryApiRef, + oauthRequestApi: oauthRequestApiRef, + configApi: configApiRef, + }, + factory: ({ discoveryApi, oauthRequestApi, configApi }) => + // delegate auth to the OAuth2 strategy + OAuth2.create({ + configApi, + discoveryApi, + oauthRequestApi, + provider: { + // this value MUST be 'oidc' + // it maps our Keycloak-branded sign-in provider onto Backstage's generic OIDC auth strategy + id: 'oidc', + title: 'Keycloak', + icon: () => null, + }, + environment: configApi.getOptionalString('auth.environment'), + defaultScopes: ['openid', 'profile', 'email'], + popupOptions: { + // optional, used to customize sign-in window size + size: { + fullscreen: true, + }, + /** + * or specify popup width and height + * size: { + width: 1000, + height: 1000, + } + */ + }, + }), + }), + /* highlight-add-end */ +]; +``` + +### The Resolver + +Resolvers exist to map the user identity from the 3rd party (in this case Keycloak) to the Backstage user identity. + +The default OIDC provider has a choice of built-in resolvers, here is how you configure them: + +```yaml title="app-config.yaml" +auth: + environment: development + providers: + oidc: + development: + # ... + signIn: + resolvers: + - resolver: emailMatchingUserEntityProfileEmail +``` + +If none of the built-in resolvers are suitable, you can alternatively write a custom resolver. + +First, install the OIDC provider module: + +```bash +yarn --cwd packages/backend add @backstage/plugin-auth-backend-module-oidc-provider +``` + +Then create a custom resolver as shown below: + +```ts title="in packages/backend/src/index.ts" +/* highlight-add-start */ +import { createBackendModule } from '@backstage/backend-plugin-api'; +import { + authProvidersExtensionPoint, + createOAuthProviderFactory, +} from '@backstage/plugin-auth-node'; +import { oidcAuthenticator } from '@backstage/plugin-auth-backend-module-oidc-provider'; +import { + stringifyEntityRef, + DEFAULT_NAMESPACE, +} from '@backstage/catalog-model'; + +const myAuthProviderModule = createBackendModule({ + // This ID must be exactly "auth" because that's the plugin it targets + pluginId: 'auth', + // This ID must be unique, but can be anything + moduleId: 'keycloak-auth-provider', + register(reg) { + reg.registerInit({ + deps: { providers: authProvidersExtensionPoint }, + async init({ providers }) { + providers.registerProvider({ + // This ID must match the actual provider config, e.g. addressing + // auth.providers.keycloak means that this must be "keycloak". + providerId: 'keycloak', + // Use createProxyAuthProviderFactory instead if it's one of the proxy + // based providers rather than an OAuth based one + factory: createOAuthProviderFactory({ + // For more info about authenticators please see https://backstage.io/docs/auth/add-auth-provider/#adding-an-oauth-based-provider + authenticator: oidcAuthenticator, + async signInResolver(info, ctx) { + const userRef = stringifyEntityRef({ + kind: 'User', + name: info.result.fullProfile.userinfo.sub, + namespace: DEFAULT_NAMESPACE, + }); + return ctx.issueToken({ + claims: { + sub: userRef, // The user's own identity + ent: [userRef], // A list of identities that the user claims ownership through + }, + }); + }, + }), + }); + }, + }); + }, +}); +/* highlight-add-end */ +//... +backend.add(import('@backstage/plugin-auth-backend')); +/* highlight-add-next-line */ +backend.add(myAuthProviderModule); +//... +``` + +For a more detailed explanation about resolvers check the [Identity Resolver][1] page. + +### The Configuration + +We will now configure our Keycloak-branded OIDC Auth Provider in Backstage, so that it can talk to our Keycloak server. + +The first step is to register an OIDC client app for Backstage in your Keycloak server. + +Then we need to configure the provider. Based on the provider's code in `plugins/auth-backend/src/providers/oidc/provider.ts` we need the following parameters in the `app-config.yaml`: + +```yaml title="app-config.yaml" +auth: + environment: development + session: + secret: ${AUTH_SESSION_SECRET} + providers: + oidc: + development: + metadataUrl: https://example.com/.well-known/openid-configuration + clientId: ${AUTH_OIDC_CLIENT_ID} + clientSecret: ${AUTH_OIDC_CLIENT_SECRET} +``` + +Anything enclosed in `${}` can be replaced directly in the YAML, or provided as environment variables. + +#### Required Parameters + +These parameters must always be set. + +- `clientId`: Grab from the Overview page. +- `clientSecret`: Can only be seen when creating the secret, if you lose it you'll need a + new secret. +- `metadataUrl`: In Overview > Endpoints tab, grab OpenID Connect metadata document URL. + +The OIDC provider **also** requires the `auth.session.secret` to be set. + +#### Optional Parameters + +These parameters have implicit default values. Don't override them unless you know what you're doing. + +- `authorizationUrl` and `tokenUrl`: Open the `metadataUrl` in a browser, that json will + hold these 2 urls somewhere in there. +- `tokenEndpointAuthMethod` +- `tokenSignedResponseAlg` +- `scope`: Only used if we didn't specify `defaultScopes` in the provider's factory, + basically the same thing. +- `prompt`: Recommended to use `auto` so the browser will request sign-in to the IDP if the + user has no active session. +- `sessionDuration`: Lifespan of the user session. +- `startUrlSearchParams`: This is a dictionary of search (query) parameters for the OIDC + authorization start URL. Don't define it unless you want to change the identity + provider's behavior. (For example, you could set the `organization` parameter to guide + users towards a particular sign-in option that your organization prefers.) **Note:** the + start URL is controlled by the browser, so this feature is only for improving the + Backstage user experience. + +:::note Config Reloading +Backstage does not yet support hot reloading of auth provider configuration. Any changes to this YAML file require a restart of Backstage. +::: + +### The Sign-In Page + +The last step is to add the provider to the sign-in page, so users can sign in with your new provider. + +If you are using the standard Backstage [`SignInPage`][3] component, you can add it to the `providers` array like this: + +```ts title="in packages/app/src/identityProviders.ts" +export const providers = [ + // other providers... + { + id: 'keycloak-auth-provider', + title: 'Keycloak', + message: 'Sign In using Keycloak', + apiRef: keycloakAuthApiRef, + }, +]; +``` + +:::note Note +These steps apply to most auth providers. The main +difference between providers will be the contents of the API factory, the code +in the Auth Provider Factory, the resolver, and the different variables each provider +needs in the YAML config or env variables. +::: + +[1]: https://backstage.io/docs/auth/identity-resolver +[3]: https://backstage.io/docs/auth/#sign-in-configuration +[4]: https://backstage.io/docs/api/utility-apis diff --git a/docs/auth/oidc.md b/docs/auth/oidc.md index 194eb08d05..1b5f7978a9 100644 --- a/docs/auth/oidc.md +++ b/docs/auth/oidc.md @@ -4,6 +4,13 @@ title: OIDC provider from scratch description: This section shows how to enable and use the Backstage OIDC provider. --- +::::info +This documentation is written for the new frontend system, which is the default +in new Backstage apps. If your Backstage app still uses the old frontend system, +read the [old frontend system version of this guide](./oidc--old.md) +instead. +:::: + This section shows how to enable and use the Backstage OIDC provider. ## Summary @@ -14,112 +21,97 @@ For example, if your organization uses [Keycloak](https://www.keycloak.org), you ## Steps -The Backstage OIDC provider is not enabled by default. You need to manually enable the provider, and tell it which OIDC server you want to use. +The Backstage OIDC provider is not enabled by default. You need to: -To enable the Backstage OIDC provider: +1. Install and configure the OIDC backend module. +1. Configure the provider in `app-config.yaml`. +1. Configure a sign-in resolver. +1. Add the provider to the Backstage sign-in page. -- Create an API reference to identify the provider. -- Create the API factory that will handle the authentication. -- Add or reuse an auth provider so you can authenticate. -- Add or reuse a resolver to handle the result from the authentication. -- Configure the provider to access your 3rd party auth solution. -- Add the provider to the Backstage sign-in page. +For simplicity, we assume that you only have a single OIDC provider in your Backstage installation. -For simplicity, we assume that you only have a single OIDC provider in your Backstage installation. (If you need to have multiple OIDC providers in Backstage, the steps will be different.) +We'll explain each step in detail next. -We'll explain each step more in detail next. +### Backend Installation -### The API Reference +To add the OIDC provider to the backend, install the provider module: -An API reference exists to enable **Dependency Injection**. (See [Utility APIs][4] for an extended explanation.) - -In this example, we'll create the API ref directly in the `packages/app/src/apis.ts` file. It is not a requirement to put the ref in this file. Any location will do as long as it's available to be imported to where the API factory is, as well as easily accessible to the rest of the application so any package and plugin can inject the API instance when necessary. - -```ts -export const keycloakAuthApiRef: ApiRef< - OpenIdConnectApi & ProfileInfoApi & BackstageIdentityApi & SessionApi -> = createApiRef({ - id: 'auth.keycloak', -}); +```bash title="from your Backstage root directory" +yarn --cwd packages/backend add @backstage/plugin-auth-backend-module-oidc-provider ``` -The `id` of the API ref can be anything you want, as long as it doesn't conflict with other refs. Backstage recommends to use a custom name that references your custom provider. +Then add it to your backend in `packages/backend/src/index.ts`: -:::note TypeScript Note -As we're exporting this API reference, as well as the TypeScript types, we need to -be able to import this reference anywhere in the app. The types will tell TypeScript -what instance we're getting from DI when injecting the API. In this case we are defining -an API for authentication, so we tell TS that this instance complies with 4 API -interfaces: - -- The OIDC API that will handle authentication. -- Profile API for requesting user profile info from the auth provider in question. -- Backstage identity API to handle and associate the user profile with backstage identity. -- Session API, to handle the session the user will have while signed in. - ::: - -### The API Factory (and auth provider) - -The Backstage API factories are part of the Backstage Dependency Injection system. The factory function runs once, when something in your Backstage app first attempts to use an instance of the API it provides. The instance is then cached by the DI system for subsequent lookups. - -Let's add a new API factory to the `apis` array in the `packages/app/src/apis.ts` file. We will tell it to use the OIDC auth provider internally. - -```ts title="packages/app/src/apis.ts" +```ts title="packages/backend/src/index.ts" +backend.add(import('@backstage/plugin-auth-backend')); /* highlight-add-next-line */ -import { OAuth2 } from '@backstage/core-app-api'; - -export const apis: AnyApiFactory[] = [ - /* highlight-add-start */ - createApiFactory({ - api: keycloakAuthApiRef, - deps: { - discoveryApi: discoveryApiRef, - oauthRequestApi: oauthRequestApiRef, - configApi: configApiRef, - }, - factory: ({ discoveryApi, oauthRequestApi, configApi }) => - // delegate auth to the OAuth2 strategy - OAuth2.create({ - configApi, - discoveryApi, - oauthRequestApi, - provider: { - // this value MUST be 'oidc' - // it maps our Keycloak-branded sign-in provider onto Backstage's generic OIDC auth strategy - id: 'oidc', - title: 'Keycloak', - icon: () => null, - }, - environment: configApi.getOptionalString('auth.environment'), - defaultScopes: ['openid', 'profile', 'email'], - popupOptions: { - // optional, used to customize sign-in window size - size: { - fullscreen: true, - }, - /** - * or specify popup width and height - * size: { - width: 1000, - height: 1000, - } - */ - }, - }), - }), - /* highlight-add-end */ -]; +backend.add(import('@backstage/plugin-auth-backend-module-oidc-provider')); ``` -### The Resolver +### Configuration -Resolvers exist to map the user identity from the 3rd party (in this case Keycloak) to the Backstage user identity. - -The default OIDC provider has a choice of built-in resolvers, here is how you configure them: +Register an OIDC client application for Backstage with your OIDC provider (for example, Keycloak). Then add the provider configuration to your `app-config.yaml`: ```yaml title="app-config.yaml" auth: environment: development + session: + secret: ${AUTH_SESSION_SECRET} + providers: + oidc: + development: + metadataUrl: https://example.com/.well-known/openid-configuration + clientId: ${AUTH_OIDC_CLIENT_ID} + clientSecret: ${AUTH_OIDC_CLIENT_SECRET} + signIn: + resolvers: + - resolver: emailMatchingUserEntityProfileEmail +``` + +Anything enclosed in `${}` can be replaced directly in the YAML, or provided as environment variables. + +#### Required parameters + +These parameters must always be set. + +- `clientId`: The client ID from your OIDC provider. +- `clientSecret`: The client secret tied to the client ID. +- `metadataUrl`: The OpenID Connect metadata document URL, for example `https://example.com/.well-known/openid-configuration`. + +The OIDC provider **also** requires `auth.session.secret` to be set. + +#### Optional parameters + +These parameters have implicit default values. Don't override them unless you know what you're doing. + +- `authorizationUrl` and `tokenUrl`: Open the `metadataUrl` in a browser, that JSON will + hold these 2 URLs somewhere in there. +- `tokenEndpointAuthMethod` +- `tokenSignedResponseAlg` +- `scope`: Defaults to `openid profile email`. Override if your provider needs different + scopes. +- `prompt`: Recommended to use `auto` so the browser will request sign-in to the identity + provider if the user has no active session. +- `sessionDuration`: Lifespan of the user session. +- `startUrlSearchParams`: A dictionary of search (query) parameters for the OIDC + authorization start URL. Don't define it unless you want to change the identity + provider's behavior. (For example, you could set the `organization` parameter to guide + users towards a particular sign-in option that your organization prefers.) **Note:** the + start URL is controlled by the browser, so this feature is only for improving the + Backstage user experience. + +:::note Config Reloading +Backstage does not yet support hot reloading of auth provider configuration. Any changes to this YAML file require a restart of Backstage. +::: + +### Resolvers + +Resolvers map the user identity from the OIDC provider to the Backstage user identity. + +The default OIDC provider has a choice of built-in resolvers that you configure in `app-config.yaml` under `auth.providers.oidc..signIn.resolvers`: + +```yaml title="app-config.yaml" +auth: providers: oidc: development: @@ -129,17 +121,11 @@ auth: - resolver: emailMatchingUserEntityProfileEmail ``` -If none of the built-in resolvers are suitable, you can alternatively write a custom resolver. +If none of the built-in resolvers are suitable, you can write a custom resolver. See [Building Custom Resolvers](./identity-resolver.md#building-custom-resolvers) for details. -First, install the OIDC provider module: +The following example shows a custom resolver that maps users by their OIDC `sub` claim: -```bash -yarn --cwd packages/backend add @backstage/plugin-auth-backend-module-oidc-provider -``` - -Then create a custom resolver as shown below: - -```ts title="in packages/backend/src/index.ts" +```ts title="packages/backend/src/index.ts" /* highlight-add-start */ import { createBackendModule } from '@backstage/backend-plugin-api'; import { @@ -153,22 +139,15 @@ import { } from '@backstage/catalog-model'; const myAuthProviderModule = createBackendModule({ - // This ID must be exactly "auth" because that's the plugin it targets pluginId: 'auth', - // This ID must be unique, but can be anything - moduleId: 'keycloak-auth-provider', + moduleId: 'custom-oidc-provider', register(reg) { reg.registerInit({ deps: { providers: authProvidersExtensionPoint }, async init({ providers }) { providers.registerProvider({ - // This ID must match the actual provider config, e.g. addressing - // auth.providers.keycloak means that this must be "keycloak". - providerId: 'keycloak', - // Use createProxyAuthProviderFactory instead if it's one of the proxy - // based providers rather than an OAuth based one + providerId: 'oidc', factory: createOAuthProviderFactory({ - // For more info about authenticators please see https://backstage.io/docs/auth/add-auth-provider/#adding-an-oauth-based-provider authenticator: oidcAuthenticator, async signInResolver(info, ctx) { const userRef = stringifyEntityRef({ @@ -178,8 +157,8 @@ const myAuthProviderModule = createBackendModule({ }); return ctx.issueToken({ claims: { - sub: userRef, // The user's own identity - ent: [userRef], // A list of identities that the user claims ownership through + sub: userRef, + ent: [userRef], }, }); }, @@ -190,98 +169,112 @@ const myAuthProviderModule = createBackendModule({ }, }); /* highlight-add-end */ -//... + +// ... backend.add(import('@backstage/plugin-auth-backend')); -/* highlight-add-next-line */ +/* highlight-add-start */ +// Use the custom module instead of the default OIDC module backend.add(myAuthProviderModule); -//... +/* highlight-add-end */ ``` -For a more detailed explanation about resolvers check the [Identity Resolver][1] page. +For a more detailed explanation about resolvers, see the [Identity Resolver](./identity-resolver.md) page. -### The Configuration +### Adding the provider to the sign-in page -We will now configure our Keycloak-branded OIDC Auth Provider in Backstage, so that it can talk to our Keycloak server. +The last step is to configure the sign-in page in the frontend to use your OIDC provider. -The first step is to register an OIDC client app for Backstage in your Keycloak server. +Since OIDC does not have a built-in auth API ref in `@backstage/core-plugin-api`, you need to create a custom one and wire it up using a `SignInPageBlueprint`. The following example shows how to set this up for a Keycloak-branded OIDC provider in `packages/app/src/App.tsx`: -Then we need to configure the provider. Based on the provider's code in `plugins/auth-backend/src/providers/oidc/provider.ts` we need the following parameters in the `app-config.yaml`: +```tsx title="packages/app/src/App.tsx" +import { createApp } from '@backstage/frontend-defaults'; +/* highlight-add-start */ +import { + createApiRef, + OpenIdConnectApi, + ProfileInfoApi, + BackstageIdentityApi, + SessionApi, +} from '@backstage/core-plugin-api'; +import { OAuth2 } from '@backstage/core-app-api'; +import { SignInPageBlueprint } from '@backstage/plugin-app-react'; +import { SignInPage } from '@backstage/core-components'; +import { + createFrontendModule, + configApiRef, + discoveryApiRef, + oauthRequestApiRef, + ApiBlueprint, +} from '@backstage/frontend-plugin-api'; -```yaml title="app-config.yaml" -auth: - environment: development - session: - secret: ${AUTH_SESSION_SECRET} - providers: - oidc: - development: - metadataUrl: https://example.com/.well-known/openid-configuration - clientId: ${AUTH_OIDC_CLIENT_ID} - clientSecret: ${AUTH_OIDC_CLIENT_SECRET} -``` +const keycloakAuthApiRef = createApiRef< + OpenIdConnectApi & ProfileInfoApi & BackstageIdentityApi & SessionApi +>({ + id: 'auth.keycloak', +}); -Anything enclosed in `${}` can be replaced directly in the YAML, or provided as environment variables. +const keycloakAuthApi = ApiBlueprint.make({ + name: 'keycloak', + params: defineParams => + defineParams({ + api: keycloakAuthApiRef, + deps: { + discoveryApi: discoveryApiRef, + oauthRequestApi: oauthRequestApiRef, + configApi: configApiRef, + }, + factory: ({ discoveryApi, oauthRequestApi, configApi }) => + OAuth2.create({ + configApi, + discoveryApi, + oauthRequestApi, + provider: { + id: 'oidc', + title: 'Keycloak', + icon: () => null, + }, + defaultScopes: ['openid', 'profile', 'email'], + }), + }), +}); -#### Required Parameters - -These parameters must always be set. - -- `clientId`: Grab from the Overview page. -- `clientSecret`: Can only be seen when creating the secret, if you lose it you'll need a - new secret. -- `metadataUrl`: In Overview > Endpoints tab, grab OpenID Connect metadata document URL. - -The OIDC provider **also** requires the `auth.session.secret` to be set. - -#### Optional Parameters - -These parameters have implicit default values. Don't override them unless you know what you're doing. - -- `authorizationUrl` and `tokenUrl`: Open the `metadataUrl` in a browser, that json will - hold these 2 urls somewhere in there. -- `tokenEndpointAuthMethod` -- `tokenSignedResponseAlg` -- `scope`: Only used if we didn't specify `defaultScopes` in the provider's factory, - basically the same thing. -- `prompt`: Recommended to use `auto` so the browser will request sign-in to the IDP if the - user has no active session. -- `sessionDuration`: Lifespan of the user session. -- `startUrlSearchParams`: This is a dictionary of search (query) parameters for the OIDC - authorization start URL. Don't define it unless you want to change the identity - provider's behavior. (For example, you could set the `organization` parameter to guide - users towards a particular sign-in option that your organization prefers.) **Note:** the - start URL is controlled by the browser, so this feature is only for improving the - Backstage user experience. - -:::note Config Reloading -Backstage does not yet support hot reloading of auth provider configuration. Any changes to this YAML file require a restart of Backstage. -::: - -### The Sign-In Page - -The last step is to add the provider to the sign-in page, so users can sign in with your new provider. - -If you are using the standard Backstage [`SignInPage`][3] component, you can just add it to the `providers` array like this: - -```ts title="in packages/app/src/identityProviders.ts" -export const providers = [ - // other providers... - { - id: 'keycloak-auth-provider', - title: 'Keycloak', - message: 'Sign In using Keycloak', - apiRef: keycloakAuthApiRef, +const signInPage = SignInPageBlueprint.make({ + params: { + loader: async () => props => + ( + + ), }, -]; +}); +/* highlight-add-end */ + +export default createApp({ + features: [ + // ... + /* highlight-add-start */ + createFrontendModule({ + pluginId: 'app', + extensions: [keycloakAuthApi, signInPage], + }), + /* highlight-add-end */ + ], +}); ``` +The `id` of the API ref and the provider `id` in `OAuth2.create` can be customized, but the `provider.id` must be `'oidc'` to match Backstage's generic OIDC auth strategy on the backend. + :::note Note -These steps apply to most auth providers. The main -difference between providers will be the contents of the API factory, the code -in the Auth Provider Factory, the resolver, and the different variables each provider -needs in the YAML config or env variables. +You can configure sign-in to use a redirect flow with no pop-up by adding `enableExperimentalRedirectFlow: true` to the root of your `app-config.yaml`. ::: +For more information about sign-in configuration, see [Sign-in Configuration](./index.md#sign-in-configuration). + [1]: https://backstage.io/docs/auth/identity-resolver -[3]: https://backstage.io/docs/auth/#sign-in-configuration -[4]: https://backstage.io/docs/api/utility-apis From 89e3f98fa15e95dd8ada3f91e15497125f79b689 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Mon, 30 Mar 2026 12:39:13 +0200 Subject: [PATCH 2/6] Apply suggestions from code review Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Patrik Oldsberg --- docs/auth/oidc.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/docs/auth/oidc.md b/docs/auth/oidc.md index 1b5f7978a9..46094f5a99 100644 --- a/docs/auth/oidc.md +++ b/docs/auth/oidc.md @@ -269,12 +269,10 @@ export default createApp({ }); ``` -The `id` of the API ref and the provider `id` in `OAuth2.create` can be customized, but the `provider.id` must be `'oidc'` to match Backstage's generic OIDC auth strategy on the backend. +The `id` of the API ref, as well as the `provider.id` used in the `SignInPage` configuration (for example, `'keycloak-auth-provider'` in the snippet above), can be customized. However, the `provider.id` passed to `OAuth2.create({ provider: { id } })` must remain `'oidc'` to match Backstage's generic OIDC auth strategy on the backend. :::note Note You can configure sign-in to use a redirect flow with no pop-up by adding `enableExperimentalRedirectFlow: true` to the root of your `app-config.yaml`. ::: For more information about sign-in configuration, see [Sign-in Configuration](./index.md#sign-in-configuration). - -[1]: https://backstage.io/docs/auth/identity-resolver From c38a71f855ff11d1185fdc5022a2007861178df1 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sun, 29 Mar 2026 21:32:52 +0200 Subject: [PATCH 3/6] docs: migrate OIDC auth docs to new frontend system Signed-off-by: Patrik Oldsberg Made-with: Cursor Signed-off-by: Patrik Oldsberg Made-with: Cursor --- docs/auth/oidc.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/auth/oidc.md b/docs/auth/oidc.md index 46094f5a99..3138a763d9 100644 --- a/docs/auth/oidc.md +++ b/docs/auth/oidc.md @@ -190,7 +190,6 @@ Since OIDC does not have a built-in auth API ref in `@backstage/core-plugin-api` import { createApp } from '@backstage/frontend-defaults'; /* highlight-add-start */ import { - createApiRef, OpenIdConnectApi, ProfileInfoApi, BackstageIdentityApi, @@ -200,6 +199,7 @@ import { OAuth2 } from '@backstage/core-app-api'; import { SignInPageBlueprint } from '@backstage/plugin-app-react'; import { SignInPage } from '@backstage/core-components'; import { + createApiRef, createFrontendModule, configApiRef, discoveryApiRef, @@ -209,7 +209,7 @@ import { const keycloakAuthApiRef = createApiRef< OpenIdConnectApi & ProfileInfoApi & BackstageIdentityApi & SessionApi ->({ +>().with({ id: 'auth.keycloak', }); @@ -269,7 +269,7 @@ export default createApp({ }); ``` -The `id` of the API ref, as well as the `provider.id` used in the `SignInPage` configuration (for example, `'keycloak-auth-provider'` in the snippet above), can be customized. However, the `provider.id` passed to `OAuth2.create({ provider: { id } })` must remain `'oidc'` to match Backstage's generic OIDC auth strategy on the backend. +The `id` of the API ref (e.g. `'auth.keycloak'`) and the `id` used in the `SignInPage` provider configuration (e.g. `'keycloak-auth-provider'`) can be customized. However, the `provider.id` passed to `OAuth2.create` must remain `'oidc'` to match Backstage's generic OIDC auth strategy on the backend. :::note Note You can configure sign-in to use a redirect flow with no pop-up by adding `enableExperimentalRedirectFlow: true` to the root of your `app-config.yaml`. From 88296d5d3379ea4a0b5968983e52fd1ae047545d Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Mon, 30 Mar 2026 12:48:01 +0200 Subject: [PATCH 4/6] docs: pass auth environment in OAuth2.create example Signed-off-by: Patrik Oldsberg Made-with: Cursor --- docs/auth/oidc.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/auth/oidc.md b/docs/auth/oidc.md index 3138a763d9..46bd5f342f 100644 --- a/docs/auth/oidc.md +++ b/docs/auth/oidc.md @@ -228,6 +228,7 @@ const keycloakAuthApi = ApiBlueprint.make({ configApi, discoveryApi, oauthRequestApi, + environment: configApi.getOptionalString('auth.environment'), provider: { id: 'oidc', title: 'Keycloak', From bf1eb45609c427557e33a1ce5fc9b9e404725503 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Mon, 30 Mar 2026 17:14:00 +0200 Subject: [PATCH 5/6] Apply suggestions from code review Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Patrik Oldsberg --- docs/auth/oidc.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/auth/oidc.md b/docs/auth/oidc.md index 46bd5f342f..9c3fb86f0e 100644 --- a/docs/auth/oidc.md +++ b/docs/auth/oidc.md @@ -84,12 +84,13 @@ The OIDC provider **also** requires `auth.session.secret` to be set. These parameters have implicit default values. Don't override them unless you know what you're doing. -- `authorizationUrl` and `tokenUrl`: Open the `metadataUrl` in a browser, that JSON will - hold these 2 URLs somewhere in there. +- `callbackUrl`: Override the default callback URL used by the OIDC provider. +- `timeout`: Override the default timeout for calls to the OIDC provider. - `tokenEndpointAuthMethod` - `tokenSignedResponseAlg` -- `scope`: Defaults to `openid profile email`. Override if your provider needs different - scopes. +- `additionalScopes`: Requests additional scopes on top of the default `openid profile + email` scopes. Do not configure `scope` directly, as the OIDC provider will reject + configurations that include it. - `prompt`: Recommended to use `auto` so the browser will request sign-in to the identity provider if the user has no active session. - `sessionDuration`: Lifespan of the user session. From 19b3196c67906f306be63fd98f3ea695653c4554 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Mon, 30 Mar 2026 17:26:29 +0200 Subject: [PATCH 6/6] docs: fix Prettier formatting in OIDC auth docs Signed-off-by: Patrik Oldsberg Made-with: Cursor --- docs/auth/oidc.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/auth/oidc.md b/docs/auth/oidc.md index 9c3fb86f0e..77af72d0fa 100644 --- a/docs/auth/oidc.md +++ b/docs/auth/oidc.md @@ -89,7 +89,7 @@ These parameters have implicit default values. Don't override them unless you kn - `tokenEndpointAuthMethod` - `tokenSignedResponseAlg` - `additionalScopes`: Requests additional scopes on top of the default `openid profile - email` scopes. Do not configure `scope` directly, as the OIDC provider will reject +email` scopes. Do not configure `scope` directly, as the OIDC provider will reject configurations that include it. - `prompt`: Recommended to use `auto` so the browser will request sign-in to the identity provider if the user has no active session.