diff --git a/.env.example.complete b/.env.example.complete index 0853bd1fe..0667cb75b 100644 --- a/.env.example.complete +++ b/.env.example.complete @@ -274,6 +274,10 @@ 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/OidcController.php b/app/Access/Controllers/OidcController.php index e8c944934..083e83e35 100644 --- a/app/Access/Controllers/OidcController.php +++ b/app/Access/Controllers/OidcController.php @@ -63,4 +63,18 @@ class OidcController extends Controller return redirect()->intended(); } + + /** + * OIDC Logout Feature: Start the authorization logout flow via OIDC. + */ + public function logout() + { + try { + return $this->oidcService->logout(); + } catch (OidcException $exception) { + $this->showErrorNotification($exception->getMessage()); + return redirect('/logout'); + } + } + } diff --git a/app/Access/Oidc/OidcService.php b/app/Access/Oidc/OidcService.php index 8778cbd98..1067b0832 100644 --- a/app/Access/Oidc/OidcService.php +++ b/app/Access/Oidc/OidcService.php @@ -217,6 +217,12 @@ class OidcService $settings->keys, ); + // OIDC Logout Feature: Temporarily save token in session + $access_token_for_logout = $idTokenText; + session()->put("oidctoken", $access_token_for_logout); + + + $returnClaims = Theme::dispatch(ThemeEvents::OIDC_ID_TOKEN_PRE_VALIDATE, $idToken->getAllClaims(), [ 'access_token' => $accessToken->getToken(), 'expires_in' => $accessToken->getExpires(), @@ -284,4 +290,37 @@ class OidcService { return $this->config()['user_to_groups'] !== false; } + + + /** + * OIDC Logout Feature: Initiate a logout flow. + * + * @throws OidcException + * + * @return string + */ + public function logout() { + + $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']."/"); + + + } + + + } diff --git a/app/Config/oidc.php b/app/Config/oidc.php index b28b8a41a..0410588b8 100644 --- a/app/Config/oidc.php +++ b/app/Config/oidc.php @@ -47,4 +47,9 @@ return [ 'groups_claim' => env('OIDC_GROUPS_CLAIM', '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/resources/views/layouts/parts/header-user-menu.blade.php b/resources/views/layouts/parts/header-user-menu.blade.php index 0440e43d0..ff28f1cfb 100644 --- a/resources/views/layouts/parts/header-user-menu.blade.php +++ b/resources/views/layouts/parts/header-user-menu.blade.php @@ -29,14 +29,28 @@

  • -
    - {{ csrf_field() }} - -
    + +
    + + + + {{ csrf_field() }} + +
  • \ No newline at end of file diff --git a/routes/web.php b/routes/web.php index c86509c68..a02b19ca3 100644 --- a/routes/web.php +++ b/routes/web.php @@ -332,6 +332,8 @@ 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']); // User invitation routes Route::get('/register/invite/{token}', [AccessControllers\UserInviteController::class, 'showSetPassword']);