Skip to content

Commit a5d9c9b

Browse files
committed
WIP
1 parent 97dc0c6 commit a5d9c9b

3 files changed

Lines changed: 160 additions & 131 deletions

File tree

src/Controllers/EndSessionController.php

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,9 @@ public function __invoke(ServerRequestInterface $request): Response
6565
(string)$idTokenHint->claims()->get('sid');
6666
}
6767

68-
$this->loggerService->debug('EndSession: ID Token Hint Session ID: ' . $sidClaim ?? 'N/A');
68+
$this->loggerService->debug(
69+
'EndSession: ID Token Hint Session ID: ' . var_export($sidClaim, true),
70+
);
6971

7072
// Check if RP is requesting logout for session that previously existed (not this current session).
7173
// Claim 'sid' from 'id_token_hint' logout parameter indicates for which session should log out be
@@ -119,7 +121,9 @@ public function __invoke(ServerRequestInterface $request): Response
119121
$this->sessionService->getCurrentSession()->doLogout($authSourceId);
120122
}
121123
} else {
122-
$this->loggerService->debug('Current session authorities not found for ID: ' . $sidClaim);
124+
$this->loggerService->debug(
125+
'Current session authorities not found for ID: ' . var_export($sidClaim, true),
126+
);
123127
}
124128

125129
$this->loggerService->debug('Was logout action called: ' . var_export($wasLogoutActionCalled, true));
@@ -213,12 +217,12 @@ protected function resolveResponse(LogoutRequest $logoutRequest, bool $wasLogout
213217
'Logout request includes post-logout redirect URI: ' . $postLogoutRedirectUri,
214218
);
215219

