summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--.travis.yml2
-rw-r--r--Authentication/JWT.php54
-rw-r--r--Exceptions/BeforeValidException.php6
-rw-r--r--Exceptions/ExpiredException.php6
-rw-r--r--Exceptions/SignatureInvalidException.php6
-rw-r--r--composer.json2
-rw-r--r--tests/JWTTest.php161
-rw-r--r--tests/autoload.php.dist2
9 files changed, 150 insertions, 90 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..22d0d82
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+vendor
diff --git a/.travis.yml b/.travis.yml
index 1ecd967..d5d716e 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -8,5 +8,5 @@ php:
before_script:
- wget -nc http://getcomposer.org/composer.phar
- php composer.phar install
-
+
script: phpunit --configuration phpunit.xml.dist
diff --git a/Authentication/JWT.php b/Authentication/JWT.php
index 90c1ac7..76c33bb 100644
--- a/Authentication/JWT.php
+++ b/Authentication/JWT.php
@@ -15,7 +15,7 @@
*/
class JWT
{
- static $methods = array(
+ public static $methods = array(
'HS256' => array('hash_hmac', 'SHA256'),
'HS512' => array('hash_hmac', 'SHA512'),
'HS384' => array('hash_hmac', 'SHA384'),
@@ -32,7 +32,7 @@ class JWT
* @return object The JWT's payload as a PHP object
* @throws UnexpectedValueException Provided JWT was invalid
* @throws DomainException Algorithm was not provided
- *
+ *
* @uses jsonDecode
* @uses urlsafeB64Decode
*/
@@ -55,20 +55,31 @@ class JWT
throw new DomainException('Empty algorithm');
}
if (is_array($key)) {
- if(isset($header->kid)) {
+ if (isset($header->kid)) {
$key = $key[$header->kid];
} else {
throw new DomainException('"kid" empty, unable to lookup correct key');
}
}
+
+ // Check the signature
if (!JWT::verify("$headb64.$bodyb64", $sig, $key, $header->alg)) {
- throw new UnexpectedValueException('Signature verification failed');
+ throw new SignatureInvalidException('Signature verification failed');
}
+
// Check token expiry time if defined.
- if (isset($payload->exp) && time() >= $payload->exp){
- throw new UnexpectedValueException('Expired Token');
+ if (isset($payload->exp) && time() >= $payload->exp) {
+ throw new ExpiredException('Expired token');
+ }
+
+ // Check if the nbf if it is defined.
+ if (isset($payload->nbf) && $payload->nbf > time()) {
+ throw new BeforeValidException(
+ 'Cannot handle token prior to ' . date(DateTime::ISO8601, $payload->nbf)
+ );
}
}
+
return $payload;
}
@@ -87,7 +98,7 @@ class JWT
public static function encode($payload, $key, $algo = 'HS256', $keyId = null)
{
$header = array('typ' => 'JWT', 'alg' => $algo);
- if($keyId !== null) {
+ if ($keyId !== null) {
$header['kid'] = $keyId;
}
$segments = array();
@@ -124,7 +135,7 @@ class JWT
case 'openssl':
$signature = '';
$success = openssl_sign($msg, $signature, $key, $algo);
- if(!$success) {
+ if (!$success) {
throw new DomainException("OpenSSL unable to sign data");
} else {
return $signature;
@@ -142,7 +153,8 @@ class JWT
* @return bool
* @throws DomainException Invalid Algorithm or OpenSSL failure
*/
- public static function verify($msg, $signature, $key, $method = 'HS256') {
+ public static function verify($msg, $signature, $key, $method = 'HS256')
+ {
if (empty(self::$methods[$method])) {
throw new DomainException('Algorithm not supported');
}
@@ -150,7 +162,7 @@ class JWT
switch($function) {
case 'openssl':
$success = openssl_verify($msg, $signature, $key, $algo);
- if(!$success) {
+ if (!$success) {
throw new DomainException("OpenSSL unable to verify data: " . openssl_error_string());
} else {
return $signature;
@@ -181,13 +193,15 @@ class JWT
public static function jsonDecode($input)
{
if (version_compare(PHP_VERSION, '5.4.0', '>=') && !(defined('JSON_C_VERSION') && PHP_INT_SIZE > 4)) {
- /* In PHP >=5.4.0, json_decode() accepts an options parameter, that allows you to specify that large ints (like Steam
- * Transaction IDs) should be treated as strings, rather than the PHP default behaviour of converting them to floats.
+ /** In PHP >=5.4.0, json_decode() accepts an options parameter, that allows you
+ * to specify that large ints (like Steam Transaction IDs) should be treated as
+ * strings, rather than the PHP default behaviour of converting them to floats.
*/
$obj = json_decode($input, false, 512, JSON_BIGINT_AS_STRING);
} else {
- /* Not all servers will support that, however, so for older versions we must manually detect large ints in the JSON
- * string and quote them (thus converting them to strings) before decoding, hence the preg_replace() call.
+ /** Not all servers will support that, however, so for older versions we must
+ * manually detect large ints in the JSON string and quote them (thus converting
+ *them to strings) before decoding, hence the preg_replace() call.
*/
$max_int_length = strlen((string) PHP_INT_MAX) - 1;
$json_without_bigints = preg_replace('/:\s*(-?\d{'.$max_int_length.',})/', ': "$1"', $input);
@@ -195,8 +209,8 @@ class JWT
}
if (function_exists('json_last_error') && $errno = json_last_error()) {
- JWT::_handleJsonError($errno);
- } else if ($obj === null && $input !== 'null') {
+ JWT::handleJsonError($errno);
+ } elseif ($obj === null && $input !== 'null') {
throw new DomainException('Null result with non-null input');
}
return $obj;
@@ -214,8 +228,8 @@ class JWT
{
$json = json_encode($input);
if (function_exists('json_last_error') && $errno = json_last_error()) {
- JWT::_handleJsonError($errno);
- } else if ($json === 'null' && $input !== null) {
+ JWT::handleJsonError($errno);
+ } elseif ($json === 'null' && $input !== null) {
throw new DomainException('Null result with non-null input');
}
return $json;
@@ -257,7 +271,7 @@ class JWT
*
* @return void
*/
- private static function _handleJsonError($errno)
+ private static function handleJsonError($errno)
{
$messages = array(
JSON_ERROR_DEPTH => 'Maximum stack depth exceeded',
@@ -270,6 +284,4 @@ class JWT
: 'Unknown JSON error: ' . $errno
);
}
-
}
-
diff --git a/Exceptions/BeforeValidException.php b/Exceptions/BeforeValidException.php
new file mode 100644
index 0000000..5a84975
--- /dev/null
+++ b/Exceptions/BeforeValidException.php
@@ -0,0 +1,6 @@
+<?php
+
+class BeforeValidException extends UnexpectedValueException
+{
+
+}
diff --git a/Exceptions/ExpiredException.php b/Exceptions/ExpiredException.php
new file mode 100644
index 0000000..bd80468
--- /dev/null
+++ b/Exceptions/ExpiredException.php
@@ -0,0 +1,6 @@
+<?php
+
+class ExpiredException extends UnexpectedValueException
+{
+
+}
diff --git a/Exceptions/SignatureInvalidException.php b/Exceptions/SignatureInvalidException.php
new file mode 100644
index 0000000..d122232
--- /dev/null
+++ b/Exceptions/SignatureInvalidException.php
@@ -0,0 +1,6 @@
+<?php
+
+class SignatureInvalidException extends UnexpectedValueException
+{
+
+}
diff --git a/composer.json b/composer.json
index 292edbf..15b246c 100644
--- a/composer.json
+++ b/composer.json
@@ -19,7 +19,7 @@
"php": ">=5.2.0"
},
"autoload": {
- "classmap": ["Authentication/"]
+ "classmap": ["Authentication/", "Exceptions/"]
},
"target-dir": "Firebase/PHP-JWT",
"minimum-stability": "dev"
diff --git a/tests/JWTTest.php b/tests/JWTTest.php
index 2149862..5a76ed4 100644
--- a/tests/JWTTest.php
+++ b/tests/JWTTest.php
@@ -1,79 +1,108 @@
<?php
-class JWTTest extends PHPUnit_Framework_TestCase {
- function testEncodeDecode() {
- $msg = JWT::encode('abc', 'my_key');
- $this->assertEquals(JWT::decode($msg, 'my_key'), 'abc');
- }
+class JWTTest extends PHPUnit_Framework_TestCase
+{
+ public function testEncodeDecode()
+ {
+ $msg = JWT::encode('abc', 'my_key');
+ $this->assertEquals(JWT::decode($msg, 'my_key'), 'abc');
+ }
- function testDecodeFromPython() {
- $msg = 'eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9.Iio6aHR0cDovL2FwcGxpY2F0aW9uL2NsaWNreT9ibGFoPTEuMjMmZi5vbz00NTYgQUMwMDAgMTIzIg.E_U8X2YpMT5K1cEiT_3-IvBYfrdIFIeVYeOqre_Z5Cg';
- $this->assertEquals(
- JWT::decode($msg, 'my_key'),
- '*:http://application/clicky?blah=1.23&f.oo=456 AC000 123'
- );
- }
+ public function testDecodeFromPython()
+ {
+ $msg = 'eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9.Iio6aHR0cDovL2FwcGxpY2F0aW9uL2NsaWNreT9ibGFoPTEuMjMmZi5vbz00NTYgQUMwMDAgMTIzIg.E_U8X2YpMT5K1cEiT_3-IvBYfrdIFIeVYeOqre_Z5Cg';
+ $this->assertEquals(
+ JWT::decode($msg, 'my_key'),
+ '*:http://application/clicky?blah=1.23&f.oo=456 AC000 123'
+ );
+ }
- function testUrlSafeCharacters() {
- $encoded = JWT::encode('f?', 'a');
- $this->assertEquals('f?', JWT::decode($encoded, 'a'));
- }
+ public function testUrlSafeCharacters()
+ {
+ $encoded = JWT::encode('f?', 'a');
+ $this->assertEquals('f?', JWT::decode($encoded, 'a'));
+ }
- function testMalformedUtf8StringsFail() {
- $this->setExpectedException('DomainException');
- JWT::encode(pack('c', 128), 'a');
- }
+ public function testMalformedUtf8StringsFail()
+ {
+ $this->setExpectedException('DomainException');
+ JWT::encode(pack('c', 128), 'a');
+ }
- function testMalformedJsonThrowsException() {
- $this->setExpectedException('DomainException');
- JWT::jsonDecode('this is not valid JSON string');
- }
+ public function testMalformedJsonThrowsException()
+ {
+ $this->setExpectedException('DomainException');
+ JWT::jsonDecode('this is not valid JSON string');
+ }
- function testExpiredToken() {
- $this->setExpectedException('UnexpectedValueException');
- $payload = array(
- "message" => "abc",
- "exp" => time() - 20); // time in the past
- $encoded = JWT::encode($payload, 'my_key');
- JWT::decode($encoded);
- }
+ public function testExpiredToken()
+ {
+ $this->setExpectedException('ExpiredException');
+ $payload = array(
+ "message" => "abc",
+ "exp" => time() - 20); // time in the past
+ $encoded = JWT::encode($payload, 'my_key');
+ JWT::decode($encoded, 'my_key');
+ }
- function testValidToken() {
- $payload = array(
- "message" => "abc",
- "exp" => time() + 20); // time in the future
- $encoded = JWT::encode($payload, 'my_key');
- $decoded = JWT::decode($encoded, 'my_key');
- $this->assertEquals($decoded->message, 'abc');
- }
+ public function testBeforeValidToken()
+ {
+ $this->setExpectedException('BeforeValidException');
+ $payload = array(
+ "message" => "abc",
+ "nbf" => time() + 20); // time in the future
+ $encoded = JWT::encode($payload, 'my_key');
+ JWT::decode($encoded, 'my_key');
+ }
- function testInvalidToken() {
- $payload = array(
- "message" => "abc",
- "exp" => time() + 20); // time in the future
- $encoded = JWT::encode($payload, 'my_key');
- $this->setExpectedException('UnexpectedValueException');
- $decoded = JWT::decode($encoded, 'my_key2');
- }
+ public function testValidToken()
+ {
+ $payload = array(
+ "message" => "abc",
+ "exp" => time() + 20); // time in the future
+ $encoded = JWT::encode($payload, 'my_key');
+ $decoded = JWT::decode($encoded, 'my_key');
+ $this->assertEquals($decoded->message, 'abc');
+ }
- function testRSEncodeDecode() {
- $privKey = openssl_pkey_new(array('digest_alg' => 'sha256',
- 'private_key_bits' => 1024,
- 'private_key_type' => OPENSSL_KEYTYPE_RSA));
- $msg = JWT::encode('abc', $privKey, 'RS256');
- $pubKey = openssl_pkey_get_details($privKey);
- $pubKey = $pubKey['key'];
- $decoded = JWT::decode($msg, $pubKey, true);
- $this->assertEquals($decoded, 'abc');
- }
+ public function testValidTokenWithNbf()
+ {
+ $payload = array(
+ "message" => "abc",
+ "exp" => time() + 20, // time in the future
+ "nbf" => time() - 20);
+ $encoded = JWT::encode($payload, 'my_key');
+ $decoded = JWT::decode($encoded, 'my_key');
+ $this->assertEquals($decoded->message, 'abc');
+ }
- function testKIDChooser() {
- $keys = array('1' => 'my_key', '2' => 'my_key2');
- $msg = JWT::encode('abc', $keys['1'], 'HS256', '1');
- $decoded = JWT::decode($msg, $keys, true);
- $this->assertEquals($decoded, 'abc');
- }
+ public function testInvalidToken()
+ {
+ $payload = array(
+ "message" => "abc",
+ "exp" => time() + 20); // time in the future
+ $encoded = JWT::encode($payload, 'my_key');
+ $this->setExpectedException('SignatureInvalidException');
+ $decoded = JWT::decode($encoded, 'my_key2');
+ }
-}
+ public function testRSEncodeDecode()
+ {
+ $privKey = openssl_pkey_new(array('digest_alg' => 'sha256',
+ 'private_key_bits' => 1024,
+ 'private_key_type' => OPENSSL_KEYTYPE_RSA));
+ $msg = JWT::encode('abc', $privKey, 'RS256');
+ $pubKey = openssl_pkey_get_details($privKey);
+ $pubKey = $pubKey['key'];
+ $decoded = JWT::decode($msg, $pubKey, true);
+ $this->assertEquals($decoded, 'abc');
+ }
-?>
+ public function testKIDChooser()
+ {
+ $keys = array('1' => 'my_key', '2' => 'my_key2');
+ $msg = JWT::encode('abc', $keys['1'], 'HS256', '1');
+ $decoded = JWT::decode($msg, $keys, true);
+ $this->assertEquals($decoded, 'abc');
+ }
+}
diff --git a/tests/autoload.php.dist b/tests/autoload.php.dist
index 4533624..2e4310a 100644
--- a/tests/autoload.php.dist
+++ b/tests/autoload.php.dist
@@ -14,4 +14,4 @@ php composer.phar install
Visit http://getcomposer.org/ for more information.
');
-} \ No newline at end of file
+}