From 10c890947f9ea5661729f88e9e85464522498dd7 Mon Sep 17 00:00:00 2001 From: Jasper Weyne Date: Tue, 7 Jul 2020 02:26:00 +0200 Subject: [PATCH] Token expiration and refreshing using the refresh_token flow --- app/Auth/Access/Guards/OpenIdSessionGuard.php | 40 ++++++++++++++++ app/Auth/Access/OpenIdService.php | 46 ++++++++++++++++++- 2 files changed, 84 insertions(+), 2 deletions(-) diff --git a/app/Auth/Access/Guards/OpenIdSessionGuard.php b/app/Auth/Access/Guards/OpenIdSessionGuard.php index 0dfbb67a3..634464493 100644 --- a/app/Auth/Access/Guards/OpenIdSessionGuard.php +++ b/app/Auth/Access/Guards/OpenIdSessionGuard.php @@ -2,6 +2,11 @@ namespace BookStack\Auth\Access\Guards; +use BookStack\Auth\Access\OpenIdService; +use BookStack\Auth\Access\RegistrationService; +use Illuminate\Contracts\Auth\UserProvider; +use Illuminate\Contracts\Session\Session; + /** * OpenId Session Guard * @@ -14,6 +19,41 @@ namespace BookStack\Auth\Access\Guards; */ class OpenIdSessionGuard extends ExternalBaseSessionGuard { + + protected $openidService; + + /** + * OpenIdSessionGuard constructor. + */ + public function __construct( + $name, + UserProvider $provider, + Session $session, + OpenIdService $openidService, + RegistrationService $registrationService + ) { + $this->openidService = $openidService; + parent::__construct($name, $provider, $session, $registrationService); + } + + /** + * Get the currently authenticated user. + * + * @return \Illuminate\Contracts\Auth\Authenticatable|null + */ + public function user() + { + // retrieve the current user + $user = parent::user(); + + // refresh the current user + if ($user && !$this->openidService->refresh()) { + $this->user = null; + } + + return $this->user; + } + /** * Validate a user's credentials. * diff --git a/app/Auth/Access/OpenIdService.php b/app/Auth/Access/OpenIdService.php index 084adfb13..377925d61 100644 --- a/app/Auth/Access/OpenIdService.php +++ b/app/Auth/Access/OpenIdService.php @@ -6,6 +6,7 @@ use BookStack\Exceptions\OpenIdException; use BookStack\Exceptions\UserRegistrationException; use Exception; use Lcobucci\JWT\Token; +use League\OAuth2\Client\Provider\Exception\IdentityProviderException; use OpenIDConnectClient\AccessToken; use OpenIDConnectClient\OpenIDConnectProvider; @@ -53,6 +54,46 @@ class OpenIdService extends ExternalAuthService return ['url' => $url, 'id' => $id]; } + /** + * Refresh the currently logged in user. + * @throws Error + */ + public function refresh(): bool + { + // Retrieve access token for current session + $json = session()->get('openid_token'); + $accessToken = new AccessToken(json_decode($json, true)); + + // Check whether the access token or ID token is expired + if (!$accessToken->getIdToken()->isExpired() && !$accessToken->hasExpired()) { + return true; + } + + // If no refresh token available, logout + if ($accessToken->getRefreshToken() === null) { + $this->actionLogout(); + return false; + } + + // ID token or access token is expired, we refresh it using the refresh token + try { + $provider = $this->getProvider(); + + $accessToken = $provider->getAccessToken('refresh_token', [ + 'refresh_token' => $accessToken->getRefreshToken(), + ]); + } catch (IdentityProviderException $e) { + // Refreshing failed, logout + $this->actionLogout(); + return false; + } + + // A valid token was obtained, we update the access token + session()->put('openid_token', json_encode($accessToken)); + + return true; + } + /** * Process the Authorization response from the authorization server and * return the matching, or new if registration active, user matched to @@ -86,7 +127,7 @@ class OpenIdService extends ExternalAuthService } /** - * Load the underlying Onelogin SAML2 toolkit. + * Load the underlying OpenID Connect Provider. * @throws Error * @throws Exception */ @@ -155,7 +196,7 @@ class OpenIdService extends ExternalAuthService } /** - * Extract the details of a user from a SAML response. + * Extract the details of a user from an ID token. */ protected function getUserDetails(Token $token): array { @@ -202,6 +243,7 @@ class OpenIdService extends ExternalAuthService } auth()->login($user); + session()->put('openid_token', json_encode($accessToken)); return $user; } }