Files
nuxt-frontend/docs/GUIA_VALIDACAO_JWT_SERVICOS_CONSUMIDORES.md
2026-04-14 19:44:21 -05:00

135 lines
3.3 KiB
Markdown

# Guia didatico: validar JWT nos servicos consumidores (sem JWKS)
Este guia mostra a forma mais simples de integrar os servicos:
1. Auth assina o token com `JWT_PRIVATE_KEY_PEM`
2. cada servico consumidor valida localmente com `JWT_PUBLIC_KEY_PEM`
## 1) Configuracao em cada servico consumidor
Cada servico precisa ter no `.env`:
```env
JWT_ISSUER="https://auth.local"
JWT_AUDIENCE="internal-apis"
JWT_PUBLIC_KEY_PEM="-----BEGIN PUBLIC KEY-----\n...\n-----END PUBLIC KEY-----"
```
Importante:
- o `JWT_PUBLIC_KEY_PEM` deve ser exatamente a chave publica do Auth
- mantenha os `\n` literais no `.env`
## 2) O que validar
Para toda rota protegida:
1. ler `Authorization`
2. extrair `Bearer <token>`
3. validar assinatura RS256 com `JWT_PUBLIC_KEY_PEM`
4. validar `iss`, `aud` e `exp`
5. validar se `sub` existe
6. montar `request.auth = { id: sub, token }`
Se algo falhar, retornar `401`.
## 3) Exemplo pratico (Node/Express + jose)
```ts
import express from 'express'
import { importSPKI, jwtVerify } from 'jose'
const app = express()
const ISSUER = process.env.JWT_ISSUER!
const AUDIENCE = process.env.JWT_AUDIENCE!
const PUBLIC_KEY_PEM = process.env.JWT_PUBLIC_KEY_PEM!.replace(/\\n/g, '\n')
let publicKeyPromise: ReturnType<typeof importSPKI> | null = null
function getPublicKey() {
if (!publicKeyPromise) {
publicKeyPromise = importSPKI(PUBLIC_KEY_PEM, 'RS256')
}
return publicKeyPromise
}
function authMiddleware() {
return async (req, res, next) => {
try {
const authHeader = req.header('authorization')
if (!authHeader) {
return res.status(401).json({ message: 'Missing Authorization header' })
}
const [scheme, token] = authHeader.split(' ')
if (!scheme || !token || scheme.toLowerCase() !== 'bearer') {
return res.status(401).json({ message: 'Authorization must be Bearer token' })
}
const publicKey = await getPublicKey()
const { payload } = await jwtVerify(token, publicKey, {
issuer: ISSUER,
audience: AUDIENCE,
algorithms: ['RS256']
})
if (typeof payload.sub !== 'string' || !payload.sub.trim()) {
return res.status(401).json({ message: 'Invalid token claims' })
}
req.auth = {
id: payload.sub,
token
}
return next()
} catch {
return res.status(401).json({ message: 'Invalid or expired access token' })
}
}
}
app.get('/profile/me', authMiddleware(), (req, res) => {
return res.json({ userId: req.auth.id })
})
```
## 4) Propagacao entre servicos (A -> B)
Quando API A chamar API B em nome do usuario:
- repasse o mesmo `Authorization: Bearer <token>`
- API B valida esse token com `JWT_PUBLIC_KEY_PEM`
- API B usa `sub` como identidade confiavel
```ts
const authHeader = req.header('authorization')
const response = await fetch('http://courses-service/courses/me', {
headers: { Authorization: authHeader! }
})
```
## 5) Checklist rapido
- todos os servicos com o mesmo `JWT_PUBLIC_KEY_PEM`
- middleware valida assinatura + `iss` + `aud` + `exp`
- `sub` obrigatorio
- retorna `401` para token ausente/invalido/expirado
- usa `sub` como ID confiavel
## 6) Trade-off dessa abordagem
Vantagem:
- muito simples para implementar e explicar
Desvantagem:
- quando trocar a chave publica, precisa atualizar o `.env` de todos os servicos consumidores