Autenticação
Documentação Auth Documentação Identity
Podemos observar que inicialmente temos um guest como provider. Esse provider faz com que todos os usuários compartilhem uma única identidade de guest. Somente é util para fins de testes e iniciar no Backstage, mas não para usar em produção. Por isso uma das primeiras coisas a se configurar é um provider ou mais de um.
Como provedor de autenticação temos os principais do mercado:
- Auth0
- Okta
- Github
- Gitlab
- Bitbucket
- Etc...
Geralmente o código da empresa esta em alguma conta do Github ou Gitlab. O login dentro dessas plataformas fazem SSO com o gerenciador de identidade que a empresa esta usando. Vamos implementar aqui o Github.
Essa configuração estará presente no arquivo app-config.yaml. Abaixo o que temos default no código inicial.
auth:
providers:
guest: {}
Vamos configurar providers, o Github, mas podemos usar vários.
Para isso é necessário na conta do Github criar o provedor de autenticação. Isso é feito em Settings > Developer settings > OAuth Apps.
Como podemos ter diferentes configs para diferentes ambientes, podemos criar mais de um também no Github. Inicialmente vamos criar para o desenvolvimento local.
- Homepage URL: É o url da página inicial, ou seja, de onde isso vem
- Authorization callback URL: É para onde serão enviadas as informações de retorno.
Seguindo a própria documentação para um desenvolvimento local eles nos mostram essas urls.

Criado já teremos o nosso Client ID que precisamos salvar pois será usado e já criar no botão generate a new client secret para criar a secret.
Exporte essas variaveis de ambiente no seu terminal. Usaremos elas mais tarde para desenvolvimento da imagem.
export AUTH_GITHUB_CLIENT_ID=xxxxxxxxxxxxxxxx
export AUTH_GITHUB_CLIENT_SECRET=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Se quiser podemos criar também no GitLab. Devemos criar uma aplicação e definir a página de retorno. É necessário definir as seguintes permissões:
- api
- read_user
- read_repository
- write_repository
- openid
- profile

Teremos então o application id que será o nosso AUTH_GITLAB_CLIENT_ID e a secret que será o nosso AUTH_GITLAB_CLIENT_SECRET e precisaremos também exportar.
export AUTH_GITLAB_CLIENT_ID=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
export AUTH_GITLAB_CLIENT_SECRET=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
auth:
environment: development
providers:
# guest: {}
github:
development:
clientId: ${AUTH_GITHUB_CLIENT_ID}
clientSecret: ${AUTH_GITHUB_CLIENT_SECRET}
signIn:
resolvers:
- resolver: usernameMatchingUserEntityName
gitlab:
development:
clientId: ${AUTH_GITLAB_CLIENT_ID}
clientSecret: ${AUTH_GITLAB_CLIENT_SECRET}
signIn:
resolvers:
- resolver: usernameMatchingUserEntityName
O resolver é o modelo de informação que o provedor devolverá baseado no login executado. A lista de resolvers disponíveis é diferente para cada provedor, já que eles frequentemente dependem do modelo de informação retornado do serviço do provedor upstream.
É necessário consultar o resolver em cada um dos provedores que deseja implementar para ver o que faz mais sentido.
No caso do GitHub e do GitLab o valor é o mesmo que ele trará de volta.
emailMatchingUserEntityProfileEmail: Corresponde o endereço de e-mail do provedor de autenticação com a entidade User que tem um spec.profile.email.emailLocalPartMatchingUserEntityName: Corresponde a parte local do endereço de e-mail do provedor de autenticação com a entidade User que tem um name.usernameMatchingUserEntityName: Corresponde o nome de usuário do provedor de autenticação com a entidade User que tem um name
Usando o usernameMatchingUserEntityName estamos recebendo o username e não o endereço de email.
Vale lembrar que usar o emailLocalPartMatchingUserEntityName trará por exemplo somante a parte antes do @domain.com. Nesse caso é bom definir quais os domínios serão aceitos.
Como não é só configurar, precisa também usar. Vamos adicionar isso package do app backend.
yarn --cwd packages/backend add @backstage/plugin-auth-backend-module-gitlab-provider
yarn --cwd packages/backend add @backstage/plugin-auth-backend-module-github-provider
yarn install
Agora vamos fazer carregar esses plugins.
Todo plugin do backend deve ser adicionado em packages/backend/src/index.ts antes da linha backend.start().
// auth plugin
backend.add(import('@backstage/plugin-auth-backend'));
// See https://backstage.io/docs/backend-system/building-backends/migrating#the-auth-plugin
// backend.add(import('@backstage/plugin-auth-backend-module-guest-provider'));
backend.add(import('@backstage/plugin-auth-backend-module-github-provider'));
backend.add(import('@backstage/plugin-auth-backend-module-gitlab-provider'));
Seguinte a própria documentação
Agora precisamos no App, no arquivo precisamos fazer alguns ajustes.
// add
import { githubAuthApiRef, gitlabAuthApiRef } from '@backstage/core-plugin-api';
//...
components: {
// comentamos a linha que utiliza o guest, e colocamos os novos.
// SignInPage: props => <SignInPage {...props} auto providers={['guest']} />,
SignInPage: props => (
<SignInPage
{...props}
providers={[
// 'guest',
{
id: 'github-auth-provider',
title: 'GitHub',
message: 'Sign in using GitHub',
apiRef: githubAuthApiRef,
},
{
id: 'gitlab-auth-provider',
title: 'GitLab',
message: 'Sign in using GitLab',
apiRef: gitlabAuthApiRef ,
},
]}
/>
),
},
Reiniciando a aplicação novamente com yarn dev e fazendo o login utilizando um desses provedores, recebemos o seguinte.
Login failed; caused by Error: Failed to sign-in, unable to resolve user identity. Please verify that your catalog contains the expected User entities that would match your configured sign-in resolver.

