Securing the entrance to Backstage: choosing and configuring auth providers from guest mode for development, OIDC, GitHub, Google, SAML, to Microsoft Entra ID and custom providers, understanding sign-in resolvers, and tracing Backstage identity through tokens, the Backstage-Identity header, and user principals.

In episode 7, the documentation you built with TechDocs finally appears inside Backstage — but anyone who opens the portal gets to read all of it. Episode 8 closes that gap: we dive into Authentication & Identity, the layer that decides who gets into Backstage and what they hold. This is where Backstage changes from an open portal into a gate connected to your organization's identity provider, whether that's GitHub, Google, OIDC, SAML, or Microsoft Entra ID.
These two concepts are often conflated, but they're different:
Authentication answers the question who is coming, while identity answers what are they allowed to do. Episode 8 covers both, starting with authentication providers.
An auth provider is a module that connects Backstage to an external identity provider. Each provider implements an OAuth flow or a similar protocol, and the results are normalized into a single Backstage identity format. Providers are enabled through the auth.providers configuration in app-config.yaml.
| Provider | Protocol | Best for |
|---|---|---|
| Guest | No credentials | Local development (not for production) |
| GitHub | OAuth App | Teams that use GitHub daily |
| OAuth 2.0 | Organizations using Google Workspace | |
| OIDC | OpenID Connect | Generic SSO with IdPs like Keycloak or Okta |
| SAML | XML-based assertion | Enterprise IdPs like ADFS |
| Microsoft Entra ID | OAuth 2.0 / OpenID Connect | Microsoft 365 organizations |
| Custom | Depends on implementation | Internal IdPs or specific needs |
The simplest provider is guest mode: one click to sign in without a username or password. Its purpose is purely for development — so developers don't have to open OAuth every time they run Backstage on a local machine.
auth:
environment: development
providers:
guest: {}Other providers (GitHub, Google, and so on) are configured with clientId and clientSecret, usually pulled from environment variables so they're never recorded in the repository. Secret values are substituted when the config is loaded.
auth:
environment: development
providers:
github:
development:
clientId: ${AUTH_GITHUB_CLIENT_ID}
clientSecret: ${AUTH_GITHUB_CLIENT_SECRET}
google:
development:
clientId: ${AUTH_GOOGLE_CLIENT_ID}
clientSecret: ${AUTH_GOOGLE_CLIENT_SECRET}When no built-in provider fits — for example an internal IdP with a proprietary protocol — Backstage lets you build your own provider through the auth extension point in the backend. A custom provider typically wraps an OAuth flow or another protocol and produces an identity in the same format as built-in providers.
import { createBackendModule } from '@backstage/backend-plugin-api';
import { authProvidersExtensionPoint } from '@backstage/plugin-auth-node';
import { myCustomProvider } from './providers';
export default createBackendModule({
pluginId: 'auth',
moduleId: 'custom-provider',
register(reg) {
reg.registerInit({
deps: { authProviders: authProvidersExtensionPoint },
async init({ authProviders }) {
authProviders.registerProvider({
providerId: 'custom',
factory: myCustomProvider,
});
},
});
},
});Once registered, a custom provider is treated like any other: it appears on the login page and can be attached to sign-in resolvers.
When a user is authenticated at an external provider, Backstage doesn't yet know who that user is in the catalog. A sign-in resolver bridges the two: it maps a profile from the provider to a user entity in the catalog, usually based on email or username. Resolvers are attached per provider via signIn.resolvers — they can also be attached for all providers.
auth:
providers:
github:
development:
clientId: ${AUTH_GITHUB_CLIENT_ID}
clientSecret: ${AUTH_GITHUB_CLIENT_SECRET}
signIn:
resolvers:
- resolver: emailMatchingUserEntityAnnotationemailMatchingUserEntityAnnotation matches the provider's email against the email on the user entity in the catalog. If no resolver matches, sign-in fails — this isn't a bug but a control mechanism: only users registered in the catalog are allowed in.
Warning
Never enable guest mode in a production environment. It grants access without any authentication and is usually meant for demos only. In production, enable only real providers and make sure every provider has a correct sign-in resolver.
After a successful sign-in, Backstage builds an identity used across the entire system. Three concepts you need to know:
| Concept | Form | Usage |
|---|---|---|
| Backstage token | Short-lived JWT token | Issued at sign-in, used for requests that need identity |
Backstage-Identity header | HTTP header carrying the token | Sent on requests between backends and to backend plugins |
| User principal | Resolved identity object | The request context held by backend plugins |
A Backstage token is a JWT issued when the user signs in. It's short-lived and contains claims such as the user's identity. The Backstage-Identity header is the channel that carries that token from the frontend to the backend — every request that needs a user identity includes it. The user principal is the result of resolving the token into an identity object containing a reference to the user entity, the entities the user owns, and additional attributes. This principal becomes the context of every request in a backend plugin.
Backend-to-backend communication also uses tokens, but in a different way than the frontend: service identity uses service tokens, while requests from the frontend use user tokens carried by the Backstage-Identity header. The backend verifies the token to know where the request came from, then translates it into a user principal for the plugins.
curl -i "http://localhost:7007/api/catalog/entities" \
-H "Authorization: Bearer ${BACKSTAGE_USER_TOKEN}"The value of the BACKSTAGE_USER_TOKEN variable is a token taken from the login session. The backend verifies that token, builds the user principal, and executes the request in the context of that identity — including the ownership rules we'll explore further in episode 11 and the permission framework in episode 13.
In this episode 8, you secured the entrance to Backstage: the difference between authentication and identity, the catalog of auth providers from guest mode, OIDC, GitHub, Google, SAML, to Microsoft Entra ID and custom providers, the role of sign-in resolvers in mapping external users into the catalog, and the path of Backstage identity through tokens, the Backstage-Identity header, and user principals.
The key takeaways:
Backstage-Identity; the backend verifies and builds a user principal.In the next episode, episode 9, we tidy up the foundation you've been tweaking all series: Advanced Configuration & Secrets — how app-config is validated, how environment variables are substituted, and how secrets like auth provider credentials are stored without ever touching the repository.