Skip to content

OAuth 2.1

Visão Geral

A Uappi expõe um Authorization Server OAuth 2.1 (authorization_code + PKCE obrigatório) para permitir que clients externos — como agentes de IA (ex.: Claude, via MCP) ou integrações de terceiros — ajam em nome de um usuário do painel sem que ele compartilhe email/senha com o client.

Diferente do login tradicional (email/senha, token de sessão), o fluxo OAuth envolve registro de client, redirecionamento do usuário para consentimento e troca de código por token — o padrão usado por qualquer provedor OAuth de mercado (Google, GitHub, etc.).

Quando usar

Use OAuth em vez do login tradicional quando o consumidor da API é um client de terceiros (não o próprio painel administrativo Uappi) que precisa operar em nome de um usuário específico, com escopo de permissão explicitamente concedido por esse usuário — por exemplo, o servidor MCP Uappi Staff Panel (user/mcp, ver documentação de MCP) aceita tanto um token de sessão do painel quanto um access token OAuth.

Fluxo completo

1. Registro do client — POST /oauth/user/register. Não exige autenticação prévia (RFC 7591, Dynamic Client Registration). Retorna client_id (e client_secret, se o client for confidencial).

2. Descoberta (opcional) — um client pode descobrir os endpoints do Authorization Server via /.well-known/oauth-authorization-server (RFC 8414) no domínio da loja, em vez de assumir os paths fixos abaixo.

3. Autorização — GET /oauth/user/authorize, com code_challenge/code_challenge_method=S256 (PKCE) obrigatórios. Redireciona (302) para a tela de consentimento do painel administrativo.

4. Consentimento — o usuário, já autenticado no painel, vê os escopos solicitados e decide quais concede (GET/POST /oauth/user/consent).

5. Emissão do token — POST /oauth/user/token, trocando o code (ou um refresh_token existente) por um access_token.

6. Uso do token — o access_token é enviado em Authorization: Bearer {access_token} nas chamadas subsequentes à API, exatamente como um token de sessão comum.

Escopos

Os escopos solicitados no authorize (parâmetro scope) são sempre restringidos pela interseção entre o que o client pediu e o que o perfil do usuário logado de fato permite — o usuário nunca consegue conceder mais acesso do que já possui. O access token resultante carrega essa lista de escopos, e cada operação da API continua reforçando o mesmo sistema de permissões usado no restante da plataforma (ver Escopos de Permissão).

Tempo de vida dos tokens

TokenDuração
access_token8 horas (mesmo TTL da sessão padrão do painel)
refresh_token6 meses
Um access_token expirado é renovado enviando o refresh_token para POST /oauth/user/token com grant_type=refresh_token, sem repetir a tela de consentimento.

Os endpoints de cada etapa estão detalhados abaixo: registro, autorização, consulta de consentimento, aprovação de consentimento e emissão/renovação de token.

Registrar Client OAuth

POST /oauth/user/register

Registra um novo client OAuth (RFC 7591 — Dynamic Client Registration)

Descrição

Registra um novo client OAuth para o Authorization Server de usuários do painel. Não exige autenticação prévia — é o primeiro passo de qualquer integração nova, seguindo o formato de resposta padrão da RFC 7591 (sem o envelope de resposta padrão da Uappi).

Parâmetros

Headers Obrigatórios

HeaderValorDescrição
Content-Typeapplication/jsonTipo de conteúdo

Parâmetros do Corpo

ParâmetroTipoObrigatórioDescrição
client_namestringNãoNome do client (máximo 255 caracteres). Se omitido, usa o host da primeira redirect_uri
redirect_urisarray de stringSimAo menos uma URL de redirecionamento válida
token_endpoint_auth_methodstringNãoclient_secret_post (client confidencial, padrão) ou none (client público, ex.: SPA/mobile)

Exemplo de Requisição

1curl --request POST \
2 --url {{MAIN_URL}}/oauth/user/register \
3 --header "Content-Type: application/json" \
4 --data '{
5 "client_name": "Meu Client OAuth",
6 "redirect_uris": ["https://meuapp.com/callback"]
7 }'

✅ Resposta de Sucesso

HTTP Status: 201 Created

1{
2 "client_id": "01a0620d-695b-709a-a4da-61c2c07417f0",
3 "client_secret": "diP7JT3ya3QrPc6twV9ifqQQvmmIXcPhjjxzXqO8",
4 "grant_types": ["authorization_code", "refresh_token"],
5 "response_types": ["code"],
6 "redirect_uris": ["https://meuapp.com/callback"],
7 "scope": "ORDER_READ ORDER_WRITE PRODUCT_READ ...",
8 "token_endpoint_auth_method": "client_secret_post"
9}

