Skip to content

Commit 1df558b

Browse files
authored
Merge pull request #597 from wouldsmina/patch-oidc-jwk
Gestion décodage JWT fix #596
2 parents f235745 + db7bab3 commit 1df558b

File tree

4 files changed

+73
-1
lines changed

4 files changed

+73
-1
lines changed

.env.example

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,5 +106,8 @@ OIDC_CLIENT_ID=deming
106106
OIDC_CLIENT_SECRET=deming
107107
OIDC_BASE_URL=http://auth.lan
108108
OIDC_SUFFIX=""
109+
OIDC_USE_ID_TOKEN=false # true pour décoder le JWT
110+
OIDC_JWT_ALG=RS256 # RS256 ou HS256. utile uniquement avec OIDC_USE_ID_TOKEN=true
111+
OIDC_JWT_SECRET_OR_KEY="" # secret pour HS256 ou clé au format PEM pour RS256
109112
OIDC_REDIRECT_URI=${APP_URL}auth/callback/oidc
110113
APP_VERSION=2025.08.13

app/Providers/Socialite/GenericSocialiteProvider.php

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,12 @@
33
namespace App\Providers\Socialite;
44

55
use GuzzleHttp\Exception\GuzzleException;
6+
use Illuminate\Support\Arr;
67
use Laravel\Socialite\Two\AbstractProvider;
78
use Laravel\Socialite\Two\ProviderInterface;
89
use Laravel\Socialite\Two\User;
10+
use Firebase\JWT\JWT;
11+
use Firebase\JWT\Key;
912
use Log;
1013

1114
/**
@@ -96,15 +99,45 @@ protected function getTokenUrl()
9699
return $this->getOIDCUrl() . '/token';
97100
}
98101

102+
/**
103+
* {@inheritdoc}
104+
*/
105+
public function user()
106+
{
107+
if ($this->user) {
108+
return $this->user;
109+
}
110+
111+
if ($this->hasInvalidState()) {
112+
throw new \Laravel\Socialite\Two\InvalidStateException;;
113+
}
114+
115+
$response = $this->getAccessTokenResponse($this->getCode());
116+
117+
$user = $this->getUserByToken(Arr::get($response, 'access_token'), Arr::get($response, 'id_token'));
118+
119+
return $this->userInstance($response, $user);
120+
}
121+
122+
99123
/**
100124
* @param string $token
101125
*
102126
* @throws GuzzleException
103127
*
104128
* @return array|mixed
105129
*/
106-
protected function getUserByToken($token)
130+
protected function getUserByToken($token, $idToken = null)
107131
{
132+
$useIdToken = config('services.oidc.use_id_token', false);
133+
134+
if ($useIdToken) {
135+
if (!$idToken) {
136+
throw new \Exception('OIDC_USE_ID_TOKEN=true but id_token not received');
137+
}
138+
return $this->decodeIdToken($idToken);
139+
}
140+
108141
$base_url = $this->getOIDCUrl() . '/userinfo';
109142
// If userinfo endpoint set, use it instead
110143
if (config('services.oidc.userinfo_endpoint')) {
@@ -140,4 +173,36 @@ protected function mapUserToObject(array $user)
140173
}
141174
return (new User())->setRaw($user)->map($socialite_user);
142175
}
176+
177+
protected function decodeIdToken($idToken)
178+
{
179+
$alg = config('services.oidc.jwt_alg', 'RS256');
180+
$key = config('services.oidc.jwt_secret_or_key');
181+
182+
if (!$key) {
183+
throw new \Exception('JWT secret or public key not configured');
184+
}
185+
186+
try {
187+
$decoded = JWT::decode($idToken, new Key($key, $alg));
188+
} catch (\Exception $e) {
189+
throw new \Exception('Failed to decode ID token: '.$e->getMessage(), 0, $e);
190+
}
191+
192+
$claims = (array) $decoded;
193+
$clientId = config('services.oidc.client_id');
194+
$expectedIssuer = rtrim(config('services.oidc.issuer', $this->getOIDCUrl()), '/');
195+
$aud = $claims['aud'] ?? null;
196+
$audiences = is_array($aud) ? $aud : ($aud !== null ? [$aud] : []);
197+
if (($claims['iss'] ?? null) !== $expectedIssuer) {
198+
throw new \Exception('Invalid ID token issuer');
199+
}
200+
if (!in_array($clientId, $audiences, true)) {
201+
throw new \Exception('Invalid ID token audience');
202+
}
203+
if (count($audiences) > 1 && ($claims['azp'] ?? null) !== $clientId) {
204+
throw new \Exception('Invalid ID token authorized party');
205+
}
206+
return $claims;
207+
}
143208
}

composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
"license": "GPLv3",
77
"require": {
88
"php": "^8.2",
9+
"firebase/php-jwt": "^7.0",
910
"directorytree/ldaprecord-laravel": "^3.4",
1011
"erusev/parsedown": "^1.7",
1112
"laravel/framework": "^11.9",

config/services.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,9 @@
7171
'authorize_endpoint' => env('OIDC_AUTHORIZE_ENDPOINT', null),
7272
'token_endpoint' => env('OIDC_TOKEN_ENDPOINT', null),
7373
'userinfo_endpoint' => env('OIDC_USERINFO_ENDPOINT', null),
74+
'use_id_token' => env('OIDC_USE_ID_TOKEN', false),
75+
'jwt_alg' => env('OIDC_JWT_ALG', 'RS256'),
76+
'jwt_secret_or_key' => env('OIDC_JWT_SECRET_OR_KEY', ''),
7477
'map_user_attr' => [
7578
'id' => 'sub',
7679
'name' => 'name',

0 commit comments

Comments
 (0)