Skip to content

Commit 6ba58e5

Browse files
test: add test to test exception when sub claims differ.
1 parent 6d1efec commit 6ba58e5

File tree

1 file changed

+105
-6
lines changed

1 file changed

+105
-6
lines changed

tests/Feature/Http/Controllers/LoginControllerResponseTest.php

Lines changed: 105 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -152,14 +152,14 @@ public function testStateDoesNotMatch(): void
152152
Config::set('oidc.client_secret', 'the-secret-client-secret');
153153

154154
// Mock LoginResponseHandlerInterface to check handleExceptionWhileAuthenticate is called.
155-
$mock = Mockery::mock(ExceptionHandlerInterface::class);
156-
$mock
155+
$mockExceptionHandler = Mockery::mock(ExceptionHandlerInterface::class);
156+
$mockExceptionHandler
157157
->shouldReceive('handleExceptionWhileAuthenticate')
158158
->withArgs(function (OpenIDConnectClientException $e) {
159159
return $e->getMessage() === 'Unable to determine state';
160160
})
161161
->once();
162-
$this->app->instance(ExceptionHandlerInterface::class, $mock);
162+
$this->app->instance(ExceptionHandlerInterface::class, $mockExceptionHandler);
163163

164164
// Set the current state, which is usually generated and saved in the session before login,
165165
// and sent to the issuer during the login redirect.
@@ -278,14 +278,14 @@ public function testIdTokenSignedWithIncorrectClientSecret(): void
278278
Config::set('oidc.client_secret', 'the-secret-client-secret');
279279

280280
// Mock LoginResponseHandlerInterface to check handleExceptionWhileAuthenticate is called.
281-
$mock = Mockery::mock(ExceptionHandlerInterface::class);
282-
$mock
281+
$mockExceptionHandler = Mockery::mock(ExceptionHandlerInterface::class);
282+
$mockExceptionHandler
283283
->shouldReceive('handleExceptionWhileAuthenticate')
284284
->withArgs(function (OpenIDConnectClientException $e) {
285285
return $e->getMessage() === 'Unable to verify signature';
286286
})
287287
->once();
288-
$this->app->instance(ExceptionHandlerInterface::class, $mock);
288+
$this->app->instance(ExceptionHandlerInterface::class, $mockExceptionHandler);
289289

290290
// Set the current state, which is usually generated and saved in the session before login,
291291
// and sent to the issuer during the login redirect.
@@ -413,6 +413,105 @@ public function testIdTokenAndUserinfoSignedWithClientSecret(): void
413413
});
414414
}
415415

416+
public function testSubClaimIdTokenDoesNotEqualsSubClaimUserinfo(): void
417+
{
418+
$idToken = generateJwt([
419+
"iss" => "https://provider.rdobeheer.nl",
420+
"aud" => 'test-client-id',
421+
"sub" => 'test-subject',
422+
], 'the-secret-client-secret');
423+
424+
$signedUserInfo = generateJwt([
425+
"iss" => "https://provider.rdobeheer.nl",
426+
"aud" => 'test-client-id',
427+
"sub" => 'different-subject',
428+
"email" => '[email protected]',
429+
], 'the-secret-client-secret');
430+
431+
Http::fake([
432+
// Token requested by OpenIDConnectClient::authenticate() function.
433+
'https://provider.rdobeheer.nl/token' => Http::response([
434+
'access_token' => 'access-token-from-token-endpoint',
435+
'id_token' => $idToken,
436+
'token_type' => 'Bearer',
437+
'expires_in' => 3600,
438+
]),
439+
// User info requested by OpenIDConnectClient::requestUserInfo() function.
440+
'https://provider.rdobeheer.nl/userinfo?schema=openid' => Http::response(
441+
body: $signedUserInfo,
442+
status: 200,
443+
headers: [
444+
'Content-Type' => 'application/jwt',
445+
]
446+
),
447+
]);
448+
449+
// Set OIDC config
450+
$this->mockOpenIDConfigurationLoader();
451+
452+
Config::set('oidc.issuer', 'https://provider.rdobeheer.nl');
453+
Config::set('oidc.client_id', 'test-client-id');
454+
Config::set('oidc.client_secret', 'the-secret-client-secret');
455+
456+
// Mock LoginResponseHandlerInterface to check handleExceptionWhileRequestUserInfo is called.
457+
$mockExceptionHandler = Mockery::mock(ExceptionHandlerInterface::class);
458+
$mockExceptionHandler
459+
->shouldReceive('handleExceptionWhileRequestUserInfo')
460+
->withArgs(function (OpenIDConnectClientException $e) {
461+
return $e->getMessage() === 'Invalid JWT signature';
462+
})
463+
->once();
464+
$this->app->instance(ExceptionHandlerInterface::class, $mockExceptionHandler);
465+
466+
// Set the current state, which is usually generated and saved in the session before login,
467+
// and sent to the issuer during the login redirect.
468+
Session::put('openid_connect_state', 'some-state');
469+
470+
// We simulate here that the user now comes back after successful login at issuer.
471+
$this->getRoute('oidc.login', ['code' => 'some-code', 'state' => 'some-state']);
472+
473+
$this->assertEmpty(session('openid_connect_state'));
474+
$this->assertEmpty(session('openid_connect_nonce'));
475+
476+
Http::assertSentCount(2);
477+
Http::assertSentInOrder([
478+
'https://provider.rdobeheer.nl/token',
479+
'https://provider.rdobeheer.nl/userinfo?schema=openid',
480+
]);
481+
Http::assertSent(function (Request $request) {
482+
if ($request->url() === 'https://provider.rdobeheer.nl/token') {
483+
$this->assertSame(
484+
expected: 'POST',
485+
actual: $request->method(),
486+
);
487+
$this->assertSame(
488+
expected: 'grant_type=authorization_code'
489+
. '&code=some-code'
490+
. '&redirect_uri=http%3A%2F%2Flocalhost%2Foidc%2Flogin'
491+
. '&client_id=test-client-id'
492+
. '&client_secret=the-secret-client-secret',
493+
actual: $request->body(),
494+
);
495+
return true;
496+
}
497+
498+
if ($request->url() === 'https://provider.rdobeheer.nl/userinfo?schema=openid') {
499+
$this->assertSame(
500+
expected: 'GET',
501+
actual: $request->method(),
502+
);
503+
$this->assertSame(
504+
expected: [
505+
'Bearer access-token-from-token-endpoint'
506+
],
507+
actual: $request->header('Authorization'),
508+
);
509+
}
510+
511+
return true;
512+
});
513+
}
514+
416515
public function testTokenSignedWithPrivateKey(): void
417516
{
418517
Http::fake([

0 commit comments

Comments
 (0)