|
| 1 | +# PAV-5: Google OAuth Integration (Frontend + Backend Fix) |
| 2 | + |
| 3 | +## Objetivo |
| 4 | + |
| 5 | +Integrar o botao "Entrar com Google" no frontend com o backend OAuth ja implementado, usando fluxo Full Redirect. |
| 6 | + |
| 7 | +## Contexto |
| 8 | + |
| 9 | +- Backend tem endpoints `GET /api/auth/google/url` e `GET /api/auth/google/callback` implementados |
| 10 | +- Frontend tem botao Google renderizado em `RigthSide.tsx` e `RegisterSide.tsx` sem onClick |
| 11 | +- O callback do backend retorna JSON em vez de redirecionar — precisa ser corrigido |
| 12 | +- O callback do backend nao persiste `userId` na sessao — precisa ser corrigido |
| 13 | + |
| 14 | +## Fluxo |
| 15 | + |
| 16 | +``` |
| 17 | +1. Usuario clica botao Google |
| 18 | +2. Frontend chama GET /api/auth/google/url |
| 19 | +3. Frontend redireciona o navegador para a URL retornada (Google consent) |
| 20 | +4. Google autentica e redireciona para GET /api/auth/google/callback?code=...&state=... |
| 21 | +5. Backend valida state, troca code por tokens, cria/encontra usuario |
| 22 | +6. Backend salva session.userId no cookie |
| 23 | +7. Backend redireciona para FRONTEND_URL/auth/callback |
| 24 | +8. Frontend (pagina /auth/callback) chama GET /api/auth/me |
| 25 | +9. AuthContext recebe o usuario, navega para /app |
| 26 | +``` |
| 27 | + |
| 28 | +Em caso de erro no passo 5-6, backend redireciona para `FRONTEND_URL/login?error=oauth_failed`. |
| 29 | + |
| 30 | +## Mudancas |
| 31 | + |
| 32 | +### Backend |
| 33 | + |
| 34 | +#### 1. Nova variavel de ambiente `FRONTEND_URL` |
| 35 | + |
| 36 | +- Adicionar em `.env.example`: `FRONTEND_URL=http://localhost:5173` |
| 37 | +- Usada exclusivamente para o redirect pos-OAuth |
| 38 | + |
| 39 | +#### 2. `auth.controller.ts` — metodo `callback` |
| 40 | + |
| 41 | +Antes (atual): |
| 42 | +```ts |
| 43 | +const result = await this.authService.handleCallback({...}); |
| 44 | +return res.json(result); |
| 45 | +``` |
| 46 | + |
| 47 | +Depois: |
| 48 | +```ts |
| 49 | +const result = await this.authService.handleCallback({...}); |
| 50 | +session.userId = result.session.userId; |
| 51 | +await session.save(); |
| 52 | +const frontendUrl = process.env.FRONTEND_URL || "http://localhost:5173"; |
| 53 | +return res.redirect(`${frontendUrl}/auth/callback`); |
| 54 | +``` |
| 55 | + |
| 56 | +Erro: |
| 57 | +```ts |
| 58 | +catch (error) { |
| 59 | + const frontendUrl = process.env.FRONTEND_URL || "http://localhost:5173"; |
| 60 | + return res.redirect(`${frontendUrl}/login?error=oauth_failed`); |
| 61 | +} |
| 62 | +``` |
| 63 | + |
| 64 | +### Frontend |
| 65 | + |
| 66 | +#### 3. `authService.ts` — nova funcao `getGoogleAuthUrl` |
| 67 | + |
| 68 | +```ts |
| 69 | +export async function getGoogleAuthUrl(): Promise<string> { |
| 70 | + const response = await fetch(buildUrl("/api/auth/google/url"), { |
| 71 | + credentials: "include", |
| 72 | + }); |
| 73 | + const payload = await parseResponse(response); |
| 74 | + if (!response.ok) { |
| 75 | + throw createError(payload, "Falha ao obter URL de autenticacao Google."); |
| 76 | + } |
| 77 | + return payload.url; |
| 78 | +} |
| 79 | +``` |
| 80 | + |
| 81 | +#### 4. `RigthSide.tsx` e `RegisterSide.tsx` — onClick no botao Google |
| 82 | + |
| 83 | +Adicionar handler que: |
| 84 | +1. Chama `getGoogleAuthUrl()` |
| 85 | +2. Redireciona com `window.location.href = url` |
| 86 | + |
| 87 | +#### 5. Nova pagina `AuthCallback.tsx` |
| 88 | + |
| 89 | +Componente em `/auth/callback` que: |
| 90 | +1. Mostra loading spinner |
| 91 | +2. Chama `/auth/me` via AuthContext (que ja faz isso no mount) |
| 92 | +3. Se usuario autenticado, navega para `/app` |
| 93 | +4. Se falha, navega para `/login` com mensagem de erro |
| 94 | + |
| 95 | +#### 6. `App.tsx` — nova rota |
| 96 | + |
| 97 | +```tsx |
| 98 | +<Route path="/auth/callback" element={<AuthCallback />} /> |
| 99 | +``` |
| 100 | + |
| 101 | +## Criterios de aceite |
| 102 | + |
| 103 | +- Login Google funciona end-to-end (clicar botao -> autenticar -> chegar no /app) |
| 104 | +- Conta criada automaticamente quando nao existe |
| 105 | +- Usuario fica autenticado no app apos login social (cookie de sessao persistido) |
| 106 | +- Erros no OAuth redirecionam para /login com indicacao visual de falha |
| 107 | +- Funciona tanto na pagina de login quanto na de registro |
| 108 | + |
| 109 | +## Fora de escopo |
| 110 | + |
| 111 | +- Login com Facebook ou Apple (botoes existem mas nao serao integrados agora) |
| 112 | +- Refresh de tokens OAuth |
| 113 | +- Linking de contas (usuario ja logado conectar Google) |
0 commit comments