X-Git-Url: http://source.bookstackapp.com/bookstack/blobdiff_plain/97cde9c56a3268da179c2701d209a9a1224bac85..refs/pull/2169/head:/app/Auth/Access/OpenIdService.php diff --git a/app/Auth/Access/OpenIdService.php b/app/Auth/Access/OpenIdService.php index 14b6ac9a5..70df963f5 100644 --- a/app/Auth/Access/OpenIdService.php +++ b/app/Auth/Access/OpenIdService.php @@ -8,7 +8,6 @@ use Exception; use Lcobucci\JWT\Token; use League\OAuth2\Client\Provider\Exception\IdentityProviderException; use OpenIDConnectClient\AccessToken; -use OpenIDConnectClient\Exception\InvalidTokenException; use OpenIDConnectClient\OpenIDConnectProvider; /** @@ -63,41 +62,72 @@ class OpenIdService extends ExternalAuthService { // Retrieve access token for current session $json = session()->get('openid_token'); - $accessToken = new AccessToken(json_decode($json, true)); - // Check if both the access token and the ID token (if present) are unexpired - $idToken = $accessToken->getIdToken(); - if (!$accessToken->hasExpired() && (!$idToken || !$idToken->isExpired())) { + // If no access token was found, reject the refresh + if (!$json) { + $this->actionLogout(); + return false; + } + + $accessToken = new AccessToken(json_decode($json, true) ?? []); + + // If the token is not expired, refreshing isn't necessary + if ($this->isUnexpired($accessToken)) { return true; } - // If no refresh token available, logout - if ($accessToken->getRefreshToken() === null) { + // Try to obtain refreshed access token + try { + $newAccessToken = $this->refreshAccessToken($accessToken); + } catch (\Exception $e) { + // Log out if an unknown problem arises + $this->actionLogout(); + throw $e; + } + + // If a token was obtained, update the access token, otherwise log out + if ($newAccessToken !== null) { + session()->put('openid_token', json_encode($newAccessToken)); + return true; + } else { $this->actionLogout(); return false; } + } + + /** + * Check whether an access token or OpenID token isn't expired. + */ + protected function isUnexpired(AccessToken $accessToken): bool + { + $idToken = $accessToken->getIdToken(); + + $accessTokenUnexpired = $accessToken->getExpires() && !$accessToken->hasExpired(); + $idTokenUnexpired = !$idToken || !$idToken->isExpired(); + + return $accessTokenUnexpired && $idTokenUnexpired; + } + + /** + * Generate an updated access token, through the associated refresh token. + * @throws Error + */ + protected function refreshAccessToken(AccessToken $accessToken): ?AccessToken + { + // If no refresh token available, abort + if ($accessToken->getRefreshToken() === null) { + return null; + } // ID token or access token is expired, we refresh it using the refresh token try { - $provider = $this->getProvider(); - - $accessToken = $provider->getAccessToken('refresh_token', [ + return $this->getProvider()->getAccessToken('refresh_token', [ 'refresh_token' => $accessToken->getRefreshToken(), ]); } catch (IdentityProviderException $e) { - // Refreshing failed, logout - $this->actionLogout(); - return false; - } catch (\Exception $e) { - // Unknown error, logout and throw - $this->actionLogout(); - throw $e; + // Refreshing failed + return null; } - - // A valid token was obtained, we update the access token - session()->put('openid_token', json_encode($accessToken)); - - return true; } /** @@ -139,6 +169,7 @@ class OpenIdService extends ExternalAuthService */ protected function getProvider(): OpenIDConnectProvider { + // Setup settings $settings = $this->config['openid']; $overrides = $this->config['openid_overrides'] ?? []; @@ -149,12 +180,27 @@ class OpenIdService extends ExternalAuthService $openIdSettings = $this->loadOpenIdDetails(); $settings = array_replace_recursive($settings, $openIdSettings, $overrides); - $signer = new \Lcobucci\JWT\Signer\Rsa\Sha256(); - return new OpenIDConnectProvider($settings, ['signer' => $signer]); + // Setup services + $services = $this->loadOpenIdServices(); + $overrides = $this->config['openid_services'] ?? []; + + $services = array_replace_recursive($services, $overrides); + + return new OpenIDConnectProvider($settings, $services); + } + + /** + * Load services utilized by the OpenID Connect provider. + */ + protected function loadOpenIdServices(): array + { + return [ + 'signer' => new \Lcobucci\JWT\Signer\Rsa\Sha256(), + ]; } /** - * Load dynamic service provider options required by the onelogin toolkit. + * Load dynamic service provider options required by the OpenID Connect provider. */ protected function loadOpenIdDetails(): array {