diff --git a/.env.example.complete b/.env.example.complete index 0853bd1fe..e8520a24c 100644 --- a/.env.example.complete +++ b/.env.example.complete @@ -273,6 +273,7 @@ OIDC_USER_TO_GROUPS=false OIDC_GROUPS_CLAIM=groups OIDC_REMOVE_FROM_GROUPS=false OIDC_EXTERNAL_ID_CLAIM=sub +OIDC_END_SESSION_ENDPOINT=false # Disable default third-party services such as Gravatar and Draw.IO # Service-specific options will override this option diff --git a/app/Access/Controllers/LoginController.php b/app/Access/Controllers/LoginController.php index 3b4f9b347..ce872ba88 100644 --- a/app/Access/Controllers/LoginController.php +++ b/app/Access/Controllers/LoginController.php @@ -3,34 +3,26 @@ 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; use BookStack\Http\Controller; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; -use Illuminate\Support\Facades\Auth; use Illuminate\Validation\ValidationException; class LoginController extends Controller { use ThrottlesLogins; - protected SocialAuthService $socialAuthService; - protected LoginService $loginService; - - /** - * Create a new controller instance. - */ - public function __construct(SocialAuthService $socialAuthService, LoginService $loginService) - { + public function __construct( + protected SocialDriverManager $socialDriverManager, + protected 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->loginService = $loginService; } /** @@ -38,7 +30,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'; @@ -52,7 +44,7 @@ class LoginController extends Controller // Store the previous location for redirect after login $this->updateIntendedFromPrevious(); - if (!$preventInitiation && $this->shouldAutoInitiate()) { + if (!$preventInitiation && $this->loginService->shouldAutoInitiate()) { return view('auth.login-initiate', [ 'authMethod' => $authMethod, ]); @@ -101,15 +93,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()); } /** @@ -200,7 +186,7 @@ class LoginController extends Controller { // Store the previous location for redirect after login $previous = url()->previous(''); - $isPreviousFromInstance = (strpos($previous, url('/')) === 0); + $isPreviousFromInstance = str_starts_with($previous, url('/')); if (!$previous || !setting('app-public') || !$isPreviousFromInstance) { return; } @@ -211,23 +197,11 @@ class LoginController extends Controller ]; foreach ($ignorePrefixList as $ignorePrefix) { - if (strpos($previous, url($ignorePrefix)) === 0) { + if (str_starts_with($previous, url($ignorePrefix))) { return; } } 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 e8c944934..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; @@ -63,4 +60,12 @@ class OidcController extends Controller return redirect()->intended(); } + + /** + * Log the user out then start the OIDC RP-initiated logout process. + */ + public function 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..cc48e0f9b 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,33 @@ 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 active based upon authentication config. + */ + public function shouldAutoInitiate(): bool + { + $autoRedirect = config('auth.auto_initiate'); + if (!$autoRedirect) { + return false; + } + + $socialDrivers = $this->socialDriverManager->getActive(); + $authMethod = config('auth.method'); + + return count($socialDrivers) === 0 && in_array($authMethod, ['oidc', 'saml2']); + } } diff --git a/app/Access/Oidc/OidcProviderSettings.php b/app/Access/Oidc/OidcProviderSettings.php index fa3f579b1..bea6a523e 100644 --- a/app/Access/Oidc/OidcProviderSettings.php +++ b/app/Access/Oidc/OidcProviderSettings.php @@ -21,6 +21,7 @@ class OidcProviderSettings public ?string $redirectUri; public ?string $authorizationEndpoint; public ?string $tokenEndpoint; + public ?string $endSessionEndpoint; /** * @var string[]|array[] @@ -132,6 +133,10 @@ class OidcProviderSettings $discoveredSettings['keys'] = $this->filterKeys($keys); } + if (!empty($result['end_session_endpoint'])) { + $discoveredSettings['endSessionEndpoint'] = $result['end_session_endpoint']; + } + return $discoveredSettings; } diff --git a/app/Access/Oidc/OidcService.php b/app/Access/Oidc/OidcService.php index 8778cbd98..f1e5b25af 100644 --- a/app/Access/Oidc/OidcService.php +++ b/app/Access/Oidc/OidcService.php @@ -84,6 +84,7 @@ class OidcService 'redirectUri' => url('/oidc/callback'), 'authorizationEndpoint' => $config['authorization_endpoint'], 'tokenEndpoint' => $config['token_endpoint'], + 'endSessionEndpoint' => is_string($config['end_session_endpoint']) ? $config['end_session_endpoint'] : null, ]); // Use keys if configured @@ -100,6 +101,14 @@ class OidcService } } + // Prevent use of RP-initiated logout if specifically disabled + // Or force use of a URL if specifically set. + if ($config['end_session_endpoint'] === false) { + $settings->endSessionEndpoint = null; + } else if (is_string($config['end_session_endpoint'])) { + $settings->endSessionEndpoint = $config['end_session_endpoint']; + } + $settings->validate(); return $settings; @@ -217,6 +226,8 @@ class OidcService $settings->keys, ); + session()->put("oidc_id_token", $idTokenText); + $returnClaims = Theme::dispatch(ThemeEvents::OIDC_ID_TOKEN_PRE_VALIDATE, $idToken->getAllClaims(), [ 'access_token' => $accessToken->getToken(), 'expires_in' => $accessToken->getExpires(), @@ -284,4 +295,30 @@ class OidcService { return $this->config()['user_to_groups'] !== false; } + + /** + * 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 + * @throws OidcException + */ + public function logout(): string + { + $oidcToken = session()->pull("oidc_id_token"); + $defaultLogoutUrl = url($this->loginService->logout()); + $oidcSettings = $this->getProviderSettings(); + + if (!$oidcSettings->endSessionEndpoint) { + return $defaultLogoutUrl; + } + + $endpointParams = [ + 'id_token_hint' => $oidcToken, + 'post_logout_redirect_uri' => $defaultLogoutUrl, + ]; + + $joiner = str_contains($oidcSettings->endSessionEndpoint, '?') ? '&' : '?'; + + return $oidcSettings->endSessionEndpoint . $joiner . 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 b28b8a41a..7f8f40d41 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 URL. + // A false value force-disables RP-Initiated Logout. + // A true value gets the URL from discovery, if active. + // A string value is used as the URL. + 'end_session_endpoint' => env('OIDC_END_SESSION_ENDPOINT', false), + // 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,6 +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), ]; 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 0440e43d0..db4820a4d 100644 --- a/resources/views/layouts/parts/header-user-menu.blade.php +++ b/resources/views/layouts/parts/header-user-menu.blade.php @@ -29,8 +29,14 @@

  • -
    + @php + $logoutPath = match (config('auth.method')) { + 'saml2' => '/saml2/logout', + 'oidc' => '/oidc/logout', + default => '/logout', + } + @endphp + {{ csrf_field() }}