docs/auth/oidc--old.md
::::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 instead. ::::
This section shows how to enable and use the Backstage OIDC provider.
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, you would re-badge the OIDC provider as Keycloak and tell users to Sign In using Keycloak.
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:
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.
An API reference exists to enable Dependency Injection. (See Utility APIs 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.
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 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.
/* 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 */
];
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:
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:
yarn --cwd packages/backend add @backstage/plugin-auth-backend-module-oidc-provider
Then create a custom resolver as shown below:
/* 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 page.
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:
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.
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.
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.tokenEndpointAuthMethodtokenSignedResponseAlgscope: 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 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 component, you can add it to the providers array like this:
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. :::