use BookStack\Facades\Theme;
use BookStack\Http\HttpRequestService;
use BookStack\Theming\ThemeEvents;
+use BookStack\Uploads\UserAvatars;
use BookStack\Users\Models\User;
use Illuminate\Support\Facades\Cache;
use League\OAuth2\Client\OptionProvider\HttpBasicAuthOptionProvider;
protected RegistrationService $registrationService,
protected LoginService $loginService,
protected HttpRequestService $http,
- protected GroupSyncService $groupService
+ protected GroupSyncService $groupService,
+ protected UserAvatars $userAvatars
) {
}
try {
$idToken->validate($settings->clientId);
} catch (OidcInvalidTokenException $exception) {
- throw new OidcException("ID token validate failed with error: {$exception->getMessage()}");
- }
-
- $userDetails = OidcUserDetails::fromToken(
- $idToken,
- $this->config()['external_id_claim'],
- $this->config()['display_name_claims'] ?? '',
- $this->config()['groups_claim'] ?? ''
- );
-
- // TODO - This should not affect id token validation
- if (!$userDetails->isFullyPopulated($this->shouldSyncGroups()) && !empty($settings->userinfoEndpoint)) {
- $provider = $this->getProvider($settings);
- $request = $provider->getAuthenticatedRequest('GET', $settings->userinfoEndpoint, $accessToken->getToken());
- $response = $provider->getParsedResponse($request);
- // TODO - Ensure response content-type is "application/json" before using in this way (5.3.2)
- // TODO - The sub Claim in the UserInfo Response MUST be verified to exactly match the sub Claim in the ID Token; if they do not match, the UserInfo Response values MUST NOT be used. (5.3.2)
- // TODO - Response validation (5.3.4)
- // TODO - Verify that the OP that responded was the intended OP through a TLS server certificate check, per RFC 6125 [RFC6125].
- // TODO - If the Client has provided a userinfo_encrypted_response_alg parameter during Registration, decrypt the UserInfo Response using the keys specified during Registration.
- // TODO - If the response was signed, the Client SHOULD validate the signature according to JWS [JWS].
- $claims = $idToken->getAllClaims();
- foreach ($response as $key => $value) {
- $claims[$key] = $value;
- }
- // TODO - Should maybe remain separate from IdToken completely
- $idToken->replaceClaims($claims);
+ throw new OidcException("ID token validation failed with error: {$exception->getMessage()}");
}
+ $userDetails = $this->getUserDetailsFromToken($idToken, $accessToken, $settings);
if (empty($userDetails->email)) {
throw new OidcException(trans('errors.oidc_no_email_address'));
}
+ if (empty($userDetails->name)) {
+ $userDetails->name = $userDetails->externalId;
+ }
$isLoggedIn = auth()->check();
if ($isLoggedIn) {
throw new OidcException($exception->getMessage());
}
+ if ($this->config()['fetch_avatar'] && !$user->avatar()->exists() && $userDetails->picture) {
+ $this->userAvatars->assignToUserFromUrl($user, $userDetails->picture);
+ }
+
if ($this->shouldSyncGroups()) {
$detachExisting = $this->config()['remove_from_groups'];
$this->groupService->syncUserWithFoundGroups($user, $userDetails->groups ?? [], $detachExisting);
return $user;
}
+ /**
+ * @throws OidcException
+ */
+ protected function getUserDetailsFromToken(OidcIdToken $idToken, OidcAccessToken $accessToken, OidcProviderSettings $settings): OidcUserDetails
+ {
+ $userDetails = new OidcUserDetails();
+ $userDetails->populate(
+ $idToken,
+ $this->config()['external_id_claim'],
+ $this->config()['display_name_claims'] ?? '',
+ $this->config()['groups_claim'] ?? ''
+ );
+
+ if (!$userDetails->isFullyPopulated($this->shouldSyncGroups()) && !empty($settings->userinfoEndpoint)) {
+ $provider = $this->getProvider($settings);
+ $request = $provider->getAuthenticatedRequest('GET', $settings->userinfoEndpoint, $accessToken->getToken());
+ $response = new OidcUserinfoResponse(
+ $provider->getResponse($request),
+ $settings->issuer,
+ $settings->keys,
+ );
+
+ try {
+ $response->validate($idToken->getClaim('sub'), $settings->clientId);
+ } catch (OidcInvalidTokenException $exception) {
+ throw new OidcException("Userinfo endpoint response validation failed with error: {$exception->getMessage()}");
+ }
+
+ $userDetails->populate(
+ $response,
+ $this->config()['external_id_claim'],
+ $this->config()['display_name_claims'] ?? '',
+ $this->config()['groups_claim'] ?? ''
+ );
+ }
+
+ return $userDetails;
+ }
+
/**
* Get the OIDC config from the application.
*/