Início rápido
Ligue o Servidor OAuth, registre um cliente e complete uma autorização de ponta a ponta.
Quinze minutos, e no fim um app de terceiro tem um token emitido pelo seu projeto.
Ligue o servidor
No Console, abra Servidor OAuth no ambiente em que você está trabalhando.
Três configurações decidem como o fluxo se comporta:
Habilitadodesligado por padrãoOptionalEnquanto estiver desligado, /oauth/authorize responde 403. Nada mais no seu
projeto é afetado.
URL da tela de consentimentopadrão: seu Site URLOptionalOnde a sua tela de consentimento está hospedada, se não for no seu Site URL. Deixe vazio e o Site URL é usado — o mesmo que carrega links de confirmação e e-mail de boas-vindas.
Caminho de autorização/oauth/consentOptionalO caminho da tela de consentimento, anexado à URL acima.
O Console mostra a URL de consentimento resultante abaixo do campo. Se ela não parecer uma página do seu app, o Site URL está errado.
Registre um cliente
Ainda em Servidor OAuth, adicione uma aplicação. Você precisa de um nome que o usuário reconheça na tela de consentimento e ao menos um redirect URI.
Nome Acme Analytics
Redirect URI https://acme.example.com/callbackRedirect URIs casam exatamente — esquema, host, porta e caminho.
https://acme.example.com/callback não casa com https://acme.example.com/callback/
nem com …/callback?x=1. É exigência do OAuth 2.1, e é o que impede um atacante
de redirecionar os códigos dos seus usuários para outro lugar.
Você recebe um client_id, e um client_secret se o app puder guardar um. O
segredo é mostrado uma única vez.
Construa a tela de consentimento
O cliente vai mandar os usuários para o seu Caminho de autorização. Essa página precisa existir antes do fluxo poder terminar — é onde o usuário vê o que está sendo pedido e diz sim.
São três chamadas: ler o pedido, renderizar e registrar a resposta.
const { data } = await nulvless.auth.oauth.getAuthorization(authorizationId);
// data.client.name, data.client.logoUri, data.scope, data.autoApproved
const { data: result } = await nulvless.auth.oauth.consent(authorizationId, 'approve');
redirect(result.redirect.uri);Construindo a tela de consentimento traz isso em Next.js, Nuxt, SvelteKit e Remix, com as partes fáceis de errar.
Mande um usuário pelo fluxo
Do lado do cliente, com PKCE:
const verifier = crypto.randomUUID() + crypto.randomUUID();
const challenge = base64url(await crypto.subtle.digest('SHA-256', new TextEncoder().encode(verifier)));
const url = new URL(`${issuer}/oauth/authorize`);
url.searchParams.set('client_id', clientId);
url.searchParams.set('redirect_uri', 'https://acme.example.com/callback');
url.searchParams.set('response_type', 'code');
url.searchParams.set('scope', 'openid email');
url.searchParams.set('state', state);
url.searchParams.set('code_challenge', challenge);
url.searchParams.set('code_challenge_method', 'S256');
location.assign(url.toString());Guarde verifier e state onde o callback consiga ler. O verifier é a prova de
que o app que termina o fluxo é o mesmo que o começou.
Troque o código
O usuário aprova, e sua tela de consentimento o devolve ao redirect URI com
?code=…&state=…. Confira que o state bate e troque o código:
const response = await fetch(`${issuer}/oauth/token`, {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({
grant_type: 'authorization_code',
code,
redirect_uri: 'https://acme.example.com/callback',
code_verifier: verifier,
client_id: clientId,
client_secret: clientSecret, // só clientes confidenciais
}),
});
const { access_token, refresh_token, id_token, expires_in } = await response.json();id_token só vem se você pediu o escopo openid. É ele que diz quem entrou.
Confirme que funcionou
Chame o endpoint de userinfo com o access token:
curl https://seu-projeto.nulvless.co/auth/oauth/userinfo \
-H "Authorization: Bearer $ACCESS_TOKEN"Você deve receber as claims cobertas pelos escopos que pediu. Se vier 401, o
token expirou ou foi emitido por outro ambiente.
Não pule o state
O state é o que impede um terceiro de induzir um usuário logado a completar a
autorização de outra pessoa. Custa uma comparação no seu callback. O fluxo
funciona sem ele, até o dia em que não funciona.