Skip to content

Commit f68efb8

Browse files
committed
Merge pull request firebase#46 from lcabral37/skew
Provide a leeway in verification of times to account for clock skew
2 parents 652f3c6 + 452f7c9 commit f68efb8

File tree

3 files changed

+89
-4
lines changed

3 files changed

+89
-4
lines changed

Authentication/JWT.php

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,14 @@
1515
*/
1616
class JWT
1717
{
18+
19+
/**
20+
* When checking nbf, iat or expiration times,
21+
* we want to provide some extra leeway time to
22+
* account for clock skew.
23+
*/
24+
public static $leeway = 0;
25+
1826
public static $supported_algs = array(
1927
'HS256' => array('hash_hmac', 'SHA256'),
2028
'HS512' => array('hash_hmac', 'SHA512'),
@@ -80,7 +88,7 @@ public static function decode($jwt, $key = null, $allowed_algs = array())
8088

8189
// Check if the nbf if it is defined. This is the time that the
8290
// token can actually be used. If it's not yet that time, abort.
83-
if (isset($payload->nbf) && $payload->nbf > time()) {
91+
if (isset($payload->nbf) && $payload->nbf > (time() + self::$leeway)) {
8492
throw new BeforeValidException(
8593
'Cannot handle token prior to ' . date(DateTime::ISO8601, $payload->nbf)
8694
);
@@ -89,14 +97,14 @@ public static function decode($jwt, $key = null, $allowed_algs = array())
8997
// Check that this token has been created before 'now'. This prevents
9098
// using tokens that have been created for later use (and haven't
9199
// correctly used the nbf claim).
92-
if (isset($payload->iat) && $payload->iat > time()) {
100+
if (isset($payload->iat) && $payload->iat > (time() + self::$leeway)) {
93101
throw new BeforeValidException(
94102
'Cannot handle token prior to ' . date(DateTime::ISO8601, $payload->iat)
95103
);
96104
}
97105

98106
// Check if this token has expired.
99-
if (isset($payload->exp) && time() >= $payload->exp) {
107+
if (isset($payload->exp) && (time() - self::$leeway) >= $payload->exp) {
100108
throw new ExpiredException('Expired token');
101109
}
102110
}

README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,16 @@ print_r($decoded);
4545

4646
$decoded_array = (array) $decoded;
4747

48+
/**
49+
* You can add a leeway to account for when there is a clock skew times between
50+
* the signing and verifying servers. It is recomended this leeway should not
51+
* be bigger than a few minutes.
52+
* Source: http://self-issued.info/docs/draft-ietf-oauth-json-web-token.html#nbfDef
53+
*/
54+
55+
JWT::$leeway = 60;
56+
$decoded = JWT::decode($jwt, $key, array('HS256'));
57+
4858
?>
4959
```
5060

tests/JWTTest.php

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,30 @@ public function testValidToken()
6969
{
7070
$payload = array(
7171
"message" => "abc",
72-
"exp" => time() + 20); // time in the future
72+
"exp" => time() + JWT::$leeway + 20); // time in the future
73+
$encoded = JWT::encode($payload, 'my_key');
74+
$decoded = JWT::decode($encoded, 'my_key', array('HS256'));
75+
$this->assertEquals($decoded->message, 'abc');
76+
}
77+
78+
public function testValidTokenWithLeeway()
79+
{
80+
JWT::$leeway = 60;
81+
$payload = array(
82+
"message" => "abc",
83+
"exp" => time() - 20); // time in the past
84+
$encoded = JWT::encode($payload, 'my_key');
85+
$decoded = JWT::decode($encoded, 'my_key', array('HS256'));
86+
$this->assertEquals($decoded->message, 'abc');
87+
}
88+
89+
public function testExpiredTokenWithLeeway()
90+
{
91+
JWT::$leeway = 60;
92+
$payload = array(
93+
"message" => "abc",
94+
"exp" => time() - 70); // time far in the past
95+
$this->setExpectedException('ExpiredException');
7396
$encoded = JWT::encode($payload, 'my_key');
7497
$decoded = JWT::decode($encoded, 'my_key', array('HS256'));
7598
$this->assertEquals($decoded->message, 'abc');
@@ -97,6 +120,50 @@ public function testValidTokenWithNbf()
97120
$this->assertEquals($decoded->message, 'abc');
98121
}
99122

123+
public function testValidTokenWithNbfLeeway()
124+
{
125+
JWT::$leeway = 60;
126+
$payload = array(
127+
"message" => "abc",
128+
"nbf" => time() + 20); // not before in near (leeway) future
129+
$encoded = JWT::encode($payload, 'my_key');
130+
$decoded = JWT::decode($encoded, 'my_key', array('HS256'));
131+
$this->assertEquals($decoded->message, 'abc');
132+
}
133+
134+
public function testInvalidTokenWithNbfLeeway()
135+
{
136+
JWT::$leeway = 60;
137+
$payload = array(
138+
"message" => "abc",
139+
"nbf" => time() + 65); // not before too far in future
140+
$encoded = JWT::encode($payload, 'my_key');
141+
$this->setExpectedException('BeforeValidException');
142+
$decoded = JWT::decode($encoded, 'my_key', array('HS256'));
143+
}
144+
145+
public function testValidTokenWithIatLeeway()
146+
{
147+
JWT::$leeway = 60;
148+
$payload = array(
149+
"message" => "abc",
150+
"iat" => time() + 20); // issued in near (leeway) future
151+
$encoded = JWT::encode($payload, 'my_key');
152+
$decoded = JWT::decode($encoded, 'my_key', array('HS256'));
153+
$this->assertEquals($decoded->message, 'abc');
154+
}
155+
156+
public function testInvalidTokenWithIatLeeway()
157+
{
158+
JWT::$leeway = 60;
159+
$payload = array(
160+
"message" => "abc",
161+
"iat" => time() + 65); // issued too far in future
162+
$encoded = JWT::encode($payload, 'my_key');
163+
$this->setExpectedException('BeforeValidException');
164+
$decoded = JWT::decode($encoded, 'my_key', array('HS256'));
165+
}
166+
100167
public function testInvalidToken()
101168
{
102169
$payload = array(

0 commit comments

Comments
 (0)