|
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