Na reta final, só passa quem tem credencial. OAuth 2.0, OIDC e JWT
são os portões da segurança digital — saber abri-los é chegar primeiro.
5.1 — O Portão da Corrida: OAuth 2.0
Imagina a largada: o fiscal pede sua credencial, você apresenta sua identidade,
ele emite um crachá temporário. Esse crachá é o access token.
🔑 Fluxo Authorization Code
- Usuário clica em "Entrar com Google"
- App redireciona para o provedor com
client_id + scope
- Usuário autentica e autoriza
- Provedor retorna um
authorization_code
- App troca o código por
access_token + id_token
- App usa o token para acessar recursos protegidos
import urllib.parse, secrets
CLIENT_ID = "circuito_ferradura_app"
REDIRECT_URI = "http://localhost:8080/callback"
SCOPE = "openid profile email"
state = secrets.token_urlsafe(16)
params = {
"response_type": "code",
"client_id": CLIENT_ID,
"redirect_uri": REDIRECT_URI,
"scope": SCOPE,
"state": state,
}
url = "https://accounts.google.com/o/oauth2/auth?" + urllib.parse.urlencode(params)
print(f"→ Redirecionar para:\n{url}")
5.2 — O Número do Peito: JWT
Cada piloto tem um número no peito — único e verificável. O JWT funciona assim:
três partes separadas por ponto, codificadas em base64url.
import base64, json
def decodificar_jwt(token: str) -> dict:
partes = token.split(".")
def decode_b64(s):
padding = 4 - len(s) % 4
return json.loads(base64.urlsafe_b64decode(s + "=" * padding))
header = decode_b64(partes[0])
payload = decode_b64(partes[1])
return {"header": header, "payload": payload, "assinatura": partes[2][:20] + "..."}
from jose import jwt
claims = jwt.decode(token, chave_publica, algorithms=["RS256"], audience=CLIENT_ID)
print(f"Bem-vindo, {claims['name']}!")
5.3 — OIDC: A Identidade sobre o OAuth
OAuth diz "você pode acessar". OIDC responde "mas quem é você?".
O id_token contém as claims de identidade verificáveis.
id_token_payload = {
"iss": "https://accounts.google.com",
"sub": "103456789012345678901",
"aud": CLIENT_ID,
"exp": 1702934400,
"iat": 1702930800,
"name": "Ciclano da Corrida",
"email": "ciclano@ferradura.dev",
"email_verified": True,
}
def piloto_autenticado(claims: dict) -> bool:
import time
return claims.get("email_verified") and claims["exp"] > time.time()