Skip to content

Commit 13a6ed7

Browse files
committed
Add first rough tests for the TokenGenerator.
1 parent 4839e66 commit 13a6ed7

1 file changed

Lines changed: 329 additions & 0 deletions

File tree

tests/unit/TokenGeneratorTest.php

Lines changed: 329 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,329 @@
1+
<?php
2+
3+
namespace Pdsinterop\Solid\Auth;
4+
5+
use Pdsinterop\Solid\Auth\Config\KeysInterface;
6+
use Pdsinterop\Solid\Auth\Config\ServerInterface;
7+
use Pdsinterop\Solid\Auth\Enum\OpenId\OpenIdConnectMetadata as OidcMeta;
8+
use Pdsinterop\Solid\Auth\Utils\Base64Url;
9+
use PHPUnit\Framework\MockObject\MockObject;
10+
use PHPUnit\Framework\TestCase;
11+
12+
function time() { return 1234;}
13+
14+
/**
15+
* @coversDefaultClass \Pdsinterop\Solid\Auth\TokenGenerator
16+
* @covers ::__construct
17+
* @covers ::<!public>
18+
*
19+
* @uses \Pdsinterop\Solid\Auth\Utils\Base64Url
20+
*/
21+
class TokenGeneratorTest extends TestCase
22+
{
23+
////////////////////////////////// FIXTURES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
24+
25+
private MockObject|Config $mockConfig;
26+
private MockObject|KeysInterface $mockKeys;
27+
28+
private function createTokenGenerator($interval = null): TokenGenerator
29+
{
30+
$this->mockConfig = $this->getMockBuilder(Config::class)
31+
->disableOriginalConstructor()
32+
->getMock()
33+
;
34+
35+
$mockInterval = $this->getMockBuilder(\DateInterval::class)
36+
->disableOriginalConstructor()
37+
->getMock()
38+
;
39+
40+
$this->mockKeys = $this->getMockBuilder(KeysInterface::class)
41+
->disableOriginalConstructor()
42+
->getMock()
43+
;
44+
45+
$this->mockConfig->expects($this->atLeast(1))
46+
->method('getKeys')
47+
->willReturn($this->mockKeys)
48+
;
49+
50+
$this->mockKeys->expects($this->once())
51+
->method('getEncryptionKey')
52+
->willReturn('mock encryption key')
53+
;
54+
55+
return new TokenGenerator($this->mockConfig, $interval??$mockInterval);
56+
}
57+
58+
////////////////////////////// CUSTOM ASSERTS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
59+
60+
private function expectArgumentCountError(int $argumentCount): void
61+
{
62+
$this->expectException(\ArgumentCountError::class);
63+
64+
$this->expectExceptionMessageMatches('/Too few arguments [^,]+, ' . ($argumentCount - 1) . ' passed/');
65+
}
66+
67+
/////////////////////////////////// TESTS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
68+
69+
/**
70+
* @testdox Token Generator SHOULD complain WHEN instantiated without Config
71+
*
72+
* @coversNothing
73+
*/
74+
final public function testInstantiateWithoutConfig(): void
75+
{
76+
$this->expectArgumentCountError(1);
77+
78+
new TokenGenerator();
79+
}
80+
81+
/**
82+
* @testdox Token Generator SHOULD be created WHEN instantiated with Config and validity period
83+
*
84+
* @covers ::__construct
85+
*/
86+
final public function testInstantiation(): void
87+
{
88+
$mockConfig = $this->getMockBuilder(Config::class)
89+
->disableOriginalConstructor()
90+
->getMock()
91+
;
92+
93+
$mockInterval = $this->getMockBuilder(\DateInterval::class)
94+
->disableOriginalConstructor()
95+
->getMock()
96+
;
97+
98+
$mockKeys = $this->getMockBuilder(KeysInterface::class)
99+
->disableOriginalConstructor()
100+
->getMock()
101+
;
102+
103+
$mockConfig->expects($this->once())
104+
->method('getKeys')
105+
->willReturn($mockKeys)
106+
;
107+
108+
$mockKeys->expects($this->once())
109+
->method('getEncryptionKey')
110+
->willReturn('mock key')
111+
;
112+
113+
$actual = new TokenGenerator($mockConfig, $mockInterval);
114+
$expected = TokenGenerator::class;
115+
116+
$this->assertInstanceOf($expected, $actual);
117+
}
118+
119+
/**
120+
* @testdox Token Generator SHOULD complain WHEN asked to generate a RegistrationAccessToken without clientId
121+
*
122+
* @covers ::generateRegistrationAccessToken
123+
*/
124+
final public function testRegistrationAccessTokenGenerationWithoutClientId(): void
125+
{
126+
$tokenGenerator = $this->createTokenGenerator();
127+
128+
$this->expectArgumentCountError(1);
129+
130+
$tokenGenerator->generateRegistrationAccessToken();
131+
}
132+
133+
/**
134+
* @testdox Token Generator SHOULD complain WHEN asked to generate a RegistrationAccessToken without privateKey
135+
*
136+
* @covers ::generateRegistrationAccessToken
137+
*/
138+
final public function testRegistrationAccessTokenGenerationWithoutPrivateKey(): void
139+
{
140+
$tokenGenerator = $this->createTokenGenerator();
141+
142+
$this->expectArgumentCountError(2);
143+
144+
$tokenGenerator->generateRegistrationAccessToken('mock client ID');
145+
}
146+
147+
/**
148+
* @testdox Token Generator SHOULD return a RegistrationAccessToken WHEN asked to generate a RegistrationAccessToken with clientId and privateKey
149+
*
150+
* @covers ::generateRegistrationAccessToken
151+
*/
152+
final public function testRegistrationAccessTokenGeneration(): void
153+
{
154+
$tokenGenerator = $this->createTokenGenerator();
155+
156+
$mockServer = $this->getMockBuilder(ServerInterface::class)
157+
->disableOriginalConstructor()
158+
->getMock()
159+
;
160+
161+
$this->mockConfig->expects($this->once())
162+
->method('getServer')
163+
->willReturn($mockServer)
164+
;
165+
166+
$mockServer->expects($this->once())
167+
->method('get')
168+
->with(OidcMeta::ISSUER)
169+
->willReturn('mock issuer')
170+
;
171+
172+
$privateKey = file_get_contents(__DIR__.'/../fixtures/keys/private.key');
173+
174+
$actual = $tokenGenerator->generateRegistrationAccessToken('mock client ID', $privateKey);
175+
176+
$expected = <<<JWT
177+
eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJtb2NrIGlzc3VlciIsImF1ZCI6Im1vY2sgY2xpZW50IElEIiwic3ViIjoibW9jayBjbGllbnQgSUQifQ.hNpQ3R3krigm6iQ-wWXDzy1bq_txFAlymXJZ4VZd9otTqSNZ8qWB1b2MW6RnFjwlTJrbPzD-yksAn0R7Wwhl5NYnvm-YNlQ6mqKybo-DIfFV6uw6b3b8R4BmfcxVhHuiGDYbBvKiO955KGA4t8FLlBBVLPdREz5w-LYRndDSdho3E62eKX5eKndufPRBOO8bzBJBSbrFLf6kj1hape9uyLvNEmeP9Rq26K98GmDRN-BxAoQlC_EodIgqajqETtKSzsf5bBasu6EpSOOOFGRijS8RCqBt4f1Kkb5ltE_t9TBp0ceBSOB6lGJEShieulEf5prBwLrYMkSFCVTpn5Gwow
178+
JWT;
179+
$this->assertEquals($expected, $actual);
180+
}
181+
182+
/**
183+
* @testdox Token Generator SHOULD complain WHEN asked to generate a IdToken without accessToken
184+
*
185+
* @covers ::generateIdToken
186+
*/
187+
final public function testIdTokenGenerationWithoutAccesToken(): void
188+
{
189+
$tokenGenerator = $this->createTokenGenerator();
190+
191+
$this->expectArgumentCountError(1);
192+
193+
$tokenGenerator->generateIdToken();
194+
}
195+
196+
/**
197+
* @testdox Token Generator SHOULD complain WHEN asked to generate a IdToken without clientId
198+
*
199+
* @covers ::generateIdToken
200+
*/
201+
final public function testIdTokenGenerationWithoutClientId(): void
202+
{
203+
$tokenGenerator = $this->createTokenGenerator();
204+
205+
$this->expectArgumentCountError(2);
206+
207+
$tokenGenerator->generateIdToken('mock access token');
208+
}
209+
210+
/**
211+
* @testdox Token Generator SHOULD complain WHEN asked to generate a IdToken without subject
212+
*
213+
* @covers ::generateIdToken
214+
*/
215+
final public function testIdTokenGenerationWithoutSubject(): void
216+
{
217+
$tokenGenerator = $this->createTokenGenerator();
218+
219+
$this->expectArgumentCountError(3);
220+
221+
$tokenGenerator->generateIdToken('mock access token', 'mock clientId');
222+
}
223+
224+
/**
225+
* @testdox Token Generator SHOULD complain WHEN asked to generate a IdToken without nonce
226+
*
227+
* @covers ::generateIdToken
228+
*/
229+
final public function testIdTokenGenerationWithoutNonce(): void
230+
{
231+
$tokenGenerator = $this->createTokenGenerator();
232+
233+
$this->expectArgumentCountError(4);
234+
235+
$tokenGenerator->generateIdToken('mock access token', 'mock clientId', 'mock subject');
236+
}
237+
238+
/**
239+
* @testdox Token Generator SHOULD complain WHEN asked to generate a IdToken without privateKey, $dpopKey
240+
*
241+
* @covers ::generateIdToken
242+
*/
243+
final public function testIdTokenGenerationWithoutPrivateKey(): void
244+
{
245+
$tokenGenerator = $this->createTokenGenerator();
246+
247+
$this->expectArgumentCountError(5);
248+
249+
$tokenGenerator->generateIdToken(
250+
'mock access token',
251+
'mock clientId',
252+
'mock subject',
253+
'mock nonce'
254+
);
255+
}
256+
257+
/**
258+
* @testdox Token Generator SHOULD return a IdToken WHEN asked to generate a IdToken with clientId and privateKey
259+
*
260+
* @covers ::generateIdToken
261+
*
262+
* @uses \Pdsinterop\Solid\Auth\Utils\Jwks
263+
*/
264+
final public function testIdTokenGeneration(): void
265+
{
266+
$validFor = new \DateInterval('PT1S');
267+
268+
$tokenGenerator = $this->createTokenGenerator($validFor);
269+
270+
$mockKey = \Lcobucci\JWT\Signer\Key\InMemory::file(__DIR__.'/../fixtures/keys/public.key');
271+
272+
$this->mockKeys->expects($this->once())
273+
->method('getPublicKey')
274+
->willReturn($mockKey)
275+
;
276+
277+
$mockServer = $this->getMockBuilder(ServerInterface::class)
278+
->disableOriginalConstructor()
279+
->getMock()
280+
;
281+
282+
$this->mockConfig->expects($this->once())
283+
->method('getServer')
284+
->willReturn($mockServer)
285+
;
286+
287+
$mockServer->expects($this->once())
288+
->method('get')
289+
->with(OidcMeta::ISSUER)
290+
->willReturn('mock issuer')
291+
;
292+
293+
$privateKey = file_get_contents(__DIR__.'/../fixtures/keys/private.key');
294+
295+
$now = new \DateTimeImmutable('1234-01-01 12:34:56.789');
296+
297+
$idToken = $tokenGenerator->generateIdToken(
298+
'mock access token',
299+
'mock clientId',
300+
'mock subject',
301+
'mock nonce',
302+
$privateKey,
303+
'mock dpop',
304+
$now
305+
);
306+
307+
[$header, $body,] = explode('.', $idToken);
308+
309+
$header = Base64Url::decode($header);
310+
$body = json_decode(Base64Url::decode($body), true);
311+
312+
$this->assertEquals('{"typ":"JWT","alg":"RS256","kid":"0c3932ca20f3a00ad2eb72035f6cc9cb"}', $header);
313+
314+
// @FIXME: Time-related values are unset, these do need to be tested.
315+
unset($body['exp'], $body['iat'], $body['nbf']);
316+
317+
$this->assertEquals([
318+
'aud' => 'mock clientId',
319+
'azp' => 'mock clientId',
320+
'c_hash' => '1EZBnvsFWlK8ESkgHQsrIQ',
321+
'at_hash' => '1EZBnvsFWlK8ESkgHQsrIQ',
322+
'cnf' => ["jkt" => "mock dpop"],
323+
'iss' => 'mock issuer',
324+
'jti' => '4dc20036dbd8313ed055',
325+
'nonce' => 'mock nonce',
326+
'sub' => 'mock subject',
327+
], $body);
328+
}
329+
}

0 commit comments

Comments
 (0)