Overview #
Backstage is designed to be a developer portal, bringing together your internal tools, services, and infrastructure into one unified platform. As you connect multiple systems — GitHub, Jenkins, Kubernetes, and others — authentication becomes increasingly crucial to handle different types of personas and access patterns.
In this guide lets walks through the two main authentication methods in Backstage:
- User-Based Authentication (via GitHub OAuth) – for users logging into the Backstage UI.
- Service-Based Authentication (via API Keys) – for backend systems, automation, and service integrations.
Authentication in Backstage #
Authentication ensures that only verified users and trusted systems can access your internal services or APIs.
In Backstage:
- User auth allows engineers to sign in using their identity provider (GitHub, Google, Okta etc.), so actions are tied to real users.
- Service auth allows non-human systems — such as CI/CD pipelines or automation scripts — to interact securely with the Backstage backend.
User-Based Authentication with GitHub OAuth #
Backstage entrypoints and packages #
First, ensure that your Backstage backend has the auth-backend plugin enabled. It handles OAuth flows for supported providers (GitHub, Google, Microsoft, Okta etc.).
In your Backstage root directory:
yarn add --cwd packages/backend @backstage/plugin-auth-backend
yarn add --cwd packages/backend @backstage/plugin-auth-backend-module-github-provider
In your backend entrypoint (packages/backend/src/index.ts):
// auth plugin
backend.add(import('@backstage/plugin-auth-backend'));
// github
backend.add(import('@backstage/plugin-auth-backend-module-github-provider'));
Create a GitHub OAuth App #
In your GitHub account or organization settings:
-
Go to Settings → Developer settings → OAuth Apps → New OAuth App
-
Fill in the following:
- Homepage URL: {backstage-url}
- Authorization callback URL: http://{backstage-url}:7007/api/auth/github/handler/frame
-
Click Register application
-
Copy the Client ID and Client Secret
Configure GitHub Provider in app-config.yaml #
Add the provider configuration under auth.providers:
auth:
environment: development
providers:
github:
development:
clientId: ${GITHUB_CLIENT_ID}
clientSecret: ${GITHUB_CLIENT_SECRET}
signIn:
resolvers:
- resolver: usernameMatchingUserEntityName
Then set the environment variables in your shell or .env file:
export GITHUB_CLIENT_ID=your-client-id
export GITHUB_CLIENT_SECRET=your-client-secret
Enable Sign-In in the Frontend #
Open packages/app/src/App.tsx and make sure you have the SignInPage configured with the GitHub provider:
import { SignInPage } from '@backstage/core-components';
//Github SSO
import { githubAuthApiRef } from '@backstage/core-plugin-api';
const githubProvider = {
id: 'github', // The id should match your app-config.yaml auth.providers.
title: 'GitHub',
message: 'Sign in using GitHub',
apiRef: githubAuthApiRef,
};
const app = createApp({
components: {
SignInPage: props => (
<SignInPage
{...props}
auto
provider={githubProvider}
/>
),
},
});
You can comment out the guest login which is set by default from SignIn Page component
// components: {
// SignInPage: props => <SignInPage {...props} auto providers={['guest']} />,
// },
Start your Backstage app (yarn dev), you’ll be able to sign in using your GitHub account.
Verify the configuration #
- Click Sign in with GitHub
- Authorize the app
- You should be redirected back to Backstage as an authenticated user.
Backstage will create an identity for your GitHub account, which can be used for ownership mapping in the catalog, scaffolder, and other plugins.
Service-Based Authentication with API Keys #
While OAuth works great for users, machine-to-machine communication requires a API key for integration.
Here are some of the use cases:
- Calling Backstage’s Scaffolder or Catalog APIs from CI/CD pipelines
- Allowing automation scripts to trigger actions in Backstage
- Integrating external systems that don’t support OAuth
Configure Backend Auth Keys #
In your app-config.yaml, add a static key under backend.auth:
backend:
auth:
keys:
- secret: ${BACKSTAGE_SERVICE_KEY}
id: "ap-key"
description: "service api key"
Then export the secret or store it in .env file:
export BACKSTAGE_SERVICE_KEY=$(openssl rand -base64 32)
Make an Authenticated API Request #
Once configured, you can call any Backstage backend API using your key:
curl -H "Authorization: Bearer $BACKSTAGE_SERVICE_KEY" \
http://localhost:7007/api/catalog/entities
curl -H "Authorization: Bearer ${BACKSTAGE_SECRET_KEY}" http://localhost:7007/api/catalog/entities
[
{
"metadata": {
"namespace": "default",
"annotations": {
"backstage.io/managed-by-location": "url:https://github.com/Vinay-Venkatesh/blog-backend/tree/main/catalog-info.yaml",
"backstage.io/managed-by-origin-location": "url:https://github.com/Vinay-Venkatesh/blog-backend/blob/main/**/catalog-info.yaml",
"backstage.io/view-url": "https://github.com/Vinay-Venkatesh/blog-backend/tree/main/catalog-info.yaml",
"backstage.io/edit-url": "https://github.com/Vinay-Venkatesh/blog-backend/edit/main/catalog-info.yaml",
"backstage.io/source-location": "url:https://github.com/Vinay-Venkatesh/blog-backend/tree/main/"
},
"name": "blog-service",
"description": "blogging service",
"tags": [
"python",
"fastapi"
],
"links": [
{
"url": "https://thequietkernel.com",
"title": "Tech Blogging site written in go.."
}
],
"uid": "4274a8dc-d565-4d45-8df6-5b61ad8ed95b",
"etag": "89fb9316df80c8666714c99401503f3aab46cc6d"
},
"apiVersion": "backstage.io/v1beta1",
"kind": "Component",
"spec": {
"type": "service",
"lifecycle": "development",
"owner": "vinayv"
},
"relations": [
{
"type": "ownedBy",
"targetRef": "group:default/ml-platform-engineering",
"target": {
"kind": "group",
"namespace": "default",
"name": "ml-platform-engineering"
}
}
]
},
...
## rest of the output
...
]
Verify Tokens in Custom Backend Plugins #
If you’re building a backend plugin that should only allow requests from trusted services, use the IdentityClient:
import { IdentityClient } from '@backstage/plugin-auth-node';
const identityClient = IdentityClient.create({ discovery: discoveryApi });
app.use(async (req, res, next) => {
try {
const token = req.headers.authorization?.split(' ')[1];
await identityClient.verifyToken(token);
next();
} catch {
res.status(401).json({ error: 'Unauthorized' });
}
});
Authentication in Backstage is designed to be flexible — enabling human users to authenticate securely via OAuth and automated systems to authenticate via API keys.
By combining both:
- Developers get a seamless sign-in experience through GitHub.
- Services and automation pipelines can integrate securely using signed API tokens.