input('returnTo', '')); if ($returnTo !== '') { $_SESSION['auth_return_to'] = $returnTo; } $provider = AuthService::provider(); $authUrl = $provider->getAuthorizationUrl(); $_SESSION['oauth2_state'] = $provider->getState(); return $this->redirect($authUrl); } public function callback(Request $request): mixed { $state = (string) $request->input('state', ''); $code = (string) $request->input('code', ''); if ($state === '' || $state !== ($_SESSION['oauth2_state'] ?? '')) { unset($_SESSION['oauth2_state']); return $this->view('auth.callback-error', [ 'pageTitle' => 'Authentication Error', 'error' => 'Invalid state parameter. Please try logging in again.', ]); } unset($_SESSION['oauth2_state']); try { $provider = AuthService::provider(); $token = $provider->getAccessToken('authorization_code', ['code' => $code]); $userInfo = AuthService::claimsFromToken($token->getToken()); if (empty($userInfo)) { throw new \RuntimeException('Access token payload was empty or undecodable.'); } AuthService::storeUser($userInfo); $redirectTo = $_SESSION['auth_return_to'] ?? '/boards'; unset($_SESSION['auth_return_to']); return $this->redirect($redirectTo); } catch (\Throwable $e) { error_log('Keycloak callback error: ' . $e->getMessage()); $debug = filter_var(getenv('APP_DEBUG'), FILTER_VALIDATE_BOOLEAN); $error = $debug ? get_class($e) . ': ' . $e->getMessage() : 'Authentication failed. Please try again.'; return $this->view('auth.callback-error', [ 'pageTitle' => 'Authentication Error', 'error' => $error, ]); } } public function logout(): mixed { AuthService::clearSession(); return $this->redirect(AuthService::logoutUrl()); } }