@@ -45,24 +45,48 @@ public function __construct(PropertyAccessorInterface $propertyAccessor, array $
4545 }
4646
4747 /**
48- * Verifies the hash using the provided user and expire time.
48+ * Verifies the hash using the provided user identifier and expire time.
49+ *
50+ * This method must be called before the user object is loaded from a provider.
4951 *
5052 * @param int $expires The expiry time as a unix timestamp
5153 * @param string $hash The plaintext hash provided by the request
5254 *
5355 * @throws InvalidSignatureException If the signature does not match the provided parameters
5456 * @throws ExpiredSignatureException If the signature is no longer valid
5557 */
56- public function verifySignatureHash ( UserInterface $ user , int $ expires , string $ hash ): void
58+ public function acceptSignatureHash ( string $ userIdentifier , int $ expires , string $ hash ): void
5759 {
58- if (!hash_equals ($ hash , $ this ->computeSignatureHash ($ user , $ expires ))) {
60+ if ($ expires < time ()) {
61+ throw new ExpiredSignatureException ('Signature has expired. ' );
62+ }
63+ $ hmac = substr ($ hash , 0 , 44 );
64+ $ payload = substr ($ hash , 44 ).': ' .$ expires .': ' .$ userIdentifier ;
65+
66+ if (!hash_equals ($ hmac , $ this ->generateHash ($ payload ))) {
5967 throw new InvalidSignatureException ('Invalid or expired signature. ' );
6068 }
69+ }
6170
71+ /**
72+ * Verifies the hash using the provided user and expire time.
73+ *
74+ * @param int $expires The expiry time as a unix timestamp
75+ * @param string $hash The plaintext hash provided by the request
76+ *
77+ * @throws InvalidSignatureException If the signature does not match the provided parameters
78+ * @throws ExpiredSignatureException If the signature is no longer valid
79+ */
80+ public function verifySignatureHash (UserInterface $ user , int $ expires , string $ hash ): void
81+ {
6282 if ($ expires < time ()) {
6383 throw new ExpiredSignatureException ('Signature has expired. ' );
6484 }
6585
86+ if (!hash_equals ($ hash , $ this ->computeSignatureHash ($ user , $ expires ))) {
87+ throw new InvalidSignatureException ('Invalid or expired signature. ' );
88+ }
89+
6690 if ($ this ->expiredSignaturesStorage && $ this ->maxUses ) {
6791 if ($ this ->expiredSignaturesStorage ->countUsages ($ hash ) >= $ this ->maxUses ) {
6892 throw new ExpiredSignatureException (sprintf ('Signature can only be used "%d" times. ' , $ this ->maxUses ));
@@ -79,7 +103,8 @@ public function verifySignatureHash(UserInterface $user, int $expires, string $h
79103 */
80104 public function computeSignatureHash (UserInterface $ user , int $ expires ): string
81105 {
82- $ signatureFields = [base64_encode ($ user ->getUserIdentifier ()), $ expires ];
106+ $ userIdentifier = $ user ->getUserIdentifier ();
107+ $ fieldsHash = hash_init ('sha256 ' );
83108
84109 foreach ($ this ->signatureProperties as $ property ) {
85110 $ value = $ this ->propertyAccessor ->getValue ($ user , $ property ) ?? '' ;
@@ -90,9 +115,16 @@ public function computeSignatureHash(UserInterface $user, int $expires): string
90115 if (!\is_scalar ($ value ) && !$ value instanceof \Stringable) {
91116 throw new \InvalidArgumentException (sprintf ('The property path "%s" on the user object "%s" must return a value that can be cast to a string, but "%s" was returned. ' , $ property , $ user ::class, get_debug_type ($ value )));
92117 }
93- $ signatureFields [] = base64_encode ($ value );
118+ hash_update ( $ fieldsHash , ' : ' . base64_encode ($ value) );
94119 }
95120
96- return base64_encode (hash_hmac ('sha256 ' , implode (': ' , $ signatureFields ), $ this ->secret ));
121+ $ fieldsHash = strtr (base64_encode (hash_final ($ fieldsHash , true )), '+/= ' , '-_~ ' );
122+
123+ return $ this ->generateHash ($ fieldsHash .': ' .$ expires .': ' .$ userIdentifier ).$ fieldsHash ;
124+ }
125+
126+ private function generateHash (string $ tokenValue ): string
127+ {
128+ return strtr (base64_encode (hash_hmac ('sha256 ' , $ tokenValue , $ this ->secret , true )), '+/= ' , '-_~ ' );
97129 }
98130}
0 commit comments