@@ -45,24 +45,48 @@ public function __construct(PropertyAccessorInterface $propertyAccessor, array $
45
45
}
46
46
47
47
/**
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.
49
51
*
50
52
* @param int $expires The expiry time as a unix timestamp
51
53
* @param string $hash The plaintext hash provided by the request
52
54
*
53
55
* @throws InvalidSignatureException If the signature does not match the provided parameters
54
56
* @throws ExpiredSignatureException If the signature is no longer valid
55
57
*/
56
- public function verifySignatureHash ( UserInterface $ user , int $ expires , string $ hash ): void
58
+ public function acceptSignatureHash ( string $ userIdentifier , int $ expires , string $ hash ): void
57
59
{
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 ))) {
59
67
throw new InvalidSignatureException ('Invalid or expired signature. ' );
60
68
}
69
+ }
61
70
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
+ {
62
82
if ($ expires < time ()) {
63
83
throw new ExpiredSignatureException ('Signature has expired. ' );
64
84
}
65
85
86
+ if (!hash_equals ($ hash , $ this ->computeSignatureHash ($ user , $ expires ))) {
87
+ throw new InvalidSignatureException ('Invalid or expired signature. ' );
88
+ }
89
+
66
90
if ($ this ->expiredSignaturesStorage && $ this ->maxUses ) {
67
91
if ($ this ->expiredSignaturesStorage ->countUsages ($ hash ) >= $ this ->maxUses ) {
68
92
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
79
103
*/
80
104
public function computeSignatureHash (UserInterface $ user , int $ expires ): string
81
105
{
82
- $ signatureFields = [base64_encode ($ user ->getUserIdentifier ()), $ expires ];
106
+ $ userIdentifier = $ user ->getUserIdentifier ();
107
+ $ fieldsHash = hash_init ('sha256 ' );
83
108
84
109
foreach ($ this ->signatureProperties as $ property ) {
85
110
$ value = $ this ->propertyAccessor ->getValue ($ user , $ property ) ?? '' ;
@@ -90,9 +115,16 @@ public function computeSignatureHash(UserInterface $user, int $expires): string
90
115
if (!\is_scalar ($ value ) && !$ value instanceof \Stringable) {
91
116
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 )));
92
117
}
93
- $ signatureFields [] = base64_encode ($ value );
118
+ hash_update ( $ fieldsHash , ' : ' . base64_encode ($ value) );
94
119
}
95
120
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 )), '+/= ' , '-_~ ' );
97
129
}
98
130
}
0 commit comments