Módulo: Autenticación
Scope
| Tipo de organización | Acceso |
|---|---|
| Empresa | ✅ |
| Agencia | ✅ |
| Reclutador independiente | ✅ |
| Usuario sin organización | ✅ |
Permisos por rol
| Acción | Disponible | Permiso |
|---|---|---|
| Login con email/password | ✅ | Público |
| Login con Google | ✅ | Público |
| Login con LinkedIn | ❌ (placeholder) | — |
| Login con Apple | ❌ (placeholder) | — |
| Registro de cuenta | ✅ | Público |
| Solicitar reset de contraseña | ✅ | Público |
| Obtener contexto de usuario | ✅ | Autenticado |
| Logout | ✅ | Autenticado |
¿Qué es?
El módulo de Autenticación gestiona la identidad del usuario: login, registro, recuperación de contraseña y autenticación social. Utiliza Better Auth en el backend y persiste la sesión como Bearer token en secure storage.
¿Qué puede hacer el usuario?
- Iniciar sesión con email y contraseña
- Iniciar sesión con Google (nativo)
- Crear una cuenta nueva con nombre, email y contraseña
- Solicitar un correo de recuperación de contraseña
- Reenviar correo de verificación de email
- Cerrar sesión
- Cambiar de idioma desde la pantalla de login
Flujo del usuario
Login con email
1. Abre la app → splash verifica token almacenado
2. Si no hay token → welcome (onboarding intro) → login
3. Ingresa email y contraseña
4. Presiona "Iniciar sesión" → validación de formulario
5. API devuelve token → se almacena en secure storage
6. Se obtiene contexto de usuario (organización, permisos)
7. Si tiene organización → home
8. Si no tiene organización → onboarding de tenant
Login con Google
1. Presiona botón de Google en login
2. Se abre selector de cuentas de Google (nativo)
3. Selecciona cuenta → SDK obtiene idToken + accessToken
4. Se envía a POST /api/auth/sign-in/social
5. API valida con Google, crea/vincula cuenta, devuelve token
6. Se almacena token → se obtiene contexto → home u onboarding
Registro
1. Presiona "Regístrate ahora" desde login
2. Llena nombre, email, contraseña, confirmar contraseña
3. Acepta términos y condiciones
4. Presiona "Crear cuenta" → API registra usuario
5. Pantalla de éxito → "Revisa tu correo de verificación"
6. Puede reenviar correo (cooldown 60s)
7. Presiona "Ir a iniciar sesión" → vuelve a login
Recuperar contraseña
1. Presiona "¿Olvidaste tu contraseña?" desde login
2. Ingresa email
3. Presiona "Enviar instrucciones" → API envía correo
4. Pantalla de éxito con email mostrado
5. Puede reenviar (cooldown 60s)
6. El cambio real de contraseña se completa vía link web
Reglas de negocio
- Contraseña mínima: 8 caracteres
- Email debe ser válido (validación con regex)
- Confirmación de contraseña debe coincidir
- Aceptar términos es obligatorio para registrarse
- Cooldown de 60 segundos para reenvío de correos
- Token se persiste en secure storage (flutter_secure_storage)
- Token expirado → se limpia automáticamente y redirige a login
- Social login con
requestSignUp: truepermite crear cuenta si no existe - Social login vincula la cuenta si el email ya existe (account linking)
- Auth guard redirige: sin token → login, sin organización → onboarding
Requisitos para login con email
| Campo | Tipo | Requerido | Notas |
|---|---|---|---|
| String | ✅ | Validado con regex | |
| password | String | ✅ | No vacío |
Requisitos para registro
| Campo | Tipo | Requerido | Notas |
|---|---|---|---|
| name | String | ✅ | Nombre completo |
| String | ✅ | Validado con regex | |
| password | String | ✅ | Mínimo 8 caracteres |
| confirmPassword | String | ✅ | Debe coincidir con password (validación local) |
| acceptTerms | bool | ✅ | Debe ser true |
Requisitos para reset de contraseña
| Campo | Tipo | Requerido | Notas |
|---|---|---|---|
| String | ✅ | Validado con regex |
Requisitos para social sign-in
| Campo | Tipo | Requerido | Notas |
|---|---|---|---|
| provider | String | ✅ | "google" |
| idToken | Object | ✅ | { token: String, accessToken: String } del SDK nativo |
| requestSignUp | bool | ✅ | true para permitir crear cuenta nueva |
| disableRedirect | bool | ✅ | true para respuesta JSON directa (móvil) |
Arquitectura
lib/features/auth/
├── application/
│ ├── auth_cubit.dart # Estado global de autenticación
│ ├── auth_state.dart # AuthStatus enum
│ ├── login_cubit.dart # Login email + social
│ ├── login_state.dart
│ ├── register_cubit.dart # Registro de cuenta
│ ├── register_state.dart
│ ├── reset_password_cubit.dart # Recuperación de contraseña
│ └── reset_password_state.dart
├── data/
│ ├── datasources/
│ │ ├── auth_remote_datasource.dart
│ │ └── auth_local_datasource.dart
│ ├── models/
│ │ ├── auth_token_model.dart
│ │ └── user_context_model.dart
│ └── repositories/
│ └── auth_repository_impl.dart
├── domain/
│ ├── entities/
│ │ ├── auth_token.dart
│ │ ├── user_context.dart
│ │ └── user_credentials.dart
│ ├── repositories/
│ │ └── auth_repository.dart
│ └── usecases/
│ ├── login_usecase.dart
│ ├── logout_usecase.dart
│ ├── check_auth_usecase.dart
│ ├── register_usecase.dart
│ ├── reset_password_usecase.dart
│ └── social_sign_in_usecase.dart
└── presentation/
├── pages/
│ ├── login_page.dart
│ ├── register_page.dart
│ ├── register_success_page.dart
│ ├── reset_password_page.dart
│ ├── welcome_page.dart
│ ├── terms_and_conditions_page.dart
│ └── privacy_policy_page.dart
└── widgets/
├── app_text_field.dart
├── auth_header.dart
├── branded_divider.dart
├── login_skeleton.dart
├── primary_gradient_button.dart
└── social_provider_button.dart
Endpoints API
| Método | Endpoint | Autenticación | Descripción |
|---|---|---|---|
| POST | /api/auth/sign-in/email | Público | Login con email/password |
| POST | /api/auth/sign-in/social | Público | Login social (Google) |
| POST | /api/auth/sign-up/email | Público | Registro de cuenta |
| POST | /api/auth/request-password-reset | Público | Enviar correo de reset |
| POST | /api/auth/send-verification-email | Público | Reenviar verificación |
| POST | /api/auth/sign-out | Autenticado | Cerrar sesión |
| GET | /auth/context | Autenticado | Contexto de usuario (sin prefijo /api) |
| POST | /api/auth/refresh | Autenticado | Refresh token |
Configuración de Google Sign-In
Android
- Package:
com.efectoestrategico.contratta - SHA-1 debug:
5C:BD:43:69:81:83:EF:38:C8:E7:FF:51:3A:34:F1:AC:50:FF:94:BB - Client ID Android:
410232511933-hncev2ls1lvv1khlic87cmmr6hb91hbh.apps.googleusercontent.com - Requiere
oauth_clientengoogle-services.json(client_type 1 = Android, client_type 3 = Web)
iOS
- Bundle ID:
com.contratta.contratta - Client ID iOS:
410232511933-c42q0bb0no9faa3g3bp18d6lohpifvu9.apps.googleusercontent.com - URL Scheme:
com.googleusercontent.apps.410232511933-c42q0bb0no9faa3g3bp18d6lohpifvu9 - Configurado en
Info.plist→CFBundleURLTypes
Shared
- Web Client ID (serverClientId):
410232511933-ece4jcl4nci13gfh2ftruuiaf8b3sbs0.apps.googleusercontent.com - Definido en
lib/core/constants/social_auth_constants.dart - NO se incluye el client secret en Flutter
Registro DI
- Datasources:
LazySingleton(remote + local) - Repository:
LazySingleton - UseCases:
LazySingleton(Login, Logout, CheckAuth, Register, ResetPassword, SocialSignIn) - GoogleSignInService:
LazySingleton - AuthCubit:
LazySingleton(estado global compartido) - LoginCubit:
Factory(nueva instancia por pantalla) - RegisterCubit:
Factory - ResetPasswordCubit:
Factory
Localización
✅ Completa — todos los strings usan context.l10n desde los archivos ARB (app_es.arb / app_en.arb).
Claves principales: welcomeBack, loginButton, loginSubtitle, forgotPassword, noAccount, registerNow, orContinueWith, registerTitle, registerButton, resetPasswordSuccess, sendInstructions, authWelcomeTitle, authWelcomeMessage, orgTypeCompany, orgTypeAgency, orgTypeRecruiter, etc.
Tests
| Archivo | Cobertura |
|---|---|
login_cubit_test.dart | Login email (success, error, reset) + Social sign-in (success, error, params) |
register_cubit_test.dart | Success, ServerFailure, NetworkFailure, params verification |
reset_password_cubit_test.dart | Success, ServerFailure, NetworkFailure, email verification |
login_usecase_test.dart | Success, AuthFailure |
register_usecase_test.dart | Success, ServerFailure, NetworkFailure |
reset_password_usecase_test.dart | Success, ServerFailure, NetworkFailure |
auth_cubit_test.dart | checkAuthStatus, logout, setAuthenticated |
Pendientes
| Tema | Estado |
|---|---|
| LinkedIn Sign-In | Placeholder — pendiente de implementación en backend |
| Apple Sign-In | Placeholder — pendiente de implementación en backend |
| Flujo de nueva contraseña in-app | No implementado — se completa vía link web |
| SHA-1 de release | Pendiente — se genera cuando se cree el keystore de producción |