216-
if ($logoutRequest->getState() !== null) {
220+
if (($logoutState = $logoutRequest->getState()) !== null) {
217221
$this->loggerService->debug(
218-
'Appending logout request state: ' . $logoutRequest->getState(),
222+
'Appending logout request state: ' . $logoutState,
219223
);
220224
$postLogoutRedirectUri .= (!str_contains($postLogoutRedirectUri, '?')) ? '?' : '&';
221-
$postLogoutRedirectUri .= http_build_query(['state' => $logoutRequest->getState()]);
225+
$postLogoutRedirectUri .= http_build_query(['state' => $logoutState]);
222226
} else {
223227
$this->loggerService->debug(
224228
'No state provided for post logout',

tests/unit/src/Server/ResponseTypes/TokenResponseTest.php

Lines changed: 40 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,7 @@
77
use DateTimeImmutable;
88
use Exception;
99
use Laminas\Diactoros\Response;
10-
use Lcobucci\Clock\SystemClock;
11-
use Lcobucci\JWT\Encoding\JoseEncoder;
12-
use Lcobucci\JWT\Signer\Key\InMemory;
1310
use Lcobucci\JWT\Signer\Rsa\Sha256;
14-
use Lcobucci\JWT\Token\Parser;
15-
use Lcobucci\JWT\Validation\Constraint\IdentifiedBy;
16-
use Lcobucci\JWT\Validation\Constraint\IssuedBy;
17-
use Lcobucci\JWT\Validation\Constraint\PermittedFor;
18-
use Lcobucci\JWT\Validation\Constraint\RelatedTo;
19-
use Lcobucci\JWT\Validation\Constraint\SignedWith;
20-
use Lcobucci\JWT\Validation\Constraint\StrictValidAt;
21-
use Lcobucci\JWT\Validation\Validator;
2211
use League\OAuth2\Server\CryptKey;
2312
use PHPUnit\Framework\MockObject\MockObject;
2413
use PHPUnit\Framework\MockObject\Stub;
@@ -36,6 +25,12 @@
3625
use SimpleSAML\Module\oidc\Services\JsonWebTokenBuilderService;
3726
use SimpleSAML\Module\oidc\Services\LoggerService;
3827
use SimpleSAML\Module\oidc\Utils\ClaimTranslatorExtractor;
28+
use SimpleSAML\OpenID\Algorithms\SignatureAlgorithmEnum;
29+
use SimpleSAML\OpenID\Core;
30+
use SimpleSAML\OpenID\Core\Factories\IdTokenFactory;
31+
use SimpleSAML\OpenID\Core\IdToken;
32+
use SimpleSAML\OpenID\ValueAbstracts\SignatureKeyPair;
33+
use SimpleSAML\OpenID\ValueAbstracts\SignatureKeyPairBag;
3934

4035
/**
4136
* @covers \SimpleSAML\Module\oidc\Server\ResponseTypes\TokenResponse
@@ -61,6 +56,11 @@ class TokenResponseTest extends TestCase
6156
protected IdTokenBuilder $idTokenBuilder;
6257
protected Stub $claimSetEntityFactoryStub;
6358
protected MockObject $loggerMock;
59+
protected MockObject $coreMock;
60+
protected MockObject $protocolSignatureKeyPairBagMock;
61+
protected MockObject $idTokenFactoryMock;
62+
protected MockObject $idTokenMock;
63+
protected MockObject $signatureKeyPairMock;
6464

6565
/**
6666
* @throws \PHPUnit\Framework\MockObject\Exception
@@ -117,12 +117,31 @@ protected function setUp(): void
117117

118118
$this->claimSetEntityFactoryStub = $this->createStub(ClaimSetEntityFactory::class);
119119

120+
$this->idTokenFactoryMock = $this->createMock(IdTokenFactory::class);
121+
122+
$this->coreMock = $this->createMock(Core::class);
123+
$this->coreMock->method('idTokenFactory')->willReturn($this->idTokenFactoryMock);
124+
120125
$this->idTokenBuilder = new IdTokenBuilder(
121126
new JsonWebTokenBuilderService($this->moduleConfigMock),
122127
new ClaimTranslatorExtractor(self::USER_ID_ATTR, $this->claimSetEntityFactoryStub),
128+
$this->coreMock,
129+
$this->moduleConfigMock,
123130
);
124131

125132
$this->loggerMock = $this->createMock(LoggerService::class);
133+
134+
$this->protocolSignatureKeyPairBagMock = $this->createMock(SignatureKeyPairBag::class);
135+
$this->signatureKeyPairMock = $this->createMock(SignatureKeyPair::class);
136+
$this->signatureKeyPairMock->method('getSignatureAlgorithm')
137+
->willReturn(SignatureAlgorithmEnum::RS256);
138+
$this->protocolSignatureKeyPairBagMock->method('getFirstOrFail')
139+
->willReturn($this->signatureKeyPairMock);
140+
141+
$this->moduleConfigMock->method('getProtocolSignatureKeyPairBag')
142+
->willReturn($this->protocolSignatureKeyPairBagMock);
143+
144+
$this->idTokenMock = $this->createMock(IdToken::class);
126145
}
127146

128147
protected function prepareMockedInstance(): TokenResponse
@@ -157,6 +176,11 @@ public function testItCanGenerateResponse(): void
157176
{
158177
$this->accessTokenEntityMock->method('getRequestedClaims')->willReturn([]);
159178
$this->accessTokenEntityMock->method('getScopes')->willReturn($this->scopes);
179+
$this->idTokenFactoryMock->method('fromData')
180+
->willReturn($this->idTokenMock);
181+
$this->idTokenMock->expects($this->once())
182+
->method('getToken')
183+
->willReturn('token');
160184
$idTokenResponse = $this->prepareMockedInstance();
161185
$idTokenResponse->setAccessToken($this->accessTokenEntityMock);
162186
$response = $idTokenResponse->generateHttpResponse(new Response());
@@ -191,6 +215,11 @@ public function testItCanGenerateResponseWithIndividualRequestedClaims(): void
191215
$this->accessTokenEntityMock->method('getScopes')->willReturn(
192216
[new ScopeEntity('openid')],
193217
);
218+
$this->idTokenFactoryMock->method('fromData')
219+
->willReturn($this->idTokenMock);
220+
$this->idTokenMock->expects($this->once())
221+
->method('getToken')
222+
->willReturn('token');
194223
$idTokenResponse->setAccessToken($this->accessTokenEntityMock);
195224
$response = $idTokenResponse->generateHttpResponse(new Response());
196225

@@ -234,61 +263,6 @@ protected function shouldHaveValidIdToken(string $body, $expectedClaims = []): b
234263
);
235264
}
236265

237-
// Check ID token
238-
$validator = new Validator();
239-
/** @var Plain $token */
240-
$token = (new Parser(new JoseEncoder()))->parse($result['id_token']);
241-
242-
$validator->assert(
243-
$token,
244-
new IdentifiedBy(self::TOKEN_ID),
245-
new IssuedBy(self::ISSUER),
246-
new PermittedFor(self::CLIENT_ID),
247-
new RelatedTo(self::SUBJECT),
248-
new StrictValidAt(SystemClock::fromUTC()),
249-
new SignedWith(
250-
new Sha256(),
251-
InMemory::plainText(file_get_contents($this->certFolder . '/oidc_module.crt')),
252-
),
253-
);
254-
255-
if ($token->headers()->get('kid') !== self::KEY_ID) {
256-
throw new Exception(
257-
'Wrong key id. Expected ' . self::KEY_ID . ' was ' . $token->headers()->get('kid'),
258-
);
259-
}
260-
$expectedClaimsKeys = array_keys($expectedClaims);
261-
$expectedClaimsKeys = ['iss', 'iat', 'jti', 'aud', 'nbf', 'exp', 'sub', 'at_hash', ...$expectedClaimsKeys];
262-
$claims = array_keys($token->claims()->all());
263-
if ($claims !== $expectedClaimsKeys) {
264-
throw new Exception(
265-
'missing expected claim. Got ' . var_export($claims, true)
266-
. ' need ' . var_export($expectedClaimsKeys, true),
267-
);
268-
}
269-
foreach ($expectedClaims as $claim => $value) {
270-
$valFromToken = $token->claims()->get($claim);
271-
if ($value !== $valFromToken) {
272-
throw new Exception(
273-
'Expected claim value ' . var_export($value, true)
274-
. ' got ' . var_export($valFromToken, true),
275-
);
276-
}
277-
}
278-
279-
$dateWithNoMicroseconds = ['nbf', 'exp', 'iat'];
280-
foreach ($dateWithNoMicroseconds as $key) {
281-
/**
282-
* @var DateTimeImmutable $val
283-
*/
284-
$val = $token->claims()->get($key);
285-
//Get format representing microseconds
286-
$val = $val->format('u');
287-
if ($val !== '000000') {
288-
throw new Exception("Value for '$key' has microseconds. micros '$val'");
289-
}
290-
}
291-
292266
return true;
293267
}
294268
}

0 commit comments

Comments
 (0)