Skip to content

Configure Spotify’s backstage with Keycloak OiDC as Auth Provider

Spotify’s backstage is a widely used developer portal framework. When properly configured, it is very powerful and integrates with dozens of other tools that are used daily by developers, managers and devops teams.

One of the first aspects to consider when setting up backstage is the user access and authentication. Although Backstage provides a few out-of-the-box identity provider integrations, Keycloak is not one of them, and the process to configure it can be a bit daunting, especially if one is new to Backstage and how it is structure.

This article aims to provide a step-by-step guide on how to create a Keycloak OiDC client and how to configure Backstage to use it.

Step 1 – Create an OiDC client in Keycloak

The first step concerns the creation of an OiDC client in keycloak. This should be straightforward. Write down the client id and secret as they will be needed later.

Step 2 – Change the app-config.yaml file to include the auth provider

In the app-config.yaml file, add the new authentication provider. The name can be anything you want, as long as it doesn’t conflict with other existing providers. We’ll go with “keycloak” in this example. Point to the realm well-known open id metadata (you can get this url form the Realm settings). Fill in the id and secret of you recently created keycloak client, and very important, set the prompt to auto, otherwise if you try to login in with keycloak from backstage and a keycloak session isn’t already open, backstage will fail to authenticate. The Auto option will ensure that if you’re not already logged in, the login prompt will be displayed.

auth:
  environment: development
  session:
    secret: <your_session_secret>
  # see https://backstage.io/docs/auth/ to learn about auth providers
  providers:
    keycloak:
      development:
        metadataUrl: https://<KC_URL>/realms/<REALM>/.well-known/openid-configuration
        clientId: <client_id>
        clientSecret: <client_secret>
        prompt: auto

        

Step 3 – Create the OIDCAuthApiRef in apis.ts

In the existing api collection, add the OpenID connect API (apis.ts file).

import {
  ScmIntegrationsApi,
  scmIntegrationsApiRef,
  ScmAuth,
} from '@backstage/integration-react';
import {
  AnyApiFactory,
  ApiRef,
  BackstageIdentityApi,
  configApiRef,
  createApiFactory,
  createApiRef,
  OpenIdConnectApi, 
  oauthRequestApiRef,
  discoveryApiRef,
  ProfileInfoApi,
  SessionApi,
} from '@backstage/core-plugin-api';


import { OAuth2 } from '@backstage/core-app-api';

export const kcOIDCAuthApiRef: ApiRef<
  OpenIdConnectApi & ProfileInfoApi & BackstageIdentityApi & SessionApi
> = createApiRef({
  id: 'auth.keycloak',
});


export const apis: AnyApiFactory[] = [
  createApiFactory({
    api: kcOIDCAuthApiRef,
    deps: {
      discoveryApi: discoveryApiRef,
      oauthRequestApi: oauthRequestApiRef,
      configApi: configApiRef,
    },
    factory: ({ discoveryApi, oauthRequestApi, configApi }) =>
      OAuth2.create({
        configApi,
        discoveryApi,
        oauthRequestApi,
        provider: {
          id: 'keycloak',
          title: 'Log in with keycloak',
          icon: () => null,
        },
        environment: configApi.getOptionalString('auth.environment'),
        defaultScopes: ['openid', 'profile', 'email'],
        
        popupOptions: {
          // optional, used to customize login in popup size
          size: {
            fullscreen: true,
          }
        }
      })
  }),
  createApiFactory({
    api: scmIntegrationsApiRef,
    deps: { configApi: configApiRef },
    factory: ({ configApi }) => ScmIntegrationsApi.fromConfig(configApi),
  }),

  ScmAuth.createDefaultApiFactory(),
];

Step 4 – Add the provider to the App.tsx file

Next, we need to tell the application which auth API ref to use. Change the default provider (which should be Guest) to the new kcOIDCAuthAPiRef, created in step 3. Add the necessary imports.

....  
components: {
    SignInPage: props => (<SignInPage {...props} auto providers={[{
      apiRef: kcOIDCAuthApiRef,
      id: "keycloak",
      message: "<message to show>",
      title: "<title to show>"
    }]} />),
  },

Step 5 – Add the custom auth provider module to the backend index.ts file

The last step in terms of code change is about adding the auth provider module to the backend. The first code block creates the module, and the second one below adds it to the backend.

....
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',
  register(reg) {
    reg.registerInit({
      deps: { providers: authProvidersExtensionPoint },
      async init({ providers }) {
        providers.registerProvider({         
          factory: createOAuthProviderFactory({            
            authenticator: oidcAuthenticator,
            async signInResolver(info, ctx) {
              const userRef: any = stringifyEntityRef({
                kind: 'User',
                name: info?.result.fullProfile.userinfo.name as string,
                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
                },
              });
            },
          }),
        });
      },
    });
  },
});

....
backend.add(myAuthProviderModule);

Step 6 – Add the following dependencies to the package.json file

The last step is just here to ensure that all the necessary packages are installed. Your IDE has probably suggested their addition as the code in the previous steps was added.

“@backstage/backend-plugin-api”: “^1.1.1”,

“@backstage/catalog-model”: “^1.7.3”,

“@backstage/plugin-auth-backend-module-oidc-provider”: “^0.3.4”,

At this moment, running the app (yarn build:backend, followed by yarn dev) shall provide you with a button to “Login with keycloak”. This will now open a popup with Keycloaks’ new client login form.

As a way to provide a better consolidated view on what needs to be changed, please refer to this commit, where all the needed changes are more easily spotted:

And that should be it. Cheers!

Published inBackendKubernetesUncategorized

Be First to Comment

Leave a Reply