Autenticação
OAuth2 client_credentials (RFC 6749 §4.4). As credenciais de longa duração são
trocadas por um access_token de curta duração, usado nas demais chamadas.
Endpoint
POST /oauth/token
Authorization: Basic base64(client_id:client_secret)
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials&scope=<escopos>
| Parâmetro | Obrigatório | Valor |
|---|---|---|
grant_type |
sim | client_credentials |
scope |
não | Escopos separados por espaço. Omitido, concede todos os do client_id. |
Resposta 200:
{
"access_token": "<jwt>",
"token_type": "Bearer",
"expires_in": 900,
"scope": "payments:write payments:read"
}
Não há refresh token. Expirado o access_token, repita a requisição acima.
Escopos
| Escopo | Permissão |
|---|---|
payments:write |
Criar e capturar cobranças |
payments:read |
Consultar cobranças |
refunds:write |
Cancelar e estornar |
webhooks:write |
Gerenciar endpoints de webhook |
Escopo requisitado além do concedido ao client_id retorna 400 invalid_scope.
Não há concessão parcial silenciosa.
Endpoints exigem escopo específico. Token sem o escopo exigido retorna
403 insufficient_scope, com o escopo faltante na mensagem.
Token
JWT assinado em ES256, curva P-256. Validade de 900 segundos.
{
"iss": "https://api-crpay.deskhotel.com.br",
"aud": "https://api-crpay.deskhotel.com.br",
"sub": "ten_01KYFJZ09JYEYH5QKK2CDAZC22",
"client_id": "test_794243d7b8541c078903cc4a80a6ccac",
"scope": "payments:write payments:read",
"env": "test",
"jti": "1f6a...",
"iat": 1785082124,
"exp": 1785083024
}
| Claim | Conteúdo |
|---|---|
sub |
Identificador do tenant |
env |
test ou live |
scope |
Escopos concedidos, separados por espaço |
jti |
Identificador único do token |
Validação da assinatura
Chaves públicas em GET /.well-known/jwks.json. Resposta cacheável por 300 segundos.
import { createRemoteJWKSet, jwtVerify } from 'jose';
const jwks = createRemoteJWKSet(
new URL('https://api-crpay.deskhotel.com.br/.well-known/jwks.json')
);
const { payload } = await jwtVerify(token, jwks, {
issuer: 'https://api-crpay.deskhotel.com.br',
audience: 'https://api-crpay.deskhotel.com.br',
algorithms: ['ES256']
});
Requisitos da implementação:
- Selecionar a chave pelo
kiddo cabeçalho do token. Durante rotação, o JWKS publica mais de uma chave simultaneamente. - Fixar
algorithms: ['ES256']. Não derivar o algoritmo do cabeçalho do token. - Validar
isseaud.
Ambientes
Determinados pelo prefixo do client_id, não por URL.
| Prefixo | Ambiente |
|---|---|
test_ |
Sandbox das adquirentes |
live_ |
Produção |
A separação é aplicada no servidor. Credencial test_ não acessa configuração live_.
Rotação do client_secret
O client_id é preservado. O segredo anterior é invalidado no ato. Tokens emitidos
com ele permanecem válidos até expirarem, no máximo 900 segundos.
Sequência sem indisponibilidade: solicitar a rotação, atualizar o segredo na aplicação, reiniciar.
Limites
POST /oauth/token: 10 requisições por minuto, por client_id. Excedido, retorna
429 com header Retry-After.
Reutilize o token em memória durante a validade. Uma requisição de token por chamada de API excede o limite.
Resposta a credencial inválida
client_id inexistente e client_secret incorreto retornam resposta idêntica, em
tempo equivalente:
{ "error": "invalid_client", "error_description": "Credenciais invalidas." }
Armazenamento das credenciais
client_secretem variável de ambiente ou cofre de segredos. Não versionar.- Um
client_idpor aplicação, para limitar o alcance de uma revogação. - Token em memória. Não persistir, não registrar em log.
- HTTPS obrigatório. Requisições em HTTP são redirecionadas.