Saltar al contenido principal
Versión: 1.2

Módulo: Autenticación

Scope

Tipo de organizaciónAcceso
Empresa
Agencia
Reclutador independiente
Usuario sin organización

Permisos por rol

AcciónDisponiblePermiso
Login con email/passwordPúblico
Login con GooglePúblico
Login con LinkedIn❌ (placeholder)
Login con Apple❌ (placeholder)
Registro de cuentaPúblico
Solicitar reset de contraseñaPúblico
Obtener contexto de usuarioAutenticado
LogoutAutenticado

¿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: true permite 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

CampoTipoRequeridoNotas
emailStringValidado con regex
passwordStringNo vacío

Requisitos para registro

CampoTipoRequeridoNotas
nameStringNombre completo
emailStringValidado con regex
passwordStringMínimo 8 caracteres
confirmPasswordStringDebe coincidir con password (validación local)
acceptTermsboolDebe ser true

Requisitos para reset de contraseña

CampoTipoRequeridoNotas
emailStringValidado con regex

Requisitos para social sign-in

CampoTipoRequeridoNotas
providerString"google"
idTokenObject{ token: String, accessToken: String } del SDK nativo
requestSignUpbooltrue para permitir crear cuenta nueva
disableRedirectbooltrue 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étodoEndpointAutenticaciónDescripción
POST/api/auth/sign-in/emailPúblicoLogin con email/password
POST/api/auth/sign-in/socialPúblicoLogin social (Google)
POST/api/auth/sign-up/emailPúblicoRegistro de cuenta
POST/api/auth/request-password-resetPúblicoEnviar correo de reset
POST/api/auth/send-verification-emailPúblicoReenviar verificación
POST/api/auth/sign-outAutenticadoCerrar sesión
GET/auth/contextAutenticadoContexto de usuario (sin prefijo /api)
POST/api/auth/refreshAutenticadoRefresh 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_client en google-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.plistCFBundleURLTypes

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

ArchivoCobertura
login_cubit_test.dartLogin email (success, error, reset) + Social sign-in (success, error, params)
register_cubit_test.dartSuccess, ServerFailure, NetworkFailure, params verification
reset_password_cubit_test.dartSuccess, ServerFailure, NetworkFailure, email verification
login_usecase_test.dartSuccess, AuthFailure
register_usecase_test.dartSuccess, ServerFailure, NetworkFailure
reset_password_usecase_test.dartSuccess, ServerFailure, NetworkFailure
auth_cubit_test.dartcheckAuthStatus, logout, setAuthenticated

Pendientes

TemaEstado
LinkedIn Sign-InPlaceholder — pendiente de implementación en backend
Apple Sign-InPlaceholder — pendiente de implementación en backend
Flujo de nueva contraseña in-appNo implementado — se completa vía link web
SHA-1 de releasePendiente — se genera cuando se cree el keystore de producción