💻 Exemplos: Capítulo 10: OAuth2 com Password Flow e Geração de Tokens JWT
Especialização em Backend com Python & FastAPI • Demonstrações Práticas Compiláveis
💻 Exemplo 1 — Emissão e Decodificação de JWT com Claims Padrão (sub, exp, iat)
from datetime import datetime, timedelta, timezone
import jwt
SECRET_KEY = "chave-secreta-de-no-minimo-32-bytes-em-producao"
ALGORITHM = "HS256"
def criar_access_token(dados: dict, expira_em: timedelta = timedelta(minutes=15)) -> str:
payload = dados.copy()
agora = datetime.now(timezone.utc)
payload.update({"iat": agora, "exp": agora + expira_em})
return jwt.encode(payload, SECRET_KEY, algorithm=ALGORITHM)
def decodificar_token(token: str) -> dict:
return jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
token = criar_access_token({"sub": "user_101", "role": "EDITOR"})
print("Token gerado:", token[:40], "...")
payload = decodificar_token(token)
print("Payload decodificado:", payload)
try:
token_vencido = criar_access_token({"sub": "user_101"}, expira_em=timedelta(seconds=-1))
decodificar_token(token_vencido)
except jwt.ExpiredSignatureError:
print("Token expirado corretamente rejeitado.")
💻 Exemplo 2 — Rota de Login com OAuth2PasswordRequestForm e Proteção com OAuth2PasswordBearer
from fastapi import Depends, FastAPI, HTTPException, status
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from passlib.context import CryptContext
from pydantic import BaseModel
app = FastAPI()
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="auth/token")
BASE_USUARIOS = {
"admin@empresa.com": {
"email": "admin@empresa.com",
"senha_hash": pwd_context.hash("admin123"),
}
}
class TokenResponse(BaseModel):
access_token: str
token_type: str = "bearer"
@app.post("/auth/token", response_model=TokenResponse)
async def login(form_data: OAuth2PasswordRequestForm = Depends()):
usuario = BASE_USUARIOS.get(form_data.username)
if not usuario or not pwd_context.verify(form_data.password, usuario["senha_hash"]):
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Usuário ou senha incorretos",
headers={"WWW-Authenticate": "Bearer"},
)
token = criar_access_token({"sub": usuario["email"]})
return TokenResponse(access_token=token)
async def get_current_user(token: str = Depends(oauth2_scheme)) -> dict:
credenciais_invalidas = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Não foi possível validar as credenciais",
headers={"WWW-Authenticate": "Bearer"},
)
try:
payload = decodificar_token(token)
except jwt.PyJWTError:
raise credenciais_invalidas
email = payload.get("sub")
usuario = BASE_USUARIOS.get(email)
if usuario is None:
raise credenciais_invalidas
return usuario
@app.get("/perfil")
async def ler_perfil(usuario_atual: dict = Depends(get_current_user)):
return {"email": usuario_atual["email"]}
💻 Exemplo 3 — Par Access/Refresh Token com Blacklist de Revogação
from datetime import timedelta
import jwt
ACCESS_TOKEN_EXPIRE = timedelta(minutes=15)
REFRESH_TOKEN_EXPIRE = timedelta(days=7)
# Em produção isto seria uma chave no Redis com TTL igual à validade
# restante do token — aqui, um set em memória para fins didáticos.
blacklist_refresh_tokens: set[str] = set()
def gerar_par_de_tokens(usuario_id: str) -> dict:
access_token = criar_access_token({"sub": usuario_id, "type": "access"}, ACCESS_TOKEN_EXPIRE)
refresh_token = criar_access_token({"sub": usuario_id, "type": "refresh"}, REFRESH_TOKEN_EXPIRE)
return {"access_token": access_token, "refresh_token": refresh_token, "token_type": "bearer"}
def renovar_access_token(refresh_token: str) -> dict:
if refresh_token in blacklist_refresh_tokens:
raise PermissionError("Refresh token revogado")
payload = decodificar_token(refresh_token)
if payload.get("type") != "refresh":
raise PermissionError("Token informado não é um refresh token")
novo_access_token = criar_access_token({"sub": payload["sub"], "type": "access"}, ACCESS_TOKEN_EXPIRE)
return {"access_token": novo_access_token, "token_type": "bearer"}
def logout(refresh_token: str) -> None:
blacklist_refresh_tokens.add(refresh_token)
par = gerar_par_de_tokens("user_101")
print("Par emitido:", {k: v[:20] + "..." for k, v in par.items() if k != "token_type"})
renovado = renovar_access_token(par["refresh_token"])
print("Access token renovado:", renovado["access_token"][:20], "...")
logout(par["refresh_token"])
try:
renovar_access_token(par["refresh_token"])
except PermissionError as erro:
print("Bloqueado após logout:", erro)