Se você quiser usar um provedor de autenticação para fazer login de usuários, você precisa configurá-lo explicitamente para ter o login habilitado e também dizer a ele como as identidades externas devem ser mapeadas para identidades de usuários dentro do Backstage.
O que aconteceu é que o resolver devolveu uma informação e este user Identity não existe no nosso catálogo, não criamos usuários dentro do sistema para conferir as informações e fazer o mapeamento. É necessário criar um resolver customizado que não precise fazer esse match por enquanto. No futuro, se você configurar para que o catálogo sincronize os usuários do seu SSO provedor será bom e poderemos retornar ao resolver que temos agora.
Voltando à situação anterior, o resolver usado em cada uma das contas foi usernameMatchingUserEntityName ficando mais claro o motivo do erro.
Backstage User Entity
Uma identidade de usuário dentro do Backstage é construída a partir de duas informações principais:
- Um user identity
- Propriedades de referência
Quando um usuário faz login, um token Backstage é gerado, que é então usado para identificar o usuário dentro do ecossistema Backstage. É encorajado que uma entidade de usuário correspondente também exista dentro do Catálogo de Software, como aconteceu acima, mas não é obrigatório. Se a entidade de usuário existir no catálogo, ela pode ser usada para armazenar dados adicionais sobre o usuário.
As referências de propriedade são usadas para determinar o que o usuário possui. Por exemplo, um usuário Jane ( user:default/jane) pode ter as referências de propriedade user:default/jane, group:default/team-a, e group:default/admins. Dadas essas reivindicações de propriedade, qualquer entidade que seja marcada como propriedade de qualquer um de user:jane, team-a, ou admins seria considerada propriedade de Jane. As referências podem ser usadas para resolver outras relações semelhantes à propriedade, como uma reivindicação para um maintainer.
No Backstage, os resolvers de autenticação mapeiam a identidade de um usuário autenticado através de um provedor externo (como GitHub, GitLab, etc.) para uma identidade interna do Backstage. Esse mapeamento é crucial porque define quem o usuário é e quais permissões ele terá na plataforma.
Ter múltiplos resolvers pode causar conflitos de identidade, especialmente se o mesmo usuário puder acessar de diferentes provedores, mas for tratado como identidades distintas no Backstage.
Imagine o seguinte cenário: Podemos fazer autenticação tanto do GitHub quanto do GitLab. Um usuário, digamos, David, tem contas em ambos os provedores:
- Conta GitHub: daviddevops
- Conta GitLab: daviddevsecops
Se o Backstage não estiver bem configurado para diferenciar as identidades entre os dois provedores, pode ocorrer o seguinte:
- O Backstage pode interpretar as duas contas (daviddevops e daviddevsecops) como dois usuários completamente diferentes, mesmo que seja a mesma pessoa. Isso cria duplicação de perfis, dificultando a gestão de permissões e atividades.
Observe que, embora seja possível configurar vários provedores de autenticação para serem usados para login, você deve tomar cuidado ao fazer isso. É melhor garantir que os diferentes provedores de autenticação não tenham sobreposição de usuários ou que todos os usuários que conseguem fazer login com vários provedores sempre acabem com a mesma identidade do Backstage. Para a maioria das organizações, faz mais sentido fornecer apenas um método de login. Neste caso, estamos habilitando GitHub e GitLab pois o meu usuário em ambas as plataformas é exatamente o mesmo nome, então deve ser mapeado para o mesmo user Identity.
Se a empresa tem um email corporativo no GitHub por exemplo, [email protected] usando o resolver emailLocalPartMatchingUserEntityName ele deveria fazer match com o fulano.tal eliminando o @empresa.com. Nada impede que [email protected] possa fazer o login mapeando para o mesmo usuário. Então garanta que somente os domínios específicos sejam utilizados na integração.
auth:
providers:
github:
development:
...
signIn:
resolvers:
- resolver: emailLocalPartMatchingUserEntityName
allowedDomains:
- empresa.com
Token Backstage que encapsula a identidade do usuário é um JWT.
Tenha cuidado ao configurar resolvedores de login, pois eles fazem parte da determinação de quem tem acesso à instância do Backstage e com qual identidade no sistema. Ter mais de um provedor de autenticação aumenta o risco de sequestro de conta.
Criando um Resolver Personalizado
Não temos usuário. Vamos permitir que usuários sejam criados com as contas que fizerem login no GitHub e GitLab. Será somente uma demonstração.
Em packages/backend/src/index.ts vamos adicionar um resolver personalizado, mas antes vamos remover o resolver do app-config.yaml, pois ele tentará usar o que ja foi definido.
auth:
environment: development
providers:
github:
development:
clientId: ${AUTH_GITHUB_CLIENT_ID}
clientSecret: ${AUTH_GITHUB_CLIENT_SECRET}
# signIn:
# resolvers:
# - resolver: usernameMatchingUserEntityName
gitlab:
development:
clientId: ${AUTH_GITLAB_CLIENT_ID}
clientSecret: ${AUTH_GITLAB_CLIENT_SECRET}
# signIn:
# resolvers:
# - resolver: usernameMatchingUserEntityName
Agora vamos customizar um. Como estou implantando em um ambiente controlado não vamos fazer nada demais, somente aceitar o que vier.
index.ts
import { githubAuthenticator } from '@backstage/plugin-auth-backend-module-github-provider'; //Mantemos os dois
import { gitlabAuthenticator } from '@backstage/plugin-auth-backend-module-gitlab-provider';
import { stringifyEntityRef } from '@backstage/catalog-model';
type AuthProviderId = 'github' | 'gitlab'; // Tipo específico para os providers para reaproveitamenteo de codigo
const customAuthResolver = createBackendModule({
pluginId: 'auth',
moduleId: 'custom-auth-provider',
register(reg) {
reg.registerInit({
deps: { providers: authProvidersExtensionPoint },
async init({ providers }) {
const authProviders: AuthProviderId[] = ['github', 'gitlab']; // Lista tipada
authProviders.forEach((providerId) => {
providers.registerProvider({
providerId,
factory: createOAuthProviderFactory({
authenticator: getAuthenticator(providerId),
async signInResolver(info, ctx) {
const { profile: { email } } = info;
if (!email) {
throw new Error('User profile contained no email');
}
const [userId, domain] = email.split('@');
if (domain !== 'gmail.com') { // Ajuste para o domínio correto
throw new Error(
`Login failed, '${email}' does not belong to the expected domain`,
);
}
// criando a user entity
const userEntity = stringifyEntityRef({
kind: 'User',
name: userId,
namespace: 'default',
});
// colocando o token
return ctx.issueToken({
claims: {
sub: userEntity,
ent: [userEntity],
},
});
},
}),
});
});
},
});
},
});
// Função auxiliar com tipo explícito
function getAuthenticator(providerId: AuthProviderId) {
switch (providerId) {
case 'github':
return githubAuthenticator;
case 'gitlab':
return gitlabAuthenticator; // Certifique-se de ter o autenticador do GitLab definido
default:
throw new Error(`No authenticator available for provider ${providerId}`);
}
}
// Neste ponte vamos remover os que tinhamos antes e usar o novo
backend.add(import('@backstage/plugin-auth-backend'));
// See https://backstage.io/docs/backend-system/building-backends/migrating#the-auth-plugin
// backend.add(import('@backstage/plugin-auth-backend-module-guest-provider'));
// backend.add(import('@backstage/plugin-auth-backend-module-github-provider'));
// backend.add(import('@backstage/plugin-auth-backend-module-gitlab-provider'));
backend.add(customAuthResolver);
Reinicie o sistema com yarn dev e faça o login. Uma coisa importante é que neste caso o seu email do GitHub e GitLab precisa estar visível no seu perfil.
Como previsto, ambos chegaram no mesmo lugar e usam o mesmo email.

Porém não foi mapeado para nenhuma entidade interna.

Logout Automático
Como mecanismo de segurança é interessante habilitar o logout automático que é projetado para desconectar usuários em caso de inatividade.
em packages/app/src/App.tsx podemos adicionar.
import { AutoLogout } from '@backstage/core-components';
// ... App.tsx contents
export default app.createRoot(
<>
// ...
<AutoLogout
idleTimeoutMinutes={30}
useWorkerTimers={false}
logoutIfDisconnected={false}
/>
// ...
</>,
);
Confira melhor sobre isso em auto logout.