diff --git a/.env.example.complete b/.env.example.complete index 0667cb75b..57d24eb5d 100644 --- a/.env.example.complete +++ b/.env.example.complete @@ -273,11 +273,8 @@ OIDC_USER_TO_GROUPS=false OIDC_GROUPS_CLAIM=groups OIDC_REMOVE_FROM_GROUPS=false OIDC_EXTERNAL_ID_CLAIM=sub - -# OIDC Logout Feature: Its value should be value of end_session_endpoint from /.well-known/openid-configuration OIDC_END_SESSION_ENDPOINT=null - # Disable default third-party services such as Gravatar and Draw.IO # Service-specific options will override this option DISABLE_EXTERNAL_SERVICES=false diff --git a/app/Access/Controllers/LoginController.php b/app/Access/Controllers/LoginController.php index 3b4f9b347..904736656 100644 --- a/app/Access/Controllers/LoginController.php +++ b/app/Access/Controllers/LoginController.php @@ -3,7 +3,7 @@ namespace BookStack\Access\Controllers; use BookStack\Access\LoginService; -use BookStack\Access\SocialAuthService; +use BookStack\Access\SocialDriverManager; use BookStack\Exceptions\LoginAttemptEmailNeededException; use BookStack\Exceptions\LoginAttemptException; use BookStack\Facades\Activity; @@ -17,19 +17,19 @@ class LoginController extends Controller { use ThrottlesLogins; - protected SocialAuthService $socialAuthService; + protected SocialDriverManager $socialDriverManager; protected LoginService $loginService; /** * Create a new controller instance. */ - public function __construct(SocialAuthService $socialAuthService, LoginService $loginService) + public function __construct(SocialDriverManager $driverManager, LoginService $loginService) { $this->middleware('guest', ['only' => ['getLogin', 'login']]); $this->middleware('guard:standard,ldap', ['only' => ['login']]); $this->middleware('guard:standard,ldap,oidc', ['only' => ['logout']]); - $this->socialAuthService = $socialAuthService; + $this->socialDriverManager = $driverManager; $this->loginService = $loginService; } @@ -38,7 +38,7 @@ class LoginController extends Controller */ public function getLogin(Request $request) { - $socialDrivers = $this->socialAuthService->getActiveDrivers(); + $socialDrivers = $this->socialDriverManager->getActive(); $authMethod = config('auth.method'); $preventInitiation = $request->get('prevent_auto_init') === 'true'; @@ -101,15 +101,9 @@ class LoginController extends Controller /** * Logout user and perform subsequent redirect. */ - public function logout(Request $request) + public function logout() { - Auth::guard()->logout(); - $request->session()->invalidate(); - $request->session()->regenerateToken(); - - $redirectUri = $this->shouldAutoInitiate() ? '/login?prevent_auto_init=true' : '/'; - - return redirect($redirectUri); + return redirect($this->loginService->logout()); } /** @@ -218,16 +212,4 @@ class LoginController extends Controller redirect()->setIntendedUrl($previous); } - - /** - * Check if login auto-initiate should be valid based upon authentication config. - */ - protected function shouldAutoInitiate(): bool - { - $socialDrivers = $this->socialAuthService->getActiveDrivers(); - $authMethod = config('auth.method'); - $autoRedirect = config('auth.auto_initiate'); - - return $autoRedirect && count($socialDrivers) === 0 && in_array($authMethod, ['oidc', 'saml2']); - } } diff --git a/app/Access/Controllers/OidcController.php b/app/Access/Controllers/OidcController.php index 083e83e35..055d4c140 100644 --- a/app/Access/Controllers/OidcController.php +++ b/app/Access/Controllers/OidcController.php @@ -11,9 +11,6 @@ class OidcController extends Controller { protected OidcService $oidcService; - /** - * OpenIdController constructor. - */ public function __construct(OidcService $oidcService) { $this->oidcService = $oidcService; @@ -65,16 +62,10 @@ class OidcController extends Controller } /** - * OIDC Logout Feature: Start the authorization logout flow via OIDC. + * Log the user out then start the OIDC RP-initiated logout process. */ public function logout() { - try { - return $this->oidcService->logout(); - } catch (OidcException $exception) { - $this->showErrorNotification($exception->getMessage()); - return redirect('/logout'); - } + return redirect($this->oidcService->logout()); } - } diff --git a/app/Access/Controllers/RegisterController.php b/app/Access/Controllers/RegisterController.php index 3c653a073..13b97f03c 100644 --- a/app/Access/Controllers/RegisterController.php +++ b/app/Access/Controllers/RegisterController.php @@ -4,7 +4,7 @@ namespace BookStack\Access\Controllers; use BookStack\Access\LoginService; use BookStack\Access\RegistrationService; -use BookStack\Access\SocialAuthService; +use BookStack\Access\SocialDriverManager; use BookStack\Exceptions\StoppedAuthenticationException; use BookStack\Exceptions\UserRegistrationException; use BookStack\Http\Controller; @@ -15,7 +15,7 @@ use Illuminate\Validation\Rules\Password; class RegisterController extends Controller { - protected SocialAuthService $socialAuthService; + protected SocialDriverManager $socialDriverManager; protected RegistrationService $registrationService; protected LoginService $loginService; @@ -23,14 +23,14 @@ class RegisterController extends Controller * Create a new controller instance. */ public function __construct( - SocialAuthService $socialAuthService, + SocialDriverManager $socialDriverManager, RegistrationService $registrationService, LoginService $loginService ) { $this->middleware('guest'); $this->middleware('guard:standard'); - $this->socialAuthService = $socialAuthService; + $this->socialDriverManager = $socialDriverManager; $this->registrationService = $registrationService; $this->loginService = $loginService; } @@ -43,7 +43,7 @@ class RegisterController extends Controller public function getRegister() { $this->registrationService->ensureRegistrationAllowed(); - $socialDrivers = $this->socialAuthService->getActiveDrivers(); + $socialDrivers = $this->socialDriverManager->getActive(); return view('auth.register', [ 'socialDrivers' => $socialDrivers, diff --git a/app/Access/Controllers/SocialController.php b/app/Access/Controllers/SocialController.php index ff6d5c2dd..bbdabb4ab 100644 --- a/app/Access/Controllers/SocialController.php +++ b/app/Access/Controllers/SocialController.php @@ -79,7 +79,7 @@ class SocialController extends Controller try { return $this->socialAuthService->handleLoginCallback($socialDriver, $socialUser); } catch (SocialSignInAccountNotUsed $exception) { - if ($this->socialAuthService->driverAutoRegisterEnabled($socialDriver)) { + if ($this->socialAuthService->drivers()->isAutoRegisterEnabled($socialDriver)) { return $this->socialRegisterCallback($socialDriver, $socialUser); } @@ -114,7 +114,7 @@ class SocialController extends Controller { $socialUser = $this->socialAuthService->handleRegistrationCallback($socialDriver, $socialUser); $socialAccount = $this->socialAuthService->newSocialAccount($socialDriver, $socialUser); - $emailVerified = $this->socialAuthService->driverAutoConfirmEmailEnabled($socialDriver); + $emailVerified = $this->socialAuthService->drivers()->isAutoConfirmEmailEnabled($socialDriver); // Create an array of the user data to create a new user instance $userData = [ diff --git a/app/Access/LoginService.php b/app/Access/LoginService.php index 27480ba21..f0f6ad4d3 100644 --- a/app/Access/LoginService.php +++ b/app/Access/LoginService.php @@ -16,13 +16,11 @@ class LoginService { protected const LAST_LOGIN_ATTEMPTED_SESSION_KEY = 'auth-login-last-attempted'; - protected $mfaSession; - protected $emailConfirmationService; - - public function __construct(MfaSession $mfaSession, EmailConfirmationService $emailConfirmationService) - { - $this->mfaSession = $mfaSession; - $this->emailConfirmationService = $emailConfirmationService; + public function __construct( + protected MfaSession $mfaSession, + protected EmailConfirmationService $emailConfirmationService, + protected SocialDriverManager $socialDriverManager, + ) { } /** @@ -163,4 +161,29 @@ class LoginService return $result; } + + /** + * Logs the current user out of the application. + * Returns an app post-redirect path. + */ + public function logout(): string + { + auth()->logout(); + session()->invalidate(); + session()->regenerateToken(); + + return $this->shouldAutoInitiate() ? '/login?prevent_auto_init=true' : '/'; + } + + /** + * Check if login auto-initiate should be valid based upon authentication config. + */ + protected function shouldAutoInitiate(): bool + { + $socialDrivers = $this->socialDriverManager->getActive(); + $authMethod = config('auth.method'); + $autoRedirect = config('auth.auto_initiate'); + + return $autoRedirect && count($socialDrivers) === 0 && in_array($authMethod, ['oidc', 'saml2']); + } } diff --git a/app/Access/Oidc/OidcService.php b/app/Access/Oidc/OidcService.php index 1067b0832..be869b179 100644 --- a/app/Access/Oidc/OidcService.php +++ b/app/Access/Oidc/OidcService.php @@ -217,11 +217,7 @@ class OidcService $settings->keys, ); - // OIDC Logout Feature: Temporarily save token in session - $access_token_for_logout = $idTokenText; - session()->put("oidctoken", $access_token_for_logout); - - + session()->put("oidc_id_token", $idTokenText); $returnClaims = Theme::dispatch(ThemeEvents::OIDC_ID_TOKEN_PRE_VALIDATE, $idToken->getAllClaims(), [ 'access_token' => $accessToken->getToken(), @@ -291,36 +287,24 @@ class OidcService return $this->config()['user_to_groups'] !== false; } - /** - * OIDC Logout Feature: Initiate a logout flow. - * - * @throws OidcException - * - * @return string + * Start the RP-initiated logout flow if active, otherwise start a standard logout flow. + * Returns a post-app-logout redirect URL. + * Reference: https://openid.net/specs/openid-connect-rpinitiated-1_0.html */ - public function logout() { + public function logout(): string + { + $endSessionEndpoint = $this->config()["end_session_endpoint"]; - $config = $this->config(); - $app_url = env('APP_URL', ''); - $end_session_endpoint = $config["end_session_endpoint"]; - - $oidctoken = session()->get("oidctoken"); - session()->invalidate(); - - if (str_contains($app_url, 'https://')) { - $protocol = 'https://'; - } else { - $protocol = 'http://'; - } - - - - return redirect($end_session_endpoint.'?id_token_hint='.$oidctoken."&post_logout_redirect_uri=".$protocol.$_SERVER['HTTP_HOST']."/"); + // TODO - Add autodiscovery and false/null config value support. + $oidcToken = session()->pull("oidc_id_token"); + $defaultLogoutUrl = url($this->loginService->logout()); + $endpointParams = [ + 'id_token_hint' => $oidcToken, + 'post_logout_redirect_uri' => $defaultLogoutUrl, + ]; + return $endSessionEndpoint . '?' . http_build_query($endpointParams); } - - - } diff --git a/app/Access/Saml2Service.php b/app/Access/Saml2Service.php index 7f599762e..e7627f7e4 100644 --- a/app/Access/Saml2Service.php +++ b/app/Access/Saml2Service.php @@ -71,8 +71,7 @@ class Saml2Service throw $error; } - $this->actionLogout(); - $url = '/'; + $url = $this->loginService->logout(); $id = null; } @@ -140,20 +139,11 @@ class Saml2Service ); } - $this->actionLogout(); + $this->loginService->logout(); return $redirect; } - /** - * Do the required actions to log a user out. - */ - protected function actionLogout() - { - auth()->logout(); - session()->invalidate(); - } - /** * Get the metadata for this service provider. * diff --git a/app/Access/SocialAuthService.php b/app/Access/SocialAuthService.php index f0e0413f0..c3c20587d 100644 --- a/app/Access/SocialAuthService.php +++ b/app/Access/SocialAuthService.php @@ -2,69 +2,24 @@ namespace BookStack\Access; -use BookStack\Auth\Access\handler; use BookStack\Exceptions\SocialDriverNotConfigured; use BookStack\Exceptions\SocialSignInAccountNotUsed; use BookStack\Exceptions\UserRegistrationException; use BookStack\Users\Models\User; -use Illuminate\Support\Facades\Event; use Illuminate\Support\Str; use Laravel\Socialite\Contracts\Factory as Socialite; use Laravel\Socialite\Contracts\Provider; use Laravel\Socialite\Contracts\User as SocialUser; use Laravel\Socialite\Two\GoogleProvider; -use SocialiteProviders\Manager\SocialiteWasCalled; use Symfony\Component\HttpFoundation\RedirectResponse; class SocialAuthService { - /** - * The core socialite library used. - * - * @var Socialite - */ - protected $socialite; - - /** - * @var LoginService - */ - protected $loginService; - - /** - * The default built-in social drivers we support. - * - * @var string[] - */ - protected $validSocialDrivers = [ - 'google', - 'github', - 'facebook', - 'slack', - 'twitter', - 'azure', - 'okta', - 'gitlab', - 'twitch', - 'discord', - ]; - - /** - * Callbacks to run when configuring a social driver - * for an initial redirect action. - * Array is keyed by social driver name. - * Callbacks are passed an instance of the driver. - * - * @var array - */ - protected $configureForRedirectCallbacks = []; - - /** - * SocialAuthService constructor. - */ - public function __construct(Socialite $socialite, LoginService $loginService) - { - $this->socialite = $socialite; - $this->loginService = $loginService; + public function __construct( + protected Socialite $socialite, + protected LoginService $loginService, + protected SocialDriverManager $driverManager, + ) { } /** @@ -74,9 +29,10 @@ class SocialAuthService */ public function startLogIn(string $socialDriver): RedirectResponse { - $driver = $this->validateDriver($socialDriver); + $socialDriver = trim(strtolower($socialDriver)); + $this->driverManager->ensureDriverActive($socialDriver); - return $this->getDriverForRedirect($driver)->redirect(); + return $this->getDriverForRedirect($socialDriver)->redirect(); } /** @@ -86,9 +42,10 @@ class SocialAuthService */ public function startRegister(string $socialDriver): RedirectResponse { - $driver = $this->validateDriver($socialDriver); + $socialDriver = trim(strtolower($socialDriver)); + $this->driverManager->ensureDriverActive($socialDriver); - return $this->getDriverForRedirect($driver)->redirect(); + return $this->getDriverForRedirect($socialDriver)->redirect(); } /** @@ -119,9 +76,10 @@ class SocialAuthService */ public function getSocialUser(string $socialDriver): SocialUser { - $driver = $this->validateDriver($socialDriver); + $socialDriver = trim(strtolower($socialDriver)); + $this->driverManager->ensureDriverActive($socialDriver); - return $this->socialite->driver($driver)->user(); + return $this->socialite->driver($socialDriver)->user(); } /** @@ -131,6 +89,7 @@ class SocialAuthService */ public function handleLoginCallback(string $socialDriver, SocialUser $socialUser) { + $socialDriver = trim(strtolower($socialDriver)); $socialId = $socialUser->getId(); // Get any attached social accounts or users @@ -181,76 +140,11 @@ class SocialAuthService } /** - * Ensure the social driver is correct and supported. - * - * @throws SocialDriverNotConfigured + * Get the social driver manager used by this service. */ - protected function validateDriver(string $socialDriver): string + public function drivers(): SocialDriverManager { - $driver = trim(strtolower($socialDriver)); - - if (!in_array($driver, $this->validSocialDrivers)) { - abort(404, trans('errors.social_driver_not_found')); - } - - if (!$this->checkDriverConfigured($driver)) { - throw new SocialDriverNotConfigured(trans('errors.social_driver_not_configured', ['socialAccount' => Str::title($socialDriver)])); - } - - return $driver; - } - - /** - * Check a social driver has been configured correctly. - */ - protected function checkDriverConfigured(string $driver): bool - { - $lowerName = strtolower($driver); - $configPrefix = 'services.' . $lowerName . '.'; - $config = [config($configPrefix . 'client_id'), config($configPrefix . 'client_secret'), config('services.callback_url')]; - - return !in_array(false, $config) && !in_array(null, $config); - } - - /** - * Gets the names of the active social drivers. - * @returns array - */ - public function getActiveDrivers(): array - { - $activeDrivers = []; - - foreach ($this->validSocialDrivers as $driverKey) { - if ($this->checkDriverConfigured($driverKey)) { - $activeDrivers[$driverKey] = $this->getDriverName($driverKey); - } - } - - return $activeDrivers; - } - - /** - * Get the presentational name for a driver. - */ - public function getDriverName(string $driver): string - { - return config('services.' . strtolower($driver) . '.name'); - } - - /** - * Check if the current config for the given driver allows auto-registration. - */ - public function driverAutoRegisterEnabled(string $driver): bool - { - return config('services.' . strtolower($driver) . '.auto_register') === true; - } - - /** - * Check if the current config for the given driver allow email address auto-confirmation. - */ - public function driverAutoConfirmEmailEnabled(string $driver): bool - { - return config('services.' . strtolower($driver) . '.auto_confirm') === true; + return $this->driverManager; } /** @@ -284,33 +178,8 @@ class SocialAuthService $driver->with(['prompt' => 'select_account']); } - if (isset($this->configureForRedirectCallbacks[$driverName])) { - $this->configureForRedirectCallbacks[$driverName]($driver); - } + $this->driverManager->getConfigureForRedirectCallback($driverName)($driver); return $driver; } - - /** - * Add a custom socialite driver to be used. - * Driver name should be lower_snake_case. - * Config array should mirror the structure of a service - * within the `Config/services.php` file. - * Handler should be a Class@method handler to the SocialiteWasCalled event. - */ - public function addSocialDriver( - string $driverName, - array $config, - string $socialiteHandler, - callable $configureForRedirect = null - ) { - $this->validSocialDrivers[] = $driverName; - config()->set('services.' . $driverName, $config); - config()->set('services.' . $driverName . '.redirect', url('/login/service/' . $driverName . '/callback')); - config()->set('services.' . $driverName . '.name', $config['name'] ?? $driverName); - Event::listen(SocialiteWasCalled::class, $socialiteHandler); - if (!is_null($configureForRedirect)) { - $this->configureForRedirectCallbacks[$driverName] = $configureForRedirect; - } - } } diff --git a/app/Access/SocialDriverManager.php b/app/Access/SocialDriverManager.php new file mode 100644 index 000000000..536b2e63d --- /dev/null +++ b/app/Access/SocialDriverManager.php @@ -0,0 +1,147 @@ + + */ + protected array $configureForRedirectCallbacks = []; + + /** + * Check if the current config for the given driver allows auto-registration. + */ + public function isAutoRegisterEnabled(string $driver): bool + { + return $this->getDriverConfigProperty($driver, 'auto_register') === true; + } + + /** + * Check if the current config for the given driver allow email address auto-confirmation. + */ + public function isAutoConfirmEmailEnabled(string $driver): bool + { + return $this->getDriverConfigProperty($driver, 'auto_confirm') === true; + } + + /** + * Gets the names of the active social drivers, keyed by driver id. + * @returns array + */ + public function getActive(): array + { + $activeDrivers = []; + + foreach ($this->validDrivers as $driverKey) { + if ($this->checkDriverConfigured($driverKey)) { + $activeDrivers[$driverKey] = $this->getName($driverKey); + } + } + + return $activeDrivers; + } + + /** + * Get the configure-for-redirect callback for the given driver. + * This is a callable that allows modification of the driver at redirect time. + * Commonly used to perform custom dynamic configuration where required. + * The callback is passed a \Laravel\Socialite\Contracts\Provider instance. + */ + public function getConfigureForRedirectCallback(string $driver): callable + { + return $this->configureForRedirectCallbacks[$driver] ?? (fn() => true); + } + + /** + * Add a custom socialite driver to be used. + * Driver name should be lower_snake_case. + * Config array should mirror the structure of a service + * within the `Config/services.php` file. + * Handler should be a Class@method handler to the SocialiteWasCalled event. + */ + public function addSocialDriver( + string $driverName, + array $config, + string $socialiteHandler, + callable $configureForRedirect = null + ) { + $this->validDrivers[] = $driverName; + config()->set('services.' . $driverName, $config); + config()->set('services.' . $driverName . '.redirect', url('/login/service/' . $driverName . '/callback')); + config()->set('services.' . $driverName . '.name', $config['name'] ?? $driverName); + Event::listen(SocialiteWasCalled::class, $socialiteHandler); + if (!is_null($configureForRedirect)) { + $this->configureForRedirectCallbacks[$driverName] = $configureForRedirect; + } + } + + /** + * Get the presentational name for a driver. + */ + protected function getName(string $driver): string + { + return $this->getDriverConfigProperty($driver, 'name') ?? ''; + } + + protected function getDriverConfigProperty(string $driver, string $property): mixed + { + return config("services.{$driver}.{$property}"); + } + + /** + * Ensure the social driver is correct and supported. + * + * @throws SocialDriverNotConfigured + */ + public function ensureDriverActive(string $driverName): void + { + if (!in_array($driverName, $this->validDrivers)) { + abort(404, trans('errors.social_driver_not_found')); + } + + if (!$this->checkDriverConfigured($driverName)) { + throw new SocialDriverNotConfigured(trans('errors.social_driver_not_configured', ['socialAccount' => Str::title($driverName)])); + } + } + + /** + * Check a social driver has been configured correctly. + */ + protected function checkDriverConfigured(string $driver): bool + { + $lowerName = strtolower($driver); + $configPrefix = 'services.' . $lowerName . '.'; + $config = [config($configPrefix . 'client_id'), config($configPrefix . 'client_secret'), config('services.callback_url')]; + + return !in_array(false, $config) && !in_array(null, $config); + } +} diff --git a/app/App/Providers/AppServiceProvider.php b/app/App/Providers/AppServiceProvider.php index 0275a5489..0f4dc55dd 100644 --- a/app/App/Providers/AppServiceProvider.php +++ b/app/App/Providers/AppServiceProvider.php @@ -2,7 +2,7 @@ namespace BookStack\App\Providers; -use BookStack\Access\SocialAuthService; +use BookStack\Access\SocialDriverManager; use BookStack\Activity\Tools\ActivityLogger; use BookStack\Entities\Models\Book; use BookStack\Entities\Models\Bookshelf; @@ -36,7 +36,7 @@ class AppServiceProvider extends ServiceProvider public $singletons = [ 'activity' => ActivityLogger::class, SettingService::class => SettingService::class, - SocialAuthService::class => SocialAuthService::class, + SocialDriverManager::class => SocialDriverManager::class, CspService::class => CspService::class, HttpRequestService::class => HttpRequestService::class, ]; diff --git a/app/Config/oidc.php b/app/Config/oidc.php index 0410588b8..5f61063f6 100644 --- a/app/Config/oidc.php +++ b/app/Config/oidc.php @@ -36,6 +36,12 @@ return [ 'authorization_endpoint' => env('OIDC_AUTH_ENDPOINT', null), 'token_endpoint' => env('OIDC_TOKEN_ENDPOINT', null), + // OIDC RP-Initiated Logout endpoint + // A null value gets the URL from discovery, if active. + // A false value force-disables RP-Initiated Logout. + // A string value forces the given URL to be used. + 'end_session_endpoint' => env('OIDC_END_SESSION_ENDPOINT', null), + // Add extra scopes, upon those required, to the OIDC authentication request // Multiple values can be provided comma seperated. 'additional_scopes' => env('OIDC_ADDITIONAL_SCOPES', null), @@ -45,11 +51,6 @@ return [ 'user_to_groups' => env('OIDC_USER_TO_GROUPS', false), // Attribute, within a OIDC ID token, to find group names within 'groups_claim' => env('OIDC_GROUPS_CLAIM', 'groups'), - // When syncing groups, remove any groups that no longer match. Otherwise sync only adds new groups. + // When syncing groups, remove any groups that no longer match. Otherwise, sync only adds new groups. 'remove_from_groups' => env('OIDC_REMOVE_FROM_GROUPS', false), - - // OIDC Logout Feature: OAuth2 end_session_endpoint - 'end_session_endpoint' => env('OIDC_END_SESSION_ENDPOINT', null), - ]; - diff --git a/app/Theming/ThemeService.php b/app/Theming/ThemeService.php index 0c2526536..94e471217 100644 --- a/app/Theming/ThemeService.php +++ b/app/Theming/ThemeService.php @@ -2,7 +2,7 @@ namespace BookStack\Theming; -use BookStack\Access\SocialAuthService; +use BookStack\Access\SocialDriverManager; use BookStack\Exceptions\ThemeException; use Illuminate\Console\Application; use Illuminate\Console\Application as Artisan; @@ -82,11 +82,11 @@ class ThemeService } /** - * @see SocialAuthService::addSocialDriver + * @see SocialDriverManager::addSocialDriver */ public function addSocialDriver(string $driverName, array $config, string $socialiteHandler, callable $configureForRedirect = null): void { - $socialAuthService = app()->make(SocialAuthService::class); - $socialAuthService->addSocialDriver($driverName, $config, $socialiteHandler, $configureForRedirect); + $driverManager = app()->make(SocialDriverManager::class); + $driverManager->addSocialDriver($driverName, $config, $socialiteHandler, $configureForRedirect); } } diff --git a/app/Users/Controllers/UserAccountController.php b/app/Users/Controllers/UserAccountController.php index 55776a7f6..708a91e9d 100644 --- a/app/Users/Controllers/UserAccountController.php +++ b/app/Users/Controllers/UserAccountController.php @@ -2,7 +2,7 @@ namespace BookStack\Users\Controllers; -use BookStack\Access\SocialAuthService; +use BookStack\Access\SocialDriverManager; use BookStack\Http\Controller; use BookStack\Permissions\PermissionApplicator; use BookStack\Settings\UserNotificationPreferences; @@ -161,7 +161,7 @@ class UserAccountController extends Controller /** * Show the view for the "Access & Security" account options. */ - public function showAuth(SocialAuthService $socialAuthService) + public function showAuth(SocialDriverManager $socialDriverManager) { $mfaMethods = user()->mfaValues()->get()->groupBy('method'); @@ -171,7 +171,7 @@ class UserAccountController extends Controller 'category' => 'auth', 'mfaMethods' => $mfaMethods, 'authMethod' => config('auth.method'), - 'activeSocialDrivers' => $socialAuthService->getActiveDrivers(), + 'activeSocialDrivers' => $socialDriverManager->getActive(), ]); } diff --git a/app/Users/Controllers/UserController.php b/app/Users/Controllers/UserController.php index 507c7cf06..185d6101c 100644 --- a/app/Users/Controllers/UserController.php +++ b/app/Users/Controllers/UserController.php @@ -2,7 +2,7 @@ namespace BookStack\Users\Controllers; -use BookStack\Access\SocialAuthService; +use BookStack\Access\SocialDriverManager; use BookStack\Exceptions\ImageUploadException; use BookStack\Exceptions\UserUpdateException; use BookStack\Http\Controller; @@ -101,7 +101,7 @@ class UserController extends Controller /** * Show the form for editing the specified user. */ - public function edit(int $id, SocialAuthService $socialAuthService) + public function edit(int $id, SocialDriverManager $socialDriverManager) { $this->checkPermission('users-manage'); @@ -109,7 +109,7 @@ class UserController extends Controller $user->load(['apiTokens', 'mfaValues']); $authMethod = ($user->system_name) ? 'system' : config('auth.method'); - $activeSocialDrivers = $socialAuthService->getActiveDrivers(); + $activeSocialDrivers = $socialDriverManager->getActive(); $mfaMethods = $user->mfaValues->groupBy('method'); $this->setPageTitle(trans('settings.user_profile')); $roles = Role::query()->orderBy('display_name', 'asc')->get(); diff --git a/resources/views/layouts/parts/header-user-menu.blade.php b/resources/views/layouts/parts/header-user-menu.blade.php index ff28f1cfb..db4820a4d 100644 --- a/resources/views/layouts/parts/header-user-menu.blade.php +++ b/resources/views/layouts/parts/header-user-menu.blade.php @@ -29,28 +29,20 @@

  • - -
    - - - - {{ csrf_field() }} - -
    + @php + $logoutPath = match (config('auth.method')) { + 'saml2' => '/saml2/logout', + 'oidc' => '/oidc/logout', + default => '/logout', + } + @endphp +
    + {{ csrf_field() }} + +
  • \ No newline at end of file diff --git a/routes/web.php b/routes/web.php index a02b19ca3..8fc90ee54 100644 --- a/routes/web.php +++ b/routes/web.php @@ -332,8 +332,7 @@ Route::get('/saml2/acs', [AccessControllers\Saml2Controller::class, 'processAcs' // OIDC routes Route::post('/oidc/login', [AccessControllers\OidcController::class, 'login']); Route::get('/oidc/callback', [AccessControllers\OidcController::class, 'callback']); -// OIDC Logout Feature: Added to cater OIDC logout -Route::get('/oidc/logout', [AccessControllers\OidcController::class, 'logout']); +Route::post('/oidc/logout', [AccessControllers\OidcController::class, 'logout']); // User invitation routes Route::get('/register/invite/{token}', [AccessControllers\UserInviteController::class, 'showSetPassword']);