sessionAuth->sanitizeRedirectTarget( (string) ($request->query('redirect_to') ?? ''), ); if (! Auth::guard('web')->check()) { return response()->json([ 'status' => true, 'authenticated' => false, 'redirect_to' => $redirectTo, ]); } /** @var list|null $roles */ $roles = session('roles'); $roles = is_array($roles) ? array_values(array_filter(array_map('strval', $roles))) : []; $currentRole = session('role'); if ($roles !== [] && count($roles) > 1 && ! $currentRole) { return response()->json([ 'status' => true, 'authenticated' => true, 'next_url' => $this->urls->webSelectRoleUrl(false), 'roles' => $roles, 'pending_redirect' => session('post_login_redirect'), ]); } if ($redirectTo !== null) { return response()->json([ 'status' => true, 'authenticated' => true, 'next_url' => $redirectTo, ]); } $pick = $currentRole !== null && $currentRole !== '' ? [(string) $currentRole] : $roles; return response()->json([ 'status' => true, 'authenticated' => true, 'next_url' => $this->sessionAuth->dashboardRouteForRoles($pick), ]); } /** Creates a web session from the submitted credentials. */ public function login(LoginSessionRequest $request): JsonResponse { $email = (string) $request->validated('email'); $password = (string) $request->validated('password'); $redirectRaw = $request->validated('redirect_to'); $sanitizedRedirect = $redirectRaw !== null ? $this->sessionAuth->sanitizeRedirectTarget((string) $redirectRaw) : null; $ip = (string) $request->ip(); if ($this->security->isIpBlocked($ip)) { return response()->json([ 'status' => false, 'message' => 'Too many failed attempts from your IP. Please try again later.', ], 429); } $user = User::query()->where('email', $email)->first(); if (! $user) { $this->security->logIpAttempt($ip); return response()->json([ 'status' => false, 'message' => 'The email and password combination you entered is invalid. Please try again.', ], 401); } if ($user->is_suspended) { return response()->json([ 'status' => false, 'message' => 'Account suspended. Please check your email to reset your password.', ], 403); } if (! verify_stored_password($password, (string) $user->password)) { $this->security->handleFailedLogin($user, $email, $ip); return response()->json([ 'status' => false, 'message' => 'The email and password combination you entered is invalid. Please try again.', ], 401); } $fresh = $user->fresh(); if (! $fresh) { return response()->json([ 'status' => false, 'message' => 'The email and password combination you entered is invalid. Please try again.', ], 401); } $this->security->resetFailedAttempts($fresh); $this->security->logSuccessfulLogin($fresh, $request); $dest = $this->sessionAuth->resolvePostLoginDestination($fresh, $sanitizedRedirect); if (($dest['kind'] ?? '') === 'no_roles') { return response()->json([ 'status' => false, 'message' => (string) ($dest['reason'] ?? 'Role not assigned. Please contact support.'), ], 403); } $loginPayload = $this->security->buildLoginResponse($fresh, JWTAuth::fromUser($fresh)); return response()->json(array_merge($loginPayload, [ 'requires_role_selection' => ($dest['kind'] ?? '') === 'select_role', 'roles' => $dest['roles'] ?? null, 'next_url' => $dest['redirect_url'] ?? $this->urls->docsHomeUrl(false), ])); } /** Closes the current web session. */ public function logout(Request $request): JsonResponse { $userId = Auth::guard('web')->id(); $email = Auth::guard('web')->user()?->email ?? session('user_email'); $this->sessionAuth->logLogout($userId ? (int) $userId : null, $email ? (string) $email : null); Auth::guard('web')->logout(); $request->session()->invalidate(); $request->session()->regenerateToken(); return response()->json([ 'status' => true, 'next_url' => $this->urls->docsHomeUrl(false), ]); } /** Returns the available post-login roles for the current user. */ public function selectRole(Request $request): JsonResponse { if (! Auth::guard('web')->check()) { return response()->json([ 'status' => false, 'message' => 'No roles available.', ], 401); } /** @var list|null $roles */ $roles = session('roles'); $roles = is_array($roles) ? array_values(array_filter(array_map('strval', $roles))) : []; if ($roles === []) { return response()->json([ 'status' => false, 'message' => 'No roles available.', ], 422); } return response()->json([ 'status' => true, 'roles' => $roles, 'pending_redirect' => session('post_login_redirect'), 'redirect_to_query' => $this->sessionAuth->sanitizeRedirectTarget( (string) ($request->query('redirect_to') ?? ''), ), ]); } /** Stores the selected post-login role in session state. */ public function setRole(SetRoleSessionRequest $request): JsonResponse { if (! Auth::guard('web')->check()) { return response()->json([ 'status' => false, 'message' => 'Unauthorized.', ], 401); } $selectedRole = (string) $request->validated('selected_role'); /** @var list|null $available */ $available = session('roles'); $available = is_array($available) ? array_values(array_filter(array_map('strval', $available))) : []; if ($available === [] || ! in_array($selectedRole, $available, true)) { return response()->json([ 'status' => false, 'message' => 'Invalid role selected.', ], 422); } $redirectRaw = $request->validated('redirect_to'); $redirectTo = $redirectRaw !== null ? $this->sessionAuth->sanitizeRedirectTarget((string) $redirectRaw) : null; if ($redirectTo === null) { $redirectTo = $this->sessionAuth->sanitizeRedirectTarget( (string) (session('post_login_redirect') ?? ''), ); } session(['role' => $selectedRole]); session()->forget('post_login_redirect'); $next = $redirectTo ?? $this->sessionAuth->dashboardRouteForRoles([$selectedRole]); return response()->json([ 'status' => true, 'role' => $selectedRole, 'next_url' => $next, ]); } }