Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 32 additions & 10 deletions src/Microsoft/Provider.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class Provider extends AbstractProvider

private const JWKS_CACHE_TTL_SECONDS = 300;

private mixed $openIdConfiguration = null;
private ?array $openIdConfiguration = null;

private ?array $jwtKeys = null;

Expand Down Expand Up @@ -303,8 +303,8 @@ private function getJWTKeysWithCache(bool $forceRefresh): array
return $this->jwtKeys;
}

$jwksUri = $this->getOpenIdConfiguration()->jwks_uri;
$cacheKey = 'socialite:microsoft:jwks:' . sha1((string) $jwksUri);
$jwksUri = $this->getOpenIdConfiguration()['jwks_uri'];
$cacheKey = 'socialite:microsoft:jwks-v2:' . sha1((string) $jwksUri);

$fetch = function () use ($jwksUri, $forceRefresh) {
$options = [
Expand Down Expand Up @@ -341,11 +341,11 @@ private function getJWTKeysWithCache(bool $forceRefresh): array
/**
* Get OpenID Configuration.
*
* @return mixed
* @return array<string, mixed>
*
* @throws \Laravel\Socialite\Two\InvalidStateException
*/
private function getOpenIdConfiguration(): mixed
private function getOpenIdConfiguration(): array
{
if ($this->openIdConfiguration !== null) {
return $this->openIdConfiguration;
Expand All @@ -358,13 +358,13 @@ private function getOpenIdConfiguration(): mixed
//
$discovery = sprintf('https://login.microsoftonline.com/%s/v2.0/.well-known/openid-configuration', $this->getConfig('tenant', 'common'));

$cacheKey = 'socialite:microsoft:openid:' . sha1((string) $discovery);
$cacheKey = 'socialite:microsoft:openid-v2:' . sha1((string) $discovery);

if (class_exists(\Illuminate\Support\Facades\Cache::class)) {
$this->openIdConfiguration = \Illuminate\Support\Facades\Cache::remember($cacheKey, self::OPENID_CONFIGURATION_CACHE_TTL_SECONDS, function () use ($discovery) {
$response = $this->getHttpClient()->get($discovery, [RequestOptions::PROXY => $this->getConfig('proxy')]);

return json_decode((string) $response->getBody());
return $this->decodeOpenIdConfiguration((string) $response->getBody());
});

Comment thread
atymic marked this conversation as resolved.
return $this->openIdConfiguration;
Comment thread
atymic marked this conversation as resolved.
Expand All @@ -375,11 +375,33 @@ private function getOpenIdConfiguration(): mixed
throw new InvalidStateException("Error on getting OpenID Configuration. {$ex}");
}

$this->openIdConfiguration = json_decode((string) $response->getBody());
$this->openIdConfiguration = $this->decodeOpenIdConfiguration((string) $response->getBody());

return $this->openIdConfiguration;
}

/**
* Decode the OpenID configuration JSON response as an associative array.
*
* @return array<string, mixed>
*
* @throws \Laravel\Socialite\Two\InvalidStateException
*/
private function decodeOpenIdConfiguration(string $body): array
{
try {
$decoded = json_decode($body, true, 512, JSON_THROW_ON_ERROR);
} catch (\JsonException $e) {
throw new InvalidStateException("Failed to decode OpenID configuration. {$e->getMessage()}");
}

if (!is_array($decoded) || !isset($decoded['jwks_uri'], $decoded['issuer'])) {
throw new InvalidStateException('Invalid OpenID configuration: missing required keys (jwks_uri, issuer).');
}

return $decoded;
}

/**
* Extract algorithm from header, failing that defer to OIDC speced supported algorithms then service's default.
*
Expand All @@ -390,7 +412,7 @@ private function getTokenSigningAlgorithm($jwtHeader): string
{
return $jwtHeader?->alg ?? (string) collect(
array_merge(
$this->getOpenIdConfiguration()->id_token_signing_alg_values_supported,
$this->getOpenIdConfiguration()['id_token_signing_alg_values_supported'],
[$this->getConfig('default_algorithm', 'RS256')]
)
)->first();
Expand Down Expand Up @@ -437,7 +459,7 @@ private function validate(string $idToken)
// iss validation - a security token service (STS) URI
// Identifies the STS that constructs and returns the token, and the Microsoft Entra tenant of the authenticated user.
// https://learn.microsoft.com/en-au/entra/identity-platform/access-tokens#multitenant-applications
$issuer = str_replace('{tenantid}', $jwtPayload->tid, $this->getOpenIdConfiguration()->issuer);
$issuer = str_replace('{tenantid}', $jwtPayload->tid, $this->getOpenIdConfiguration()['issuer']);
if (strcmp($iss = $jwtPayload->iss, $issuer)) {
throw new InvalidStateException('iss on id_token does not match issuer value on the OpenID configuration');
}
Expand Down
Loading