JWT Authentication - Diagramas de Fluxo¶
🔄 Fluxo Completo: WebChat com JWT¶
sequenceDiagram
participant User as 👤 Usuário<br/>(Autenticado no Site)
participant Frontend as 🌐 Frontend<br/>(WebChat Widget)
participant Adapter as 📡 WebChatAdapter
participant Processor as ⚙️ ConversationProcessor
participant JWTMgr as 🔐 JWTContextManager
participant SessionSvc as 💾 SessionService
participant Runner as 🤖 ADK Runner
participant Tool as 🛠️ Tool (buscar_usuario)
participant API as 🌍 API iFriend
Note over User,Frontend: 1. Usuário Envia Mensagem
User->>Frontend: "Quero reservar uma tour em Roma"
Frontend->>Frontend: Obtém JWT do localStorage/cookie
Note over Frontend,Adapter: 2. Request com JWT
Frontend->>Adapter: POST /webchat/message<br/>Authorization: Bearer eyJhbG...
Note over Adapter,Processor: 3. Parse & Create IncomingMessage
Adapter->>Adapter: Extrai JWT do header/body
Adapter->>Adapter: Cria IncomingMessage<br/>metadata.jwt_token = JWT
Adapter->>Processor: process_message(IncomingMessage)
Note over Processor,JWTMgr: 4. Decode & Validate JWT
Processor->>JWTMgr: get_user_context(jwt_token)
JWTMgr->>JWTMgr: Decodifica JWT
JWTMgr->>JWTMgr: Valida assinatura
JWTMgr->>JWTMgr: Valida expiração
JWTMgr->>Processor: UserContext(user_id, email, roles, token)
Note over Processor,SessionSvc: 5. Injeta JWT na Sessão
Processor->>SessionSvc: set_jwt_context(session_id, jwt_context)
SessionSvc->>SessionSvc: session.metadata.jwt_context = {...}
SessionSvc->>Processor: OK
Processor->>Processor: Configura ContextVar<br/>current_session_context
Note over Processor,Runner: 6. Processa Mensagem
Processor->>Runner: run_async(session_id, user_id, message)
Runner->>Runner: Agente decide chamar tool
Note over Runner,Tool: 7. Tool Executa com JWT Context
Runner->>Tool: buscar_usuario_tool(email="user@example.com")
Tool->>Tool: ctx = current_session_context.get()
Tool->>SessionSvc: get_session_context(ctx)
SessionSvc->>Tool: session.metadata.jwt_context
Tool->>Tool: get_auth_headers(session_context)<br/>→ "Authorization: Bearer <JWT>"
Note over Tool,API: 8. Chamada Autenticada
Tool->>API: GET /users?userEmail=...<br/>Authorization: Bearer <JWT do usuário>
API->>API: Valida JWT
API->>API: Retorna dados do USUÁRIO REAL
API->>Tool: {user: {...}}
Note over Tool,User: 9. Resposta ao Usuário
Tool->>Runner: Resultado da tool
Runner->>Processor: Resposta do agente
Processor->>Adapter: Envia resposta
Adapter->>Frontend: JSON response
Frontend->>User: "Encontrei seu perfil! Vamos à reserva..."
🔀 Decisão: Usar JWT ou Auth Default?¶
flowchart TD
Start([Nova Mensagem<br/>do Usuário]) --> CheckJWT{JWT token<br/>presente no<br/>metadata?}
CheckJWT -->|Sim| ValidateJWT[Validar JWT]
CheckJWT -->|Não| UseDefault[Usar Auth Default]
ValidateJWT --> IsValid{JWT válido<br/>e não expirado?}
IsValid -->|Sim| DecodeJWT[Decodificar JWT<br/>Extrair claims]
IsValid -->|Não| LogWarning[⚠️ Log: JWT inválido]
LogWarning --> UseDefault
DecodeJWT --> InjectContext[Injetar JWT Context<br/>na Sessão ADK]
InjectContext --> ProcessMsg[Processar Mensagem<br/>com Context JWT]
UseDefault --> LogDefault[ℹ️ Log: Usando auth default]
LogDefault --> ProcessMsg2[Processar Mensagem<br/>com Auth Default]
ProcessMsg --> ToolExec[Tool Execution]
ProcessMsg2 --> ToolExec
ToolExec --> CheckContext{Tem JWT<br/>no session<br/>context?}
CheckContext -->|Sim| UseJWTAuth[get_auth_headers<br/>usa JWT]
CheckContext -->|Não| UseDefaultAuth[get_auth_headers<br/>autentica c/ API_EMAIL/PASSWORD]
UseJWTAuth --> CallAPI[Chamada API<br/>c/ credenciais do usuário]
UseDefaultAuth --> CallAPI2[Chamada API<br/>c/ credenciais default]
CallAPI --> Success([✅ Resposta<br/>personalizada])
CallAPI2 --> Success2([✅ Resposta<br/>genérica])
style Start fill:#e1f5fe
style Success fill:#c8e6c9
style Success2 fill:#fff9c4
style LogWarning fill:#ffccbc
style DecodeJWT fill:#b2dfdb
style InjectContext fill:#b2dfdb
🏗️ Componentes da Arquitetura¶
graph TB
subgraph "Frontend"
WebUI[🌐 Web UI<br/>React/Vue/Angular]
Widget[💬 WebChat Widget]
Mobile[📱 Mobile App]
end
subgraph "Messaging Adapters"
WebChatAdapter[📡 WebChatAdapter]
SSEAdapter[⚡ SSEAdapter]
WhatsAppAdapter[💬 WhatsAppAdapter]
SlackAdapter[💼 SlackAdapter]
end
subgraph "JWT Processing Layer"
JWTManager[🔐 JWTContextManager<br/>Decode & Validate]
SessionHelper[💾 SessionContextHelper<br/>Store JWT Context]
ContextVar[🔄 ContextVar<br/>Thread-safe Context]
end
subgraph "ADK Core"
Processor[⚙️ ConversationProcessor]
Runner[🤖 ADK Runner]
SessionSvc[💾 Session Service<br/>Redis/CloudSQL]
MemorySvc[🧠 Memory Service]
end
subgraph "Tools & Auth"
AuthMgr[🔑 Enhanced Auth Manager]
BookingTools[🎫 Booking Tools]
PaymentTools[💳 Payment Tools]
SearchTools[🔍 Search Tools]
end
subgraph "External Services"
APIiFriend[🌍 API iFriend]
Redis[(Redis<br/>JWT Lookup Cache)]
end
WebUI -->|JWT in header| WebChatAdapter
Widget -->|JWT in header| WebChatAdapter
Widget -->|JWT in header| SSEAdapter
Mobile -->|Phone → lookup| WhatsAppAdapter
WebChatAdapter --> Processor
SSEAdapter --> Processor
WhatsAppAdapter -->|Lookup JWT| Redis
SlackAdapter -->|Lookup JWT| Redis
Redis --> WhatsAppAdapter
Redis --> SlackAdapter
WhatsAppAdapter --> Processor
SlackAdapter --> Processor
Processor --> JWTManager
JWTManager --> SessionHelper
SessionHelper --> SessionSvc
Processor --> ContextVar
Processor --> Runner
Runner --> SessionSvc
Runner --> MemorySvc
Runner --> BookingTools
Runner --> PaymentTools
Runner --> SearchTools
BookingTools --> ContextVar
PaymentTools --> ContextVar
SearchTools --> ContextVar
BookingTools --> AuthMgr
PaymentTools --> AuthMgr
SearchTools --> AuthMgr
AuthMgr -->|JWT ou Default| APIiFriend
style JWTManager fill:#ffecb3
style SessionHelper fill:#ffecb3
style ContextVar fill:#ffecb3
style AuthMgr fill:#c5e1a5
style Redis fill:#b3e5fc
📱 Fluxo WhatsApp: Autenticação One-Time¶
sequenceDiagram
participant User as 👤 Usuário
participant WebUI as 🌐 Site iFriend
participant Backend as 🖥️ Backend
participant Redis as 💾 Redis
participant WhatsApp as 💬 WhatsApp
participant Adapter as 📡 WhatsAppAdapter
Note over User,WebUI: Passo 1: Vinculação One-Time
User->>WebUI: Login no site
WebUI->>User: JWT token
User->>WebUI: "Vincular WhatsApp"<br/>+5511999999999
WebUI->>Backend: POST /whatsapp/link<br/>Authorization: Bearer <JWT><br/>{phone: "+5511999999999"}
Backend->>Backend: Valida JWT
Backend->>Redis: SET whatsapp:jwt:+5511999999999<br/>= JWT token<br/>EX 2592000 (30 dias)
Redis->>Backend: OK
Backend->>WebUI: ✅ WhatsApp vinculado
WebUI->>User: "Agora você pode usar o WhatsApp!"
Note over User,Adapter: Passo 2: Conversas Subsequentes (automático)
User->>WhatsApp: "Quero reservar uma tour"
WhatsApp->>Adapter: Webhook: new message
Adapter->>Adapter: Extrai phone: +5511999999999
Adapter->>Redis: GET whatsapp:jwt:+5511999999999
Redis->>Adapter: JWT token
Adapter->>Adapter: Adiciona JWT ao metadata
Adapter->>Backend: process_message()<br/>metadata.jwt_token = JWT
Note over Backend: Processa normalmente<br/>com autenticação do usuário
Backend->>Adapter: Resposta
Adapter->>WhatsApp: Send message
WhatsApp->>User: "Ótimo! Vamos reservar..."
🔐 JWT Validation Flow¶
flowchart TD
Start([JWT Token<br/>Recebido]) --> Parse[Parse JWT<br/>Header.Payload.Signature]
Parse --> CheckFormat{Formato<br/>válido?}
CheckFormat -->|Não| Error1[❌ JWT Malformed]
CheckFormat -->|Sim| DecodeHeader[Decodifica Header<br/>alg, typ]
DecodeHeader --> CheckAlg{Algoritmo<br/>suportado?}
CheckAlg -->|Não| Error2[❌ Algorithm Not Allowed]
CheckAlg -->|Sim| VerifySignature[Verifica Assinatura<br/>usando JWT_SECRET_KEY]
VerifySignature --> SigValid{Assinatura<br/>válida?}
SigValid -->|Não| Error3[❌ Invalid Signature]
SigValid -->|Sim| DecodePayload[Decodifica Payload<br/>claims]
DecodePayload --> CheckRequired{Claims<br/>obrigatórios<br/>presentes?}
CheckRequired -->|Não| Error4[❌ Missing Required Claims<br/>sub, email, exp]
CheckRequired -->|Sim| CheckExp{exp claim<br/>não expirado?}
CheckExp -->|Expirado| Error5[❌ Token Expired]
CheckExp -->|Válido| CheckIssuer{iss claim<br/>correto?}
CheckIssuer -->|Não| Error6[❌ Invalid Issuer]
CheckIssuer -->|Sim| CheckAudience{aud claim<br/>correto?}
CheckAudience -->|Não| Error7[❌ Invalid Audience]
CheckAudience -->|Sim| Success([✅ JWT Válido<br/>Extrai UserContext])
Error1 --> Fallback[⚠️ Fallback:<br/>Usar Auth Default]
Error2 --> Fallback
Error3 --> Fallback
Error4 --> Fallback
Error5 --> Fallback
Error6 --> Fallback
Error7 --> Fallback
Success --> InjectContext[Injeta Context<br/>na Sessão ADK]
Fallback --> ProcessDefault[Processa sem JWT<br/>Auth Default]
style Error1 fill:#ffcdd2
style Error2 fill:#ffcdd2
style Error3 fill:#ffcdd2
style Error4 fill:#ffcdd2
style Error5 fill:#ffcdd2
style Error6 fill:#ffcdd2
style Error7 fill:#ffcdd2
style Success fill:#c8e6c9
style Fallback fill:#fff9c4
🛠️ Tool Execution com JWT Context¶
flowchart TD
Start([Tool Chamada<br/>buscar_usuario_tool]) --> GetContext[Obtém ContextVar<br/>current_session_context]
GetContext --> HasContext{ContextVar<br/>presente?}
HasContext -->|Não| UseDefault1[⚠️ Sem contexto<br/>usa auth default]
HasContext -->|Sim| GetSession[Busca Session no<br/>SessionService]
GetSession --> SessionFound{Sessão<br/>encontrada?}
SessionFound -->|Não| UseDefault2[⚠️ Sessão não encontrada<br/>usa auth default]
SessionFound -->|Sim| CheckJWTContext{session.metadata<br/>tem jwt_context?}
CheckJWTContext -->|Não| UseDefault3[ℹ️ Sem JWT context<br/>usa auth default]
CheckJWTContext -->|Sim| ExtractJWT[Extrai JWT token<br/>do jwt_context]
ExtractJWT --> CheckExpiry{Token ainda<br/>válido?}
CheckExpiry -->|Expirado| UseDefault4[⚠️ Token expirado<br/>usa auth default]
CheckExpiry -->|Válido| UseJWT[✅ Usa JWT token<br/>Authorization: Bearer JWT]
UseDefault1 --> AuthDefault[get_auth_headers<br/>autentica com<br/>API_EMAIL/PASSWORD]
UseDefault2 --> AuthDefault
UseDefault3 --> AuthDefault
UseDefault4 --> AuthDefault
UseJWT --> AuthJWT[get_auth_headers<br/>passa JWT token]
AuthDefault --> GetToken[Autentica na API<br/>POST /authentication_token]
GetToken --> CallAPI1[GET /users<br/>Authorization: Bearer DEFAULT_TOKEN]
AuthJWT --> CallAPI2[GET /users<br/>Authorization: Bearer USER_JWT_TOKEN]
CallAPI1 --> Response1([Resposta com<br/>dados do usuário default])
CallAPI2 --> Response2([Resposta com<br/>dados do usuário real])
style UseDefault1 fill:#fff9c4
style UseDefault2 fill:#fff9c4
style UseDefault3 fill:#fff9c4
style UseDefault4 fill:#fff9c4
style UseJWT fill:#c8e6c9
style Response2 fill:#e1f5fe
🔄 Session Context Lifecycle¶
stateDiagram-v2
[*] --> NoSession: Primeira mensagem do usuário
NoSession --> SessionCreated: create_session()
SessionCreated --> NoJWT: Mensagem sem JWT
SessionCreated --> JWTReceived: Mensagem com JWT
NoJWT --> JWTReceived: Próxima mensagem com JWT
JWTReceived --> NoJWT: JWT expirou/inválido
state NoJWT {
[*] --> DefaultAuth
DefaultAuth --> ProcessingDefault: Processa com auth default
ProcessingDefault --> [*]
}
state JWTReceived {
[*] --> ValidateJWT
ValidateJWT --> InvalidJWT: Validação falhou
ValidateJWT --> ValidJWT: Validação OK
InvalidJWT --> FallbackDefault: Volta para auth default
ValidJWT --> InjectContext: set_jwt_context()
InjectContext --> ProcessingJWT: Processa com JWT
ProcessingJWT --> [*]
}
NoJWT --> SessionExpired: TTL expirado
JWTReceived --> SessionExpired: TTL expirado
SessionExpired --> [*]: Sessão deletada
📊 Performance & Scalability¶
graph LR
subgraph "Load Balancer"
LB[⚖️ Load Balancer]
end
subgraph "App Servers"
App1[🖥️ App Server 1]
App2[🖥️ App Server 2]
App3[🖥️ App Server 3]
end
subgraph "Session Layer"
Redis1[(Redis Primary<br/>Session + JWT Lookup)]
Redis2[(Redis Replica)]
end
subgraph "Memory Layer"
CloudSQL[(CloudSQL<br/>Memory Service)]
Firestore[(Firestore<br/>Backup)]
end
subgraph "External"
API[🌍 API iFriend]
end
LB --> App1
LB --> App2
LB --> App3
App1 --> Redis1
App2 --> Redis1
App3 --> Redis1
Redis1 -.Replication.-> Redis2
App1 --> CloudSQL
App2 --> CloudSQL
App3 --> CloudSQL
App1 -.Fallback.-> Firestore
App2 -.Fallback.-> Firestore
App3 -.Fallback.-> Firestore
App1 --> API
App2 --> API
App3 --> API
style Redis1 fill:#b3e5fc
style Redis2 fill:#b3e5fc
style CloudSQL fill:#c5cae9
style Firestore fill:#d1c4e9
Última atualização: 13 de fevereiro de 2026