⚠️ client_secret só é retornado nesta resposta — guarde-o com segurança, ele não pode ser recuperado depois. Vem null quando token_endpoint_auth_method é none (client público).

⚠️ Possíveis erros:

Código InternoCódigo HttpDescrição
invalid_client_metadata400Algum campo do corpo é inválido (ex.: redirect_uris ausente ou não é uma URL válida).

Solicitar Autorização

GET /oauth/user/authorize

Inicia o fluxo de autorização OAuth, redirecionando para a tela de consentimento

Descrição

Valida o pedido de autorização (client, redirect_uri, PKCE) e redireciona (302) o navegador para a tela de consentimento do painel administrativo, carregando um continuationToken de curto prazo (10 minutos) que representa esse pedido.

PKCE (code_challenge + code_challenge_method=S256) é obrigatório para todo client — não há suporte a clients sem PKCE.

Parâmetros

Parâmetros de Query

ParâmetroTipoObrigatórioDescrição
response_typestringSimSempre code
client_idstringSimClient obtido no endpoint de registro
redirect_uristringSimDeve corresponder a uma das redirect_uris registradas do client
scopestringSimLista de escopos solicitados, separados por espaço (ex.: ORDER_READ PRODUCT_READ)
statestringNãoValor opaco devolvido inalterado ao final do fluxo, para prevenir CSRF
code_challengestringSimDesafio PKCE (SHA-256 do code_verifier, base64url sem padding)
code_challenge_methodstringSimSempre S256

Exemplo de Requisição

1curl --request GET \
2 --url "{{MAIN_URL}}/oauth/user/authorize?response_type=code&client_id=SEU_CLIENT_ID&redirect_uri=https://meuapp.com/callback&scope=ORDER_READ%20PRODUCT_READ&state=xyz&code_challenge=SEU_CODE_CHALLENGE&code_challenge_method=S256"

✅ Resposta de Sucesso

HTTP Status: 302 Found

1Location: https://loja.exemplo.com/uappi/oauth-authorize?continuationToken=eyJ0eXAiOiJKV1Qi...

O client deve seguir esse redirecionamento normalmente (é a tela de login/consentimento do painel, não uma resposta de API a ser tratada programaticamente).

⚠️ Possíveis erros:

Código InternoCódigo HttpDescrição
invalid_request302 (redirect para redirect_uri) ou 400client_id/redirect_uri desconhecidos, code_challenge ausente, ou outro parâmetro OAuth inválido (RFC 6749 §4.1.2.1)

Consultar Consentimento

GET /oauth/user/consent

Retorna os dados de consentimento (client, escopos) para a tela de autorização

Descrição

Usado pela tela de consentimento do painel administrativo para montar a interface — retorna o nome do client solicitante e a lista de escopos a exibir. Exige uma sessão de painel autenticada; se o usuário ainda não estiver logado, retorna UAPPI_API_OAUTH_SESSION_NOT_AUTHENTICATED para que a tela redirecione para o login e volte automaticamente.

Os escopos retornados já vêm restringidos pela interseção entre o que o client pediu (no authorize) e o que o perfil do usuário logado de fato permite — nunca é preciso filtrar escopo no client.

Parâmetros

Headers Obrigatórios

HeaderValorDescrição
AuthorizationBearer {token}Token de sessão do painel

Parâmetros de Query

ParâmetroTipoObrigatórioDescrição
continuationTokenstringSimToken retornado pelo redirecionamento do endpoint de autorização

Exemplo de Requisição

1curl --request GET \
2 --url "{{MAIN_URL}}/oauth/user/consent?continuationToken=SEU_CONTINUATION_TOKEN" \
3 --header "Authorization: Bearer SEU_TOKEN_DE_PAINEL"

✅ Resposta de Sucesso

1{
2 "status": "consent_required",
3 "continuationToken": "eyJ0eXAiOiJKV1Qi...",
4 "client": {
5 "id": "01a0620d-695b-709a-a4da-61c2c07417f0",
6 "name": "Meu Client OAuth"
7 },
8 "scopes": ["ORDER_READ", "PRODUCT_READ"],
9 "scopeDescriptions": {
10 "ORDER_READ": "Escopo de permissão para leitura de pedidos",
11 "PRODUCT_READ": "Escopo de permissão para leitura de produtos"
12 }
13}

⚠️ O continuationToken desta resposta já vem reemitido com a identidade da sessão autenticada — é esse token (não o original do authorize) que deve ser enviado para o endpoint de aprovação de consentimento.

⚠️ Possíveis erros:

Código InternoCódigo HttpDescrição
UAPPI_API_OAUTH_SESSION_NOT_AUTHENTICATED401Sessão do painel ainda não autenticada — redirecionar para o login.
UAPPI_API_OAUTH_AUTHORIZATION_REQUEST_INVALID401continuationToken inválido ou expirado (validade de 10 minutos).

