|
20 | 20 | */
|
21 | 21 | class ResetPasswordTokenGeneratorTest extends TestCase
|
22 | 22 | {
|
23 |
| - /** |
24 |
| - * @var MockObject|ResetPasswordRandomGenerator |
25 |
| - */ |
26 |
| - private $mockRandomGenerator; |
27 |
| - |
28 |
| - /** |
29 |
| - * @var MockObject|\DateTimeImmutable |
30 |
| - */ |
31 |
| - private $mockExpiresAt; |
| 23 | + private MockObject&\DateTimeImmutable $mockExpiresAt; |
| 24 | + private ResetPasswordTokenGenerator $tokenGenerator; |
32 | 25 |
|
33 | 26 | protected function setUp(): void
|
34 | 27 | {
|
35 |
| - $this->mockRandomGenerator = $this->createMock(ResetPasswordRandomGenerator::class); |
36 | 28 | $this->mockExpiresAt = $this->createMock(\DateTimeImmutable::class);
|
| 29 | + $this->tokenGenerator = new ResetPasswordTokenGenerator('secret-key', new ResetPasswordRandomGenerator()); |
37 | 30 | }
|
38 | 31 |
|
39 |
| - public function testSelectorGeneratedByRandomGenerator(): void |
| 32 | + public function testCreateTokenReturnsValidHashedTokenComponents(): void |
40 | 33 | {
|
41 |
| - $this->mockRandomGenerator |
42 |
| - ->expects($this->exactly(2)) |
43 |
| - ->method('getRandomAlphaNumStr') |
44 |
| - ; |
| 34 | + $result = $this->tokenGenerator->createToken($this->mockExpiresAt, 'userId'); |
45 | 35 |
|
46 |
| - $generator = $this->getTokenGenerator(); |
47 |
| - $generator->createToken($this->mockExpiresAt, 'userId'); |
48 |
| - } |
| 36 | + // The public token = "selector token" + "verifier token" |
| 37 | + self::assertSame(20, \strlen($result->getSelector())); |
| 38 | + self::assertSame(40, \strlen($result->getPublicToken())); |
49 | 39 |
|
50 |
| - public function testHashedTokenIsCreatedWithExpectedParams(): void |
51 |
| - { |
52 |
| - $this->mockRandomGenerator |
53 |
| - ->expects($this->exactly(2)) |
54 |
| - ->method('getRandomAlphaNumStr') |
55 |
| - ->willReturnOnConsecutiveCalls('verifier', 'selector') |
56 |
| - ; |
57 |
| - |
58 |
| - $this->mockExpiresAt |
59 |
| - ->expects($this->once()) |
60 |
| - ->method('getTimestamp') |
61 |
| - ->willReturn(2020) |
62 |
| - ; |
63 |
| - |
64 |
| - $expected = hash_hmac( |
65 |
| - 'sha256', |
66 |
| - json_encode(['verifier', 'user1234', 2020]), |
67 |
| - 'key', |
68 |
| - true |
69 |
| - ); |
70 |
| - |
71 |
| - $generator = $this->getTokenGenerator(); |
72 |
| - $result = $generator->createToken($this->mockExpiresAt, 'user1234'); |
73 |
| - |
74 |
| - self::assertSame(base64_encode($expected), $result->getHashedToken()); |
| 40 | + $verifier = substr($result->getPublicToken(), 20, 20); |
| 41 | + |
| 42 | + $expectedHash = base64_encode(hash_hmac( |
| 43 | + algo: 'sha256', |
| 44 | + data: json_encode([$verifier, 'userId', $this->mockExpiresAt->getTimestamp()]), |
| 45 | + key: 'secret-key', |
| 46 | + binary: true |
| 47 | + )); |
| 48 | + |
| 49 | + self::assertSame($expectedHash, $result->getHashedToken()); |
75 | 50 | }
|
76 | 51 |
|
77 |
| - public function testHashedTokenIsCreatedUsingOptionVerifierParam(): void |
| 52 | + public function testCreateTokenUsesProvidedVerifierToken(): void |
78 | 53 | {
|
79 |
| - $date = 2020; |
80 |
| - $userId = 'user1234'; |
81 |
| - $knownVerifier = 'verified'; |
82 |
| - |
83 |
| - $this->mockRandomGenerator |
84 |
| - ->expects($this->once()) |
85 |
| - ->method('getRandomAlphaNumStr') |
86 |
| - ->willReturnOnConsecutiveCalls('un-used-verifier', 'selector') |
87 |
| - ; |
88 |
| - |
89 |
| - $this->mockExpiresAt |
90 |
| - ->expects($this->once()) |
91 |
| - ->method('getTimestamp') |
92 |
| - ->willReturn($date) |
93 |
| - ; |
94 |
| - |
95 |
| - $knownToken = hash_hmac( |
96 |
| - 'sha256', |
97 |
| - json_encode([$knownVerifier, $userId, $date]), |
98 |
| - 'key', |
99 |
| - true |
100 |
| - ); |
101 |
| - |
102 |
| - $generator = $this->getTokenGenerator(); |
103 |
| - $result = $generator->createToken($this->mockExpiresAt, $userId, $knownVerifier); |
104 |
| - |
105 |
| - self::assertSame(base64_encode($knownToken), $result->getHashedToken()); |
| 54 | + $result = $this->tokenGenerator->createToken($this->mockExpiresAt, 'userId', '1234'); |
| 55 | + |
| 56 | + $expectedHash = base64_encode(hash_hmac( |
| 57 | + algo: 'sha256', |
| 58 | + data: json_encode(['1234', 'userId', $this->mockExpiresAt->getTimestamp()]), |
| 59 | + key: 'secret-key', |
| 60 | + binary: true |
| 61 | + )); |
| 62 | + |
| 63 | + self::assertSame($expectedHash, $result->getHashedToken()); |
106 | 64 | }
|
107 | 65 |
|
108 |
| - private function getTokenGenerator(): ResetPasswordTokenGenerator |
| 66 | + public function testCreateTokenUsesProvidedParams(): void |
109 | 67 | {
|
110 |
| - return new ResetPasswordTokenGenerator('key', $this->mockRandomGenerator); |
| 68 | + $result = $this->tokenGenerator->createToken($this->mockExpiresAt, 'userId', '1234'); |
| 69 | + |
| 70 | + $expectedHash = base64_encode(hash_hmac( |
| 71 | + algo: 'sha256', |
| 72 | + data: json_encode(['1234', 'userId', '0123456789']), |
| 73 | + key: 'secret-key', |
| 74 | + binary: true |
| 75 | + )); |
| 76 | + |
| 77 | + // We used a "fake" timestamp in our expectedHash |
| 78 | + self::assertNotSame($expectedHash, $result->getHashedToken()); |
| 79 | + |
| 80 | + $expectedHash = base64_encode(hash_hmac( |
| 81 | + algo: 'sha256', |
| 82 | + data: json_encode(['1234', 'bad-user-id', $this->mockExpiresAt->getTimestamp()]), |
| 83 | + key: 'secret-key', |
| 84 | + binary: true |
| 85 | + )); |
| 86 | + |
| 87 | + // We used a "fake" user id in our expectedHash |
| 88 | + self::assertNotSame($expectedHash, $result->getHashedToken()); |
111 | 89 | }
|
112 | 90 | }
|
0 commit comments