libs/login/src/lib/feature-login/src/lib/login/login.component.tslibs/login/src/lib/data-access/src/lib/login.service.tslibs/shared/data-access/src/lib/auth/auth.service.tsapps/nebula-shell/src/app/app.routes.tsLoginComponent construye un formulario reactivo con user y password, ambos obligatorios. onSubmit() marca los controles como tocados si el formulario no es válido. Si es válido, normaliza el usuario con trim().toLowerCase() y recorta espacios al inicio/final de la contraseña.
LoginService.login() envía:
POST /simappe-oauth2-server/api/v1/login{ username, password }x-simappe-environment, resuelto por resolveEnvKey().La respuesta se interpreta como ApiResponse<AuthContent> y se toma response.content. En tap, AuthService.handleSuccessfulAuth() guarda tokens y datos de sesión. Los errores se convierten en mensajes para credenciales inválidas, error de conexión o mensaje provisto en la respuesta.
Si AuthContent.mustChangePassword es verdadero, el componente marca must_change_password en localStorage y navega a /cambiar-clave. Esta sesión es suficiente para permitir el cambio obligatorio, pero no se continúa al ERP hasta completar el cambio.
Si no requiere cambio, se limpia la marca pendiente y se invoca AuthService.validateTokenAndGetSession(), que consulta POST /simappe-oauth2-server/api/v1/jwt-get con el access token en AccessToken. El backend devuelve los datos de usuario que se guardan en memoria, cookie de perfil y localStorage.user_data en forma reducida.
Si la consulta falla, el login muestra el error y no navega al área de trabajo.
Al recibir la sesión, la pantalla aplica preferencias de tema/perfil y finalmente navega a /layout/company, donde se elige la compañía/producto según el flujo del sistema.
En app.routes.ts:
/login: usa LOGIN_ROUTES y loginGuard./activar-usuario: flujo público de activación./recuperar: flujo público de recuperación./cambiar-clave: guard propio para cambio obligatorio./layout/**: protegido por authGuard.