Aprovar Consentimento

POST /oauth/user/consent

Aprova o pedido de autorização com os escopos selecionados pelo usuário

Descrição

Confirma o consentimento do usuário autenticado, gerando o código de autorização e retornando a URL de redirecionamento de volta para o client (redirect_uri original, com code e state). Só os escopos enviados em scopes e que o usuário de fato possui são concedidos — a interseção final é sempre recalculada no servidor.

Parâmetros

Headers Obrigatórios

HeaderValorDescrição
AuthorizationBearer {token}Token de sessão do painel
Content-Typeapplication/jsonTipo de conteúdo

Parâmetros do Corpo

ParâmetroTipoObrigatórioDescrição
continuationTokenstringSimToken retornado pelo endpoint de consulta de consentimento
scopesarray de stringNãoEscopos que o usuário optou por conceder. Omitido/vazio = nenhum escopo concedido

Exemplo de Requisição

1curl --request POST \
2 --url {{MAIN_URL}}/oauth/user/consent \
3 --header "Authorization: Bearer SEU_TOKEN_DE_PAINEL" \
4 --header "Content-Type: application/json" \
5 --data '{
6 "continuationToken": "eyJ0eXAiOiJKV1Qi...",
7 "scopes": ["ORDER_READ", "PRODUCT_READ"]
8 }'

✅ Resposta de Sucesso

1{
2 "redirectUri": "https://meuapp.com/callback?code=def50200...&state=xyz"
3}

O client (ou a tela que chamou este endpoint) deve navegar para redirectUri — o code ali contido é trocado por um token no endpoint de emissão de token.

⚠️ Possíveis erros:

Código InternoCódigo HttpDescrição
UAPPI_API_OAUTH_AUTHORIZATION_REQUEST_INVALID401continuationToken inválido, expirado, ou sem a identidade da sessão embutida.

Emitir/Renovar Token

POST /oauth/user/token

Troca um código de autorização (ou refresh token) por um access token

Descrição

Endpoint de protocolo puro (RFC 6749) — sem o envelope de resposta padrão da Uappi. Suporta dois grant_type:

  • authorization_code: troca o code obtido no endpoint de aprovação de consentimento por um access_token + refresh_token. Exige o code_verifier original (PKCE).
  • refresh_token: renova um access_token expirado usando um refresh_token válido, sem repetir a tela de consentimento.
  • Parâmetros

    Headers Obrigatórios

    HeaderValorDescrição
    Content-Typeapplication/x-www-form-urlencodedTipo de conteúdo

    Parâmetros do Corpo — grant_type=authorization_code

    ParâmetroTipoObrigatórioDescrição
    grant_typestringSimauthorization_code
    client_idstringSimClient obtido no endpoint de registro
    client_secretstringCondicionalObrigatório se o client for confidencial (token_endpoint_auth_method=client_secret_post)
    redirect_uristringSimMesma redirect_uri usada no endpoint de autorização
    codestringSimCódigo retornado em redirectUri pelo endpoint de aprovação de consentimento
    code_verifierstringSimVerificador PKCE original (usado para gerar o code_challenge do authorize)

    Parâmetros do Corpo — grant_type=refresh_token

    ParâmetroTipoObrigatórioDescrição
    grant_typestringSimrefresh_token
    client_idstringSimMesmo client que originou o token
    client_secretstringCondicionalObrigatório se o client for confidencial
    refresh_tokenstringSimRefresh token emitido junto do access token anterior

    Exemplo de Requisição

    1curl --request POST \
    2 --url {{MAIN_URL}}/oauth/user/token \
    3 --data "grant_type=authorization_code" \
    4 --data "client_id=SEU_CLIENT_ID" \
    5 --data "client_secret=SEU_CLIENT_SECRET" \
    6 --data "redirect_uri=https://meuapp.com/callback" \
    7 --data "code=SEU_CODE" \
    8 --data "code_verifier=SEU_CODE_VERIFIER"

    ✅ Resposta de Sucesso

    1{
    2 "token_type": "Bearer",
    3 "expires_in": 28800,
    4 "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9...",
    5 "refresh_token": "def50200..."
    6}

  • access_token: válido por 8 horas (expires_in em segundos).
  • refresh_token: válido por 6 meses.

⚠️ Possíveis erros:

Código InternoCódigo HttpDescrição
invalid_request400Parâmetro obrigatório ausente ou malformado (ex.: client_secret faltando).
invalid_grant400code/refresh_token inválido, expirado, já usado, ou code_verifier não corresponde ao code_challenge original.
invalid_client401client_id/client_secret inválidos.

Uappi é a melhor opção para o crescimento da sua loja!

;