@@ -53,6 +53,7 @@ protected function setUp(): void
5353
5454 /**
5555 * @group time-sensitive
56+ *
5657 * @dataProvider provideCreateLoginLinkData
5758 */
5859 public function testCreateLoginLink ($ user , array $ extraProperties , Request $ request = null )
@@ -68,7 +69,7 @@ public function testCreateLoginLink($user, array $extraProperties, Request $requ
6869 // allow a small expiration offset to avoid time-sensitivity
6970 && abs (time () + 600 - $ parameters ['expires ' ]) <= 1
7071 // make sure hash is what we expect
71- && $ parameters ['hash ' ] === $ this ->createSignatureHash ('weaverryan ' , $ parameters ['expires ' ], array_values ( $ extraProperties) );
72+ && $ parameters ['hash ' ] === $ this ->createSignatureHash ('weaverryan ' , $ parameters ['expires ' ], $ extraProperties );
7273 }),
7374 UrlGeneratorInterface::ABSOLUTE_URL
7475 )
@@ -115,7 +116,7 @@ public function provideCreateLoginLinkData()
115116 public function testConsumeLoginLink ()
116117 {
117118 $ expires = time () + 500 ;
118- $ signature = $ this ->createSignatureHash ('weaverryan ' , $ expires, [ ' ryan@symfonycasts.com ' , ' pwhash ' ] );
119+ $ signature = $ this ->createSignatureHash ('weaverryan ' , $ expires );
119120 $ request = Request::create (sprintf ('/login/verify?user=weaverryan&hash=%s&expires=%d ' , $ signature , $ expires ));
120121
121122 $ user = new TestLoginLinkHandlerUser ('weaverryan ' , 'ryan@symfonycasts.com ' , 'pwhash ' );
@@ -131,44 +132,37 @@ public function testConsumeLoginLink()
131132
132133 public function testConsumeLoginLinkWithExpired ()
133134 {
134- $ this ->expectException (ExpiredLoginLinkException::class);
135135 $ expires = time () - 500 ;
136- $ signature = $ this ->createSignatureHash ('weaverryan ' , $ expires, [ ' ryan@symfonycasts.com ' , ' pwhash ' ] );
136+ $ signature = $ this ->createSignatureHash ('weaverryan ' , $ expires );
137137 $ request = Request::create (sprintf ('/login/verify?user=weaverryan&hash=%s&expires=%d ' , $ signature , $ expires ));
138138
139- $ user = new TestLoginLinkHandlerUser ('weaverryan ' , 'ryan@symfonycasts.com ' , 'pwhash ' );
140- $ this ->userProvider ->createUser ($ user );
141-
142139 $ linker = $ this ->createLinker (['max_uses ' => 3 ]);
140+ $ this ->expectException (ExpiredLoginLinkException::class);
143141 $ linker ->consumeLoginLink ($ request );
144142 }
145143
146144 public function testConsumeLoginLinkWithUserNotFound ()
147145 {
148- $ this ->expectException (InvalidLoginLinkException::class);
149- $ request = Request::create ('/login/verify?user=weaverryan&hash=thehash&expires=10000 ' );
146+ $ request = Request::create ('/login/verify?user=weaverryan&hash=thehash&expires= ' .(time () + 500 ));
150147
151148 $ linker = $ this ->createLinker ();
149+ $ this ->expectException (InvalidLoginLinkException::class);
152150 $ linker ->consumeLoginLink ($ request );
153151 }
154152
155153 public function testConsumeLoginLinkWithDifferentSignature ()
156154 {
157- $ this ->expectException (InvalidLoginLinkException::class);
158155 $ request = Request::create (sprintf ('/login/verify?user=weaverryan&hash=fake_hash&expires=%d ' , time () + 500 ));
159156
160- $ user = new TestLoginLinkHandlerUser ('weaverryan ' , 'ryan@symfonycasts.com ' , 'pwhash ' );
161- $ this ->userProvider ->createUser ($ user );
162-
163157 $ linker = $ this ->createLinker ();
158+ $ this ->expectException (InvalidLoginLinkException::class);
164159 $ linker ->consumeLoginLink ($ request );
165160 }
166161
167162 public function testConsumeLoginLinkExceedsMaxUsage ()
168163 {
169- $ this ->expectException (ExpiredLoginLinkException::class);
170164 $ expires = time () + 500 ;
171- $ signature = $ this ->createSignatureHash ('weaverryan ' , $ expires, [ ' ryan@symfonycasts.com ' , ' pwhash ' ] );
165+ $ signature = $ this ->createSignatureHash ('weaverryan ' , $ expires );
172166 $ request = Request::create (sprintf ('/login/verify?user=weaverryan&hash=%s&expires=%d ' , $ signature , $ expires ));
173167
174168 $ user = new TestLoginLinkHandlerUser ('weaverryan ' , 'ryan@symfonycasts.com ' , 'pwhash ' );
@@ -179,6 +173,7 @@ public function testConsumeLoginLinkExceedsMaxUsage()
179173 $ this ->expiredLinkCache ->save ($ item );
180174
181175 $ linker = $ this ->createLinker (['max_uses ' => 3 ]);
176+ $ this ->expectException (ExpiredLoginLinkException::class);
182177 $ linker ->consumeLoginLink ($ request );
183178 }
184179
@@ -206,15 +201,12 @@ public function testConsumeLoginLinkWithMissingExpiration()
206201 $ linker ->consumeLoginLink ($ request );
207202 }
208203
209- private function createSignatureHash (string $ username , int $ expires , array $ extraFields ): string
204+ private function createSignatureHash (string $ username , int $ expires , array $ extraFields = [ ' emailProperty ' => ' ryan@symfonycasts.com ' , ' passwordProperty ' => ' pwhash ' ] ): string
210205 {
211- $ fields = [base64_encode ($ username ), $ expires ];
212- foreach ($ extraFields as $ extraField ) {
213- $ fields [] = base64_encode ($ extraField );
214- }
206+ $ hasher = new SignatureHasher ($ this ->propertyAccessor , array_keys ($ extraFields ), 's3cret ' );
207+ $ user = new TestLoginLinkHandlerUser ($ username , $ extraFields ['emailProperty ' ] ?? '' , $ extraFields ['passwordProperty ' ] ?? '' , $ extraFields ['lastAuthenticatedAt ' ] ?? null );
215208
216- // matches hash logic in the class
217- return base64_encode (hash_hmac ('sha256 ' , implode (': ' , $ fields ), 's3cret ' ));
209+ return $ hasher ->computeSignatureHash ($ user , $ expires );
218210 }
219211
220212 private function createLinker (array $ options = [], array $ extraProperties = ['emailProperty ' , 'passwordProperty ' ]): LoginLinkHandler
@@ -298,7 +290,7 @@ public function loadUserByIdentifier(string $userIdentifier): TestLoginLinkHandl
298290
299291 public function refreshUser (UserInterface $ user ): TestLoginLinkHandlerUser
300292 {
301- return $ this ->users [$ username ];
293+ return $ this ->users [$ user -> getUserIdentifier () ];
302294 }
303295
304296 public function supportsClass (string $ class ): bool
0 commit comments