PKCE (S256)
Como gerar o code_verifier e o code_challenge (SHA-256/BASE64URL) e onde enviá-los no fluxo OAuth do OrbitSender.
O PKCE (Proof Key for Code Exchange) é uma extensão do OAuth 2.0 que protege o código de autorização (orb_ac_...) contra interceptação. No OrbitSender usamos exclusivamente o método S256.
Por que o PKCE existe
No fluxo Authorization Code, o /authorize devolve um código temporário na URL de redirecionamento. Se esse código for interceptado, um atacante poderia tentar trocá-lo por um token de acesso.
O PKCE resolve isso amarrando o código a um segredo efêmero que só o seu app conhece:
- Antes de iniciar, o app gera um
code_verifier(segredo aleatório) e dele deriva umcode_challenge. - O
code_challengeviaja no/authorize; ocode_verifierfica guardado no app. - Na troca no
/token, o app envia ocode_verifieroriginal. O OrbitSender recalcula o challenge e só libera o token se bater.
Obrigatório para apps public; recomendado para confidential
Apps do tipo public (SEM client_secret) precisam usar PKCE — é a única proteção do código. Apps confidential (com client_secret) devem usar PKCE também, como camada extra além do secret.
Como o par é construído
| Elemento | Regra |
|---|---|
code_verifier | String aleatória de 43 a 128 caracteres. Gerada e guardada pelo app. |
code_challenge | BASE64URL( SHA256(code_verifier) ) |
code_challenge_method | Sempre S256 (único método aceito). |
O BASE64URL é o Base64 "URL-safe" sem padding (sem os = no final, com - e _ no lugar de + e /).
Gerando o par no seu código
Node.js
import crypto from "node:crypto";
// 1) code_verifier: aleatório, URL-safe, dentro de 43–128 chars
const codeVerifier = crypto.randomBytes(32).toString("base64url");
// 2) code_challenge = BASE64URL( SHA256(code_verifier) )
const codeChallenge = crypto
.createHash("sha256")
.update(codeVerifier)
.digest("base64url");
console.log({ codeVerifier, codeChallenge, codeChallengeMethod: "S256" });Python
import secrets
import hashlib
import base64
# 1) code_verifier: aleatório, URL-safe, dentro de 43–128 chars
code_verifier = base64.urlsafe_b64encode(secrets.token_bytes(32)).rstrip(b"=").decode()
# 2) code_challenge = BASE64URL( SHA256(code_verifier) )
digest = hashlib.sha256(code_verifier.encode()).digest()
code_challenge = base64.urlsafe_b64encode(digest).rstrip(b"=").decode()
print({"code_verifier": code_verifier, "code_challenge": code_challenge, "method": "S256"})Guarde o code_verifier
Você precisará do mesmo code_verifier na etapa de troca no /token. Gere-o e associe ao state da requisição (por exemplo, na sessão do usuário) para recuperá-lo quando o callback voltar.
Onde cada valor é enviado
No /authorize vão o challenge e o método
Ao mandar o usuário para a tela de consentimento, inclua nos query params:
GET https://app.orbitsender.com/oauth/authorize?response_type=code
&client_id=cli_abc123
&redirect_uri=https://seuapp.com/callback
&scope=statistics:read%20campaigns:read
&state=xyz123
&code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM
&code_challenge_method=S256Envie apenas code_challenge e code_challenge_method=S256. Nunca envie o code_verifier aqui.
No /token vai o code_verifier
Na troca do código pelo token, envie o code_verifier correspondente no corpo form-urlencoded:
curl -X POST https://api.orbitsender.com/api/oauth/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=authorization_code" \
-d "client_id=cli_abc123" \
-d "code=orb_ac_..." \
-d "redirect_uri=https://seuapp.com/callback" \
-d "code_verifier=SEU_CODE_VERIFIER_ORIGINAL"O OrbitSender recalcula BASE64URL(SHA256(code_verifier)) e compara com o code_challenge recebido no /authorize. Se não bater, a resposta é o erro invalid_grant.
Um par por fluxo
Gere um code_verifier/code_challenge novo a cada início de fluxo. O código (orb_ac_...) é de uso único e expira em 60 segundos.