- SAAS-015 OAuth: Specified -> Completed (already implemented) - SAAS-016 Analytics: Specified -> Completed (already implemented) - ET-SAAS-015: Proposed -> Implemented - ET-SAAS-016: Proposed -> Implemented Backend implementation verified: - OAuth: 799 lines (controller + service) - Analytics: 513 lines with full metrics Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
330 lines
8.0 KiB
Markdown
330 lines
8.0 KiB
Markdown
---
|
|
id: "SAAS-015"
|
|
title: "OAuth 2.0 Endpoints"
|
|
type: "Module"
|
|
status: "Completed"
|
|
priority: "P1"
|
|
module: "oauth"
|
|
version: "1.0.0"
|
|
created_date: "2026-01-24"
|
|
updated_date: "2026-01-24"
|
|
story_points: 5
|
|
---
|
|
|
|
# SAAS-015: OAuth 2.0 Endpoints
|
|
|
|
## Metadata
|
|
- **Codigo:** SAAS-015
|
|
- **Modulo:** OAuth (extension de Auth)
|
|
- **Prioridad:** P1
|
|
- **Estado:** Especificado
|
|
- **Fase:** 2 - Integraciones
|
|
- **Story Points:** 5
|
|
|
|
## Descripcion
|
|
|
|
Implementacion completa de endpoints OAuth 2.0 para autenticacion federada con multiples proveedores. Permite a los usuarios autenticarse usando sus cuentas de Google, Microsoft, GitHub o Apple.
|
|
|
|
**Nota:** La tabla DDL `auth.oauth_connections` ya existe. Este modulo completa la implementacion backend y frontend.
|
|
|
|
## Objetivos
|
|
|
|
1. Autenticacion via Google OAuth 2.0
|
|
2. Autenticacion via Microsoft OAuth 2.0
|
|
3. Autenticacion via GitHub OAuth 2.0
|
|
4. Autenticacion via Apple Sign-In
|
|
5. Vincular/desvincular cuentas OAuth a usuarios existentes
|
|
6. Gestion de conexiones OAuth del usuario
|
|
|
|
## Alcance
|
|
|
|
### Incluido
|
|
- Flujo OAuth completo para 4 proveedores
|
|
- Passport.js strategies para cada proveedor
|
|
- Callback handling con creacion/vinculacion de usuario
|
|
- Gestion de conexiones OAuth por usuario
|
|
- Refresh de tokens OAuth cuando expiran
|
|
- UI para login con proveedores
|
|
- UI para gestionar conexiones en perfil
|
|
|
|
### Excluido
|
|
- SSO/SAML (enterprise feature - SAAS-E01)
|
|
- OAuth como proveedor (ser IdP) - futuro
|
|
- Proveedores adicionales (LinkedIn, Twitter) - bajo demanda
|
|
|
|
## Modelo de Datos
|
|
|
|
### Tablas Existentes (schema: auth)
|
|
|
|
**oauth_connections** (DDL ya existe)
|
|
```sql
|
|
- id: UUID PRIMARY KEY
|
|
- user_id: UUID REFERENCES users.users(id)
|
|
- provider: oauth_provider ENUM ('google', 'microsoft', 'github', 'apple')
|
|
- provider_user_id: VARCHAR(255) NOT NULL
|
|
- access_token: TEXT
|
|
- refresh_token: TEXT
|
|
- token_expires_at: TIMESTAMPTZ
|
|
- profile_data: JSONB
|
|
- created_at: TIMESTAMPTZ DEFAULT NOW()
|
|
- updated_at: TIMESTAMPTZ DEFAULT NOW()
|
|
|
|
UNIQUE(provider, provider_user_id)
|
|
UNIQUE(user_id, provider)
|
|
```
|
|
|
|
### ENUMs Existentes
|
|
|
|
```sql
|
|
CREATE TYPE auth.oauth_provider AS ENUM (
|
|
'google',
|
|
'microsoft',
|
|
'github',
|
|
'apple'
|
|
);
|
|
```
|
|
|
|
## Endpoints API
|
|
|
|
| Metodo | Endpoint | Descripcion | Auth |
|
|
|--------|----------|-------------|------|
|
|
| GET | /auth/oauth/:provider | Iniciar flujo OAuth | No |
|
|
| GET | /auth/oauth/:provider/callback | Callback del proveedor | No |
|
|
| POST | /auth/oauth/link/:provider | Vincular a cuenta existente | JWT |
|
|
| DELETE | /auth/oauth/:provider | Desvincular proveedor | JWT |
|
|
| GET | /auth/oauth/connections | Listar conexiones del usuario | JWT |
|
|
|
|
### Detalle de Endpoints
|
|
|
|
#### GET /auth/oauth/:provider
|
|
Inicia el flujo OAuth redirigiendo al proveedor.
|
|
|
|
**Parametros:**
|
|
- `provider`: google | microsoft | github | apple
|
|
- `redirect_uri` (query): URL de retorno post-auth
|
|
|
|
**Response:** Redirect 302 a URL del proveedor
|
|
|
|
#### GET /auth/oauth/:provider/callback
|
|
Procesa el callback del proveedor OAuth.
|
|
|
|
**Query params:**
|
|
- `code`: Authorization code del proveedor
|
|
- `state`: State para validar CSRF
|
|
|
|
**Response:**
|
|
```json
|
|
{
|
|
"access_token": "jwt_token",
|
|
"refresh_token": "refresh_token",
|
|
"user": {
|
|
"id": "uuid",
|
|
"email": "user@example.com",
|
|
"name": "Usuario"
|
|
},
|
|
"is_new_user": true
|
|
}
|
|
```
|
|
|
|
#### POST /auth/oauth/link/:provider
|
|
Vincula un proveedor OAuth a la cuenta actual.
|
|
|
|
**Headers:** Authorization: Bearer {jwt}
|
|
|
|
**Response:**
|
|
```json
|
|
{
|
|
"message": "Provider linked successfully",
|
|
"connection": {
|
|
"provider": "google",
|
|
"email": "user@gmail.com",
|
|
"linked_at": "2026-01-24T12:00:00Z"
|
|
}
|
|
}
|
|
```
|
|
|
|
#### DELETE /auth/oauth/:provider
|
|
Desvincula un proveedor OAuth de la cuenta.
|
|
|
|
**Headers:** Authorization: Bearer {jwt}
|
|
|
|
**Response:**
|
|
```json
|
|
{
|
|
"message": "Provider unlinked successfully"
|
|
}
|
|
```
|
|
|
|
#### GET /auth/oauth/connections
|
|
Lista todas las conexiones OAuth del usuario.
|
|
|
|
**Headers:** Authorization: Bearer {jwt}
|
|
|
|
**Response:**
|
|
```json
|
|
{
|
|
"connections": [
|
|
{
|
|
"provider": "google",
|
|
"email": "user@gmail.com",
|
|
"linked_at": "2026-01-24T12:00:00Z"
|
|
},
|
|
{
|
|
"provider": "github",
|
|
"username": "user123",
|
|
"linked_at": "2026-01-20T10:00:00Z"
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
## Implementacion Backend
|
|
|
|
### Entities
|
|
- `OAuthConnection` - Entity TypeORM para oauth_connections
|
|
|
|
### Services
|
|
- `OAuthService` - Logica de negocio OAuth
|
|
- `OAuthCallbackService` - Procesamiento de callbacks
|
|
|
|
### Controllers
|
|
- `OAuthController` - Endpoints OAuth
|
|
|
|
### Passport Strategies
|
|
- `GoogleStrategy` - Passport Google OAuth 2.0
|
|
- `MicrosoftStrategy` - Passport Microsoft OAuth 2.0
|
|
- `GitHubStrategy` - Passport GitHub OAuth 2.0
|
|
- `AppleStrategy` - Passport Apple Sign-In
|
|
|
|
### Dependencias NPM
|
|
```json
|
|
{
|
|
"passport": "^0.7.0",
|
|
"passport-google-oauth20": "^2.0.0",
|
|
"passport-microsoft": "^1.0.0",
|
|
"passport-github2": "^0.1.12",
|
|
"passport-apple": "^2.0.2"
|
|
}
|
|
```
|
|
|
|
## Implementacion Frontend
|
|
|
|
### Componentes
|
|
- `OAuthButtons` - Botones de login OAuth
|
|
- `OAuthCallback` - Pagina de callback
|
|
- `OAuthConnections` - Gestion de conexiones en perfil
|
|
|
|
### Paginas
|
|
- `/auth/oauth/callback` - Procesa callback OAuth
|
|
|
|
### Hooks
|
|
- `useOAuthLogin` - Iniciar flujo OAuth
|
|
- `useOAuthConnections` - Gestionar conexiones
|
|
|
|
## Flujos
|
|
|
|
### Login con OAuth (nuevo usuario)
|
|
```
|
|
1. Usuario click "Login con Google"
|
|
2. Frontend redirige a GET /auth/oauth/google
|
|
3. Backend redirige a Google
|
|
4. Usuario autoriza en Google
|
|
5. Google redirige a /auth/oauth/google/callback
|
|
6. Backend:
|
|
a. Valida code con Google
|
|
b. Obtiene perfil del usuario
|
|
c. Crea nuevo User + Tenant
|
|
d. Crea OAuthConnection
|
|
e. Genera JWT tokens
|
|
7. Redirect a frontend con tokens
|
|
8. Usuario autenticado
|
|
```
|
|
|
|
### Login con OAuth (usuario existente)
|
|
```
|
|
1. Usuario click "Login con Google"
|
|
2-5. (Mismo flujo)
|
|
6. Backend:
|
|
a. Valida code con Google
|
|
b. Busca OAuthConnection existente
|
|
c. Obtiene User vinculado
|
|
d. Genera JWT tokens
|
|
7. Redirect a frontend con tokens
|
|
8. Usuario autenticado
|
|
```
|
|
|
|
### Vincular OAuth a cuenta existente
|
|
```
|
|
1. Usuario autenticado va a Perfil > Conexiones
|
|
2. Click "Vincular Google"
|
|
3. Redirige a Google OAuth
|
|
4. Usuario autoriza
|
|
5. Backend:
|
|
a. Valida que no existe conexion con ese provider_user_id
|
|
b. Crea OAuthConnection para user actual
|
|
6. Muestra conexion en lista
|
|
```
|
|
|
|
## Configuracion
|
|
|
|
### Variables de Entorno
|
|
```env
|
|
# Google OAuth
|
|
GOOGLE_CLIENT_ID=xxx.apps.googleusercontent.com
|
|
GOOGLE_CLIENT_SECRET=xxx
|
|
GOOGLE_CALLBACK_URL=https://app.example.com/auth/oauth/google/callback
|
|
|
|
# Microsoft OAuth
|
|
MICROSOFT_CLIENT_ID=xxx
|
|
MICROSOFT_CLIENT_SECRET=xxx
|
|
MICROSOFT_CALLBACK_URL=https://app.example.com/auth/oauth/microsoft/callback
|
|
|
|
# GitHub OAuth
|
|
GITHUB_CLIENT_ID=xxx
|
|
GITHUB_CLIENT_SECRET=xxx
|
|
GITHUB_CALLBACK_URL=https://app.example.com/auth/oauth/github/callback
|
|
|
|
# Apple Sign-In
|
|
APPLE_CLIENT_ID=xxx
|
|
APPLE_TEAM_ID=xxx
|
|
APPLE_KEY_ID=xxx
|
|
APPLE_PRIVATE_KEY=xxx
|
|
APPLE_CALLBACK_URL=https://app.example.com/auth/oauth/apple/callback
|
|
```
|
|
|
|
## Seguridad
|
|
|
|
1. **CSRF Protection:** Usar state parameter en flujo OAuth
|
|
2. **Token Storage:** Encriptar access/refresh tokens en BD
|
|
3. **Scope Minimo:** Solicitar solo email y perfil basico
|
|
4. **Validacion de Dominio:** Verificar callback URL contra whitelist
|
|
5. **Rate Limiting:** Limitar intentos de OAuth por IP
|
|
|
|
## Criterios de Aceptacion
|
|
|
|
- [ ] Login con Google funciona end-to-end
|
|
- [ ] Login con Microsoft funciona end-to-end
|
|
- [ ] Login con GitHub funciona end-to-end
|
|
- [ ] Login con Apple funciona end-to-end
|
|
- [ ] Usuario puede vincular multiples proveedores
|
|
- [ ] Usuario puede desvincular proveedores (si tiene password o otro provider)
|
|
- [ ] Conexiones se muestran en perfil
|
|
- [ ] Tests unitarios con cobertura >70%
|
|
- [ ] Tests E2E para flujo completo
|
|
|
|
## Dependencias
|
|
|
|
- SAAS-001 (Auth) - Base de autenticacion
|
|
- SAAS-003 (Users) - Gestion de usuarios
|
|
|
|
## Referencias
|
|
|
|
- [Google OAuth 2.0](https://developers.google.com/identity/protocols/oauth2)
|
|
- [Microsoft Identity Platform](https://docs.microsoft.com/en-us/azure/active-directory/develop/)
|
|
- [GitHub OAuth](https://docs.github.com/en/developers/apps/building-oauth-apps)
|
|
- [Apple Sign-In](https://developer.apple.com/sign-in-with-apple/)
|
|
|
|
---
|
|
|
|
*SAAS-015 v1.0.0 - Template SaaS*
|