diff --git a/README.md b/README.md
index 55adae4..13cfa36 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
@@ -23,8 +23,8 @@ $ make && make install
## Quick Example
```php
-$key = "example_key";
-$claims = array(
+$key = "example-hmac-key";
+$payload = array(
"data" => [
"name" => "ZiHang Gao",
"admin" => true
@@ -34,43 +34,298 @@ $claims = array(
);
// default HS256 algorithm
-$token = jwt_encode($claims, $key);
+$token = jwt_encode($payload, $key);
-echo $token . PHP_EOL;
-//eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.
-//eyJpc3MiOiJodHRwOlwvXC9leGFtcGxlLm9yZyIsInN1YiI6IjEyMzQ1Njc4OTAiLCJuYW1lIjoiWmlIYW5nIEdhbyIsImFkbWluIjp0cnVlfQ.
-//2lFeBTsRegsjXiBCZNkW41KFlsZPSFu7KTsyAM9lUiQ
+//eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJkYXRhIjp7Im5hbWUiOiJaaUhhbmcgR2FvIiwiYWRtaW4iOnRydWV9LCJpc3MiOiJodHRwOlwvXC9leGFtcGxlLm9yZyIsInN1YiI6IjEyMzQ1Njc4OTAifQ.UcrCt9o9rz38kKMTa-nCrm7JNQRNAId5Xg9C7EIl2Zc
+echo $token;
-print_r(jwt_decode($token, $key));
-/**
-Array
-(
- [data] => Array
- (
- [name] => ZiHang Gao
- [admin] => 1
- )
+$decoded_token = jwt_decode($token, $key);
- [iss] => http://example.org
- [sub] => 1234567890
-)
-*/
+// Array
+// (
+// [data] => Array
+// (
+// [name] => ZiHang Gao
+// [admin] => 1
+// )
+//
+// [iss] => http://example.org
+// [sub] => 1234567890
+// )
+print_r($decoded_token);
```
## [Example](https://github.com/cdoco/php-jwt/tree/master/example)
+## Algorithms and Usage
+
+The JWT supports NONE, HMAC, RSASSA and ECDSA algorithms for cryptographic signing.
+
+#### NONE
+
+- none - unsigned token
+
+```php
+$payload = ['data' => 'test'];
+
+// IMPORTANT: set null as key parameter
+$token = jwt_encode($payload, null, 'none');
+
+// eyJ0eXAiOiJKV1QiLCJhbGciOiJub25lIn0.eyJkYXRhIjoidGVzdCJ9.
+echo $token;
+
+// Set key to nil and options to false otherwise this won't work
+$decoded_token = jwt_decode($token, null, false);
+
+// Array
+// (
+// [data] => test
+// )
+print_r($decoded_token);
+```
+
+#### HMAC (default: HS256)
+
+- HS256 - HMAC using SHA-256 hash algorithm (default)
+- HS384 - HMAC using SHA-384 hash algorithm
+- HS512 - HMAC using SHA-512 hash algorithm
+
+```php
+$hmackey = "example-hmac-key";
+
+$token = jwt_encode($payload, $hmackey, 'HS256');
+
+// eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJkYXRhIjoidGVzdCJ9.C8kzOqBbcaPRhRdLWdNVSvYkIPIBPu7f_8-avoG-JiU
+echo $token;
+
+$decoded_token = jwt_decode($token, $hmackey, ['algorithm' => 'HS256']);
+
+// Array
+// (
+// [data] => test
+// )
+print_r($decoded_token);
+```
+
+#### RSA
+
+- RS256 - RSA using SHA-256 hash algorithm
+- RS384 - RSA using SHA-384 hash algorithm
+- RS512 - RSA using SHA-512 hash algorithm
+
+```php
+$privateKey = file_get_contents('key/rsa_private_key.pem');
+$publicKey = file_get_contents('key/rsa_public_key.pem');
+
+$token = jwt_encode($payload, $privateKey, 'RS256');
+
+// eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJkYXRhIjoidGVzdCJ9.pkpKlkzQWSkme42WcOxwkLeUttiLeNORzthSJeIt140iNEtRK_f8IotoinfIKI7Y6x8pfQ4n1DHJ_5IUDe6elds8gnhLwfq5XRY48BGc8Dc_QowVQd75m5fXI6nFySW8z8CAsbwn2Efg-p7SLdfhWpNQ9AISfwa_1l-OB3BgKFw
+echo $token;
+
+$decoded_token = jwt_decode($token, $publicKey, ['algorithm' => 'RS256']);
+
+// Array
+// (
+// [data] => test
+// )
+print_r($decoded_token);
+```
+
+#### ECDSA
+
+- ES256 - ECDSA using P-256 and SHA-256
+- ES384 - ECDSA using P-384 and SHA-384
+- ES512 - ECDSA using P-521 and SHA-512
+
+```php
+$privateKey = file_get_contents('key/ec_private_key.pem');
+$publicKey = file_get_contents('key/ec_public_key.pem');
+
+$token = jwt_encode($payload, $privateKey, 'ES256');
+
+// eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJkYXRhIjoidGVzdCJ9.etzxzSvJi1QS5nUtKDuLX2sScZ5W50CJL6PivKys45nc77QLxnLsF5QQApEAis8SI28rqwP9VITqPPlwJBNdH3N5n0I58z3jevGJYOfRtBnCa6omUNE03nxoEYMqRBuP
+echo $token;
+
+$decoded_token = jwt_decode($token, $publicKey, ['algorithm' => 'ES256']);
+
+// Array
+// (
+// [data] => test
+// )
+print_r($decoded_token);
+```
+
+## Support for reserved claim names
+
+JSON Web Token defines some reserved claim names and defines how they should be used. JWT supports these reserved claim names:
+
+- 'exp' (Expiration Time) Claim
+- 'nbf' (Not Before Time) Claim
+- 'iss' (Issuer) Claim
+- 'aud' (Audience) Claim
+- 'jti' (JWT ID) Claim
+- 'iat' (Issued At) Claim
+- 'sub' (Subject) Claim
+
+### Expiration Time Claim
+
+> The `exp` (expiration time) claim identifies the expiration time on or after which the JWT MUST NOT be accepted for processing. The processing of the `exp` claim requires that the current date/time MUST be before the expiration date/time listed in the `exp` claim. Implementers MAY provide for some small `leeway`, usually no more than a few minutes, to account for clock skew. Its value MUST be a number containing a **NumericDate** value. Use of this claim is OPTIONAL.
+
+#### Handle Expiration Claim
+
+```php
+$payload = ['data' => 'data', 'exp' => time() + 4 * 3600];
+
+$token = jwt_encode($payload, $hmackey, 'HS256');
+
+try {
+ $decoded_token = jwt_decode($token, $hmackey, ['algorithm' => 'HS256']);
+} catch (Exception $e) {
+ // Expired token
+ $e->getMessage();
+}
+```
+
+#### Adding Leeway
+
+```php
+$payload = ['data' => 'data', 'exp' => time() - 10];
+
+// build expired token
+$token = jwt_encode($payload, $hmackey, 'HS256');
+
+try {
+ $decoded_token = jwt_decode($token, $hmackey, ['leeway' => 30, 'algorithm' => 'HS256']);
+} catch (Exception $e) {
+ // Expired token
+}
+```
+
+### Not Before Time Claim
+
+> The `nbf` (not before) claim identifies the time before which the JWT MUST NOT be accepted for processing. The processing of the `nbf` claim requires that the current date/time MUST be after or equal to the not-before date/time listed in the `nbf` claim. Implementers MAY provide for some small `leeway`, usually no more than a few minutes, to account for clock skew. Its value MUST be a number containing a **NumericDate** value. Use of this claim is OPTIONAL.
+
+#### Handle Not Before Claim
+
+```php
+$payload = ['data' => 'data', 'nbf' => time() - 3600];
+
+$token = jwt_encode($payload, $hmackey, 'HS256');
+
+try {
+ $decoded_token = jwt_decode($token, $hmackey, ['algorithm' => 'HS256']);
+} catch (Exception $e) {
+ // Handle invalid token
+}
+```
+
+#### Adding Leeway
+
+```php
+$payload = ['data' => 'data', 'nbf' => time() + 10];
+
+// build expired token
+$token = jwt_encode($payload, $hmackey, 'HS256');
+
+try {
+ $decoded_token = jwt_decode($token, $hmackey, ['leeway' => 30, 'algorithm' => 'HS256']);
+} catch (Exception $e) {
+ // Handle invalid token
+}
+```
+
+### Issuer Claim
+
+> The `iss` (issuer) claim identifies the principal that issued the JWT. The processing of this claim is generally application specific. The `iss` value is a case-sensitive string containing a **StringOrURI** value. Use of this claim is OPTIONAL.
+
+```php
+$payload = ['data' => 'data', 'iss' => 'http://example.org'];
+
+$token = jwt_encode($payload, $hmackey, 'HS256');
+
+try {
+ $decoded_token = jwt_decode($token, $hmackey, ['iss' => 'http://example.org', 'algorithm' => 'HS256']);
+} catch (Exception $e) {
+ // Handle invalid token
+}
+```
+
+### Audience Claim
+
+> The `aud` (audience) claim identifies the recipients that the JWT is intended for. Each principal intended to process the JWT MUST identify itself with a value in the audience claim. If the principal processing the claim does not identify itself with a value in the `aud` claim when this claim is present, then the JWT MUST be rejected. In the general case, the `aud` value is an array of case-sensitive strings, each containing a **StringOrURI** value. In the special case when the JWT has one audience, the `aud` value MAY be a single case-sensitive string containing a **StringOrURI** value. The interpretation of audience values is generally application specific. Use of this claim is OPTIONAL.
+
+```php
+$payload = ['data' => 'data', 'aud' => 'Young Man'];
+
+$token = jwt_encode($payload, $hmackey, 'HS256');
+
+try {
+ $decoded_token = jwt_decode($token, $hmackey, ['aud' => 'Young Man', 'algorithm' => 'HS256']);
+} catch (Exception $e) {
+ // Handle invalid token
+}
+```
+
+### JWT ID Claim
+
+> The `jti` (JWT ID) claim provides a unique identifier for the JWT. The identifier value MUST be assigned in a manner that ensures that there is a negligible probability that the same value will be accidentally assigned to a different data object; if the application uses multiple issuers, collisions MUST be prevented among values produced by different issuers as well. The `jti` claim can be used to prevent the JWT from being replayed. The `jti` value is a **case-sensitive string**. Use of this claim is OPTIONAL.
+
+```php
+$payload = ['data' => 'data', 'jti' => md5('id')];
+
+$token = jwt_encode($payload, $hmackey, 'HS256');
+
+try {
+ $decoded_token = jwt_decode($token, $hmackey, ['jti' => md5('id'), 'algorithm' => 'HS256']);
+} catch (Exception $e) {
+ // Handle invalid token
+}
+```
+
+### Issued At Claim
+
+> The `iat` (issued at) claim identifies the time at which the JWT was issued. This claim can be used to determine the age of the JWT. Its value MUST be a number containing a **NumericDate** value. Use of this claim is OPTIONAL.
+
+```php
+$payload = ['data' => 'data', 'iat' => time()];
+
+$token = jwt_encode($payload, $hmackey, 'HS256');
+
+try {
+ $decoded_token = jwt_decode($token, $hmackey, ['algorithm' => 'HS256']);
+} catch (Exception $e) {
+ // Handle invalid token
+}
+```
+
+### Subject Claim
+
+> The `sub` (subject) claim identifies the principal that is the subject of the JWT. The Claims in a JWT are normally statements about the subject. The subject value MUST either be scoped to be locally unique in the context of the issuer or be globally unique. The processing of this claim is generally application specific. The sub value is a case-sensitive string containing a **StringOrURI** value. Use of this claim is OPTIONAL.
+
+```php
+$payload = ['data' => 'data', 'sub' => 'Subject'];
+
+$token = jwt_encode($payload, $hmackey, 'HS256');
+
+try {
+ $decoded_token = jwt_decode($token, $hmackey, ['sub' => 'Subject', 'algorithm' => 'HS256']);
+} catch (Exception $e) {
+ // Handle invalid token
+}
+```
+
## Benchmarks

-## Methods
+## Functions
```php
//encode
-string jwt_encode(array $claims, string $key [, string $alg = 'HS256'])
+string jwt_encode(array $claims, string $key [, string $algorithm = 'HS256'])
//decode
-array jwt_decode(string $token, string $key [, string $alg = 'HS256'])
+array jwt_decode(string $token, string $key [, array $options = ['algorithm' => 'HS256']])
```
## The algorithm of support
@@ -81,6 +336,13 @@ HMAC|HS256|HS384|HS512
RSA|RS256|RS384|RS512
ECDSA|ES256|ES384|ES512
+## Inspired By
+
+-
+-
+-
+-
+
## License
PHP License. See the [LICENSE](LICENSE) file.
\ No newline at end of file
diff --git a/example/ecdsa.php b/example/ecdsa.php
index 955b5d7..827334b 100644
--- a/example/ecdsa.php
+++ b/example/ecdsa.php
@@ -17,7 +17,7 @@
-----END PUBLIC KEY-----
EOD;
-$claims = array(
+$payload = array(
"data" => [
"name" => "ZiHang Gao",
"admin" => true
@@ -26,7 +26,7 @@
"sub" => "1234567890",
);
-$token = jwt_encode($claims, $privateKey, 'ES256');
+$token = jwt_encode($payload, $privateKey, 'ES256');
echo $token . PHP_EOL;
-print_r(jwt_decode($token, $publicKey, 'ES256'));
\ No newline at end of file
+print_r(jwt_decode($token, $publicKey, ['algorithm' => 'ES256']));
\ No newline at end of file
diff --git a/example/hmac.php b/example/hmac.php
index ecd4fac..ddaa7a8 100644
--- a/example/hmac.php
+++ b/example/hmac.php
@@ -1,7 +1,7 @@
[
"name" => "ZiHang Gao",
"admin" => true
@@ -11,7 +11,7 @@
);
// default HS256 algorithm
-$token = jwt_encode($claims, $key);
+$token = jwt_encode($payload, $key);
echo $token . PHP_EOL;
print_r(jwt_decode($token, $key));
\ No newline at end of file
diff --git a/example/none.php b/example/none.php
new file mode 100644
index 0000000..eda02ac
--- /dev/null
+++ b/example/none.php
@@ -0,0 +1,16 @@
+ [
+ "name" => "ZiHang Gao",
+ "admin" => true
+ ],
+ "iss" => "http://example.org",
+ "sub" => "1234567890",
+);
+
+// none algorithm
+$token = jwt_encode($payload, null, 'none');
+
+echo $token . PHP_EOL;
+print_r(jwt_decode($token, null, false));
\ No newline at end of file
diff --git a/example/rsa.php b/example/rsa.php
index 0d64d58..bb07ace 100644
--- a/example/rsa.php
+++ b/example/rsa.php
@@ -28,7 +28,7 @@
-----END PUBLIC KEY-----
EOD;
-$claims = array(
+$payload = array(
"data" => [
"name" => "ZiHang Gao",
"admin" => true
@@ -37,7 +37,7 @@
"sub" => "1234567890",
);
-$token = jwt_encode($claims, $privateKey, 'RS256');
+$token = jwt_encode($payload, $privateKey, 'RS256');
echo $token . PHP_EOL;
-print_r(jwt_decode($token, $publicKey, 'RS256'));
\ No newline at end of file
+print_r(jwt_decode($token, $publicKey, ['algorithm' => 'RS256']));
\ No newline at end of file
diff --git a/jwt.c b/jwt.c
index 4a35467..51d3fd2 100644
--- a/jwt.c
+++ b/jwt.c
@@ -37,6 +37,8 @@
#include "php_jwt.h"
+ZEND_DECLARE_MODULE_GLOBALS(jwt)
+
/* string to algorithm */
jwt_alg_t jwt_str_alg(const char *alg)
{
@@ -221,36 +223,147 @@ zend_string *jwt_b64_url_decode(const char *src)
return php_base64_decode_ex((const unsigned char *)new, strlen(new), 1);
}
-void jwt_parse_body(char *body, zval *return_value)
+char *jwt_hash_str_find_str(zval *arr, char *key)
+{
+ char *str = NULL;
+ zval *zv = zend_hash_str_find(Z_ARRVAL_P(arr), key, strlen(key));
+
+ if (zv != NULL) {
+ str = Z_STRVAL_P(zv);
+ }
+
+ return str;
+}
+
+long jwt_hash_str_find_long(zval *arr, char *key)
+{
+ zval *zv = zend_hash_str_find(Z_ARRVAL_P(arr), key, strlen(key));
+
+ if (zv != NULL) {
+ return Z_LVAL_P(zv);
+ }
+
+ return 0;
+}
+
+int jwt_verify_claims(zval *arr, char *key, char *str)
{
+ char *rs = jwt_hash_str_find_str(arr, key);
+ if (rs && strcmp(rs, str)) {
+ return FAILURE;
+ }
+
+ return 0;
+}
+
+int jwt_verify_body(char *body, zval *return_value)
+{
+ char *err_msg = NULL;
+ time_t curr_time = time((time_t*)NULL);
zend_string *vs = jwt_b64_url_decode(body);
php_json_decode_ex(return_value, ZSTR_VAL(vs), ZSTR_LEN(vs), PHP_JSON_OBJECT_AS_ARRAY, 512);
-
zend_string_free(vs);
+
+ /* Expiration */
+ if (JWT_G(expiration) && (curr_time - JWT_G(leeway)) >= JWT_G(expiration)) {
+ err_msg = "Expired token";
+ }
+
+ /* not before */
+ if (JWT_G(not_before) && JWT_G(not_before) > (curr_time + JWT_G(leeway))) {
+ struct tm *timeinfo;
+ char buf[128];
+
+ timeinfo = localtime(&JWT_G(not_before));
+ strftime(buf, sizeof(buf), "Cannot handle token prior to %Y-%m-%d %H:%M:%S", timeinfo);
+ err_msg = buf;
+ }
+
+ /* iss */
+ if (JWT_G(iss) && jwt_verify_claims(return_value, "iss", JWT_G(iss)))
+ err_msg = "Iss verify fail";
+
+ /* iat */
+ if (JWT_G(iat) && JWT_G(iat) > (curr_time + JWT_G(leeway))) {
+ struct tm *timeinfo;
+ char buf[128];
+
+ timeinfo = localtime(&JWT_G(iat));
+ strftime(buf, sizeof(buf), "Cannot handle token prior to %Y-%m-%d %H:%M:%S", timeinfo);
+ err_msg = buf;
+ }
+
+ /* jti */
+ if (JWT_G(jti) && jwt_verify_claims(return_value, "jti", JWT_G(jti)))
+ err_msg = "Tti verify fail";
+
+ /* aud */
+ if (JWT_G(aud) && jwt_verify_claims(return_value, "aud", JWT_G(aud)))
+ err_msg = "Aud verify fail";
+
+ /* sub */
+ if (JWT_G(sub) && jwt_verify_claims(return_value, "sub", JWT_G(sub)))
+ err_msg = "Sub verify fail";
+
+ if (err_msg) {
+ zend_throw_exception(zend_ce_exception, err_msg, 0);
+ return FAILURE;
+ }
+
+ return 0;
}
+int jwt_parse_options(zval *options)
+{
+ /* check options */
+ if (options != NULL) {
+ switch(Z_TYPE_P(options)) {
+ case IS_ARRAY:
+ {
+ /* check algorithm */
+ char *alg = jwt_hash_str_find_str(options, "algorithm");
+ if (alg) {
+ JWT_G(algorithm) = alg;
+ }
+
+ /* options */
+ JWT_G(leeway) = jwt_hash_str_find_long(options, "leeway");
+ JWT_G(iss) = jwt_hash_str_find_str(options, "iss");
+ JWT_G(jti) = jwt_hash_str_find_str(options, "jti");
+ JWT_G(aud) = jwt_hash_str_find_str(options, "aud");
+ JWT_G(sub) = jwt_hash_str_find_str(options, "sub");
+ }
+ break;
+ case IS_NULL:
+ case IS_FALSE:
+ JWT_G(algorithm) = "none";
+ break;
+ default:
+ break;
+ }
+ }
+
+ return 0;
+}
PHP_FUNCTION(jwt_encode)
{
- zval *claims = NULL, header;
+ zval *payload = NULL, header;
zend_string *key = NULL;
- smart_str json_header = {0}, json_claims = {0}, segments = {0};
+ smart_str json_header = {0}, json_payload = {0}, segments = {0};
- char *sig = NULL, *alg = NULL;
+ char *sig = NULL, *alg = "HS256";
unsigned int sig_len;
size_t alg_len;
jwt_t *jwt = NULL;
- if (zend_parse_parameters(ZEND_NUM_ARGS(), "aS|s", &claims, &key, &alg, &alg_len) == FAILURE) {
+ if (zend_parse_parameters(ZEND_NUM_ARGS(), "aS|s", &payload, &key, &alg, &alg_len) == FAILURE) {
return;
}
/* init jwt */
jwt_new(&jwt);
- /* not set algorithm */
- alg = (alg == NULL) ? "HS256" : alg;
-
/* check algorithm */
jwt->alg = jwt_str_alg(alg);
@@ -259,6 +372,11 @@ PHP_FUNCTION(jwt_encode)
goto encode_done;
}
+ /* set expiration and not before */
+ JWT_G(expiration) = jwt_hash_str_find_long(payload, "exp");
+ JWT_G(not_before) = jwt_hash_str_find_long(payload, "nbf");
+ JWT_G(iat) = jwt_hash_str_find_long(payload, "iat");
+
/* init */
array_init(&header);
@@ -268,35 +386,41 @@ PHP_FUNCTION(jwt_encode)
/* json encode */
php_json_encode(&json_header, &header, 0);
- php_json_encode(&json_claims, claims, 0);
+ php_json_encode(&json_payload, payload, 0);
zval_ptr_dtor(&header);
/* base64 encode */
smart_str_appends(&segments, jwt_b64_url_encode(json_header.s));
smart_str_appends(&segments, ".");
- smart_str_appends(&segments, jwt_b64_url_encode(json_claims.s));
+ smart_str_appends(&segments, jwt_b64_url_encode(json_payload.s));
smart_str_free(&json_header);
- smart_str_free(&json_claims);
-
- /* set jwt struct */
- jwt->key = key;
- jwt->str = segments.s;
+ smart_str_free(&json_payload);
/* sign */
- if (jwt_sign(jwt, &sig, &sig_len)) {
- zend_throw_exception(zend_ce_exception, "Signature error", 0);
- goto encode_done;
- }
+ if (jwt->alg == JWT_ALG_NONE) {
+ // alg none.
+ smart_str_appendl(&segments, ".", 1);
+ } else {
+ /* set jwt struct */
+ jwt->key = key;
+ jwt->str = segments.s;
+
+ /* sign */
+ if (jwt_sign(jwt, &sig, &sig_len)) {
+ zend_throw_exception(zend_ce_exception, "Signature error", 0);
+ goto encode_done;
+ }
- /* string concatenation */
- smart_str_appends(&segments, ".");
+ /* string concatenation */
+ smart_str_appends(&segments, ".");
- zend_string *sig_str = zend_string_init(sig, sig_len, 0);
+ zend_string *sig_str = zend_string_init(sig, sig_len, 0);
- smart_str_appends(&segments, jwt_b64_url_encode(sig_str));
- zend_string_free(sig_str);
+ smart_str_appends(&segments, jwt_b64_url_encode(sig_str));
+ zend_string_free(sig_str);
+ }
smart_str_0(&segments);
@@ -315,25 +439,28 @@ PHP_FUNCTION(jwt_encode)
PHP_FUNCTION(jwt_decode)
{
zend_string *token = NULL, *key = NULL;
+ zval *options = NULL;
smart_str segments = {0};
- char *alg = NULL, *body = NULL, *sig = NULL;
- size_t alg_len;
+ char *body = NULL, *sig = NULL;
jwt_t *jwt = NULL;
- if (zend_parse_parameters(ZEND_NUM_ARGS(), "SS|s", &token, &key, &alg, &alg_len) == FAILURE) {
+ if (zend_parse_parameters(ZEND_NUM_ARGS(), "SS|z", &token, &key, &options) == FAILURE) {
return;
}
- /* not set algorithm */
- alg = (alg == NULL) ? "HS256" : alg;
-
char *head = estrdup(ZSTR_VAL(token));
/* jwt init */
jwt_new(&jwt);
- /* check algorithm */
- jwt->alg = jwt_str_alg(alg);
+ /* Parse options */
+ if (jwt_parse_options(options) == FAILURE) {
+ zend_throw_exception(zend_ce_exception, "Options parse error", 0);
+ goto decode_done;
+ }
+
+ /* Algorithm */
+ jwt->alg = jwt_str_alg(JWT_G(algorithm));
if (jwt->alg == JWT_ALG_INVAL) {
zend_throw_exception(zend_ce_exception, "Algorithm not supported", 0);
@@ -376,7 +503,7 @@ PHP_FUNCTION(jwt_decode)
zval_ptr_dtor(&zv);
- if (strcmp(Z_STRVAL_P(zalg), alg)) {
+ if (strcmp(Z_STRVAL_P(zalg), JWT_G(algorithm))) {
zend_throw_exception(zend_ce_exception, "Algorithm not allowed", 0);
goto decode_done;
}
@@ -386,19 +513,26 @@ PHP_FUNCTION(jwt_decode)
}
/* parse body */
- jwt_parse_body(body, return_value);
+ if (jwt_verify_body(body, return_value) == FAILURE) {
+ goto decode_done;
+ }
- /* set jwt struct */
- jwt->key = key;
+ /* verify */
+ if (jwt->alg == JWT_ALG_NONE) {
+ /* done */
+ } else {
+ /* set jwt struct */
+ jwt->key = key;
- smart_str_appends(&segments, head);
- smart_str_appends(&segments, ".");
- smart_str_appends(&segments, body);
+ smart_str_appends(&segments, head);
+ smart_str_appends(&segments, ".");
+ smart_str_appends(&segments, body);
- jwt->str = segments.s;
+ jwt->str = segments.s;
- if (jwt_verify(jwt, sig)) {
- zend_throw_exception(zend_ce_exception, "Signature verification failed", 0);
+ if (jwt_verify(jwt, sig)) {
+ zend_throw_exception(zend_ce_exception, "Signature verification failed", 0);
+ }
}
smart_str_free(&segments);
@@ -414,6 +548,19 @@ const zend_function_entry jwt_functions[] = {
PHP_FE_END
};
+/* GINIT */
+PHP_GINIT_FUNCTION(jwt) {
+ jwt_globals->expiration = 0;
+ jwt_globals->not_before = 0;
+ jwt_globals->iss = NULL;
+ jwt_globals->iat = 0;
+ jwt_globals->jti = NULL;
+ jwt_globals->aud = NULL;
+ jwt_globals->sub = NULL;
+ jwt_globals->leeway = 0;
+ jwt_globals->algorithm = "HS256";
+}
+
PHP_MINIT_FUNCTION(jwt)
{
return SUCCESS;
@@ -438,14 +585,14 @@ PHP_MINFO_FUNCTION(jwt)
php_info_print_table_end();
}
-static const zend_module_dep jwt_dep_deps[] = {
+static const zend_module_dep jwt_deps[] = {
ZEND_MOD_REQUIRED("json")
ZEND_MOD_END
};
zend_module_entry jwt_module_entry = {
STANDARD_MODULE_HEADER_EX, NULL,
- jwt_dep_deps,
+ jwt_deps,
"jwt",
jwt_functions,
PHP_MINIT(jwt),
@@ -454,7 +601,11 @@ zend_module_entry jwt_module_entry = {
NULL, /* Replace with NULL if there's nothing to do at request end */
PHP_MINFO(jwt),
PHP_JWT_VERSION,
- STANDARD_MODULE_PROPERTIES
+ PHP_MODULE_GLOBALS(jwt),
+ PHP_GINIT(jwt),
+ NULL,
+ NULL,
+ STANDARD_MODULE_PROPERTIES_EX
};
#ifdef COMPILE_DL_JWT
diff --git a/jwt.php b/jwt.php
index de0f160..00f64c3 100644
--- a/jwt.php
+++ b/jwt.php
@@ -11,12 +11,12 @@
"name" => "ZiHang Gao",
"admin" => true
],
- "iss" => "http://example.org",
"sub" => "1234567890",
+ "nbf" => time() + 100
);
// default HS256 algorithm
$token = jwt_encode($claims, $key);
echo $token . PHP_EOL;
-print_r(jwt_decode($token, $key));
+print_r(jwt_decode($token, $key, ['leeway' => 2, "iss" => "http://example.org"]));
diff --git a/php_jwt.h b/php_jwt.h
index 8c6ad0d..a2520c3 100644
--- a/php_jwt.h
+++ b/php_jwt.h
@@ -30,6 +30,21 @@ extern zend_module_entry jwt_module_entry;
#include "TSRM.h"
#endif
+#define JWT_G(v) ZEND_MODULE_GLOBALS_ACCESSOR(jwt, v)
+
+ZEND_BEGIN_MODULE_GLOBALS(jwt)
+ time_t expiration;
+ time_t not_before;
+ char *iss;
+ time_t iat;
+ char *jti;
+ char *aud;
+ char *sub;
+ size_t leeway;
+ char *algorithm;
+ZEND_END_MODULE_GLOBALS(jwt)
+
+
/** JWT algorithm types. */
typedef enum jwt_alg {
JWT_ALG_NONE = 0,
diff --git a/tests/002.phpt b/tests/002.phpt
index 653ec30..0bf4c02 100644
--- a/tests/002.phpt
+++ b/tests/002.phpt
@@ -5,7 +5,7 @@ Check for jwt HMAC algorithm (HS256)
--FILE--
[
"name" => "ZiHang Gao",
"admin" => true
@@ -14,7 +14,7 @@ $claims = array(
"sub" => "1234567890",
);
-$token = jwt_encode($claims, $key);
+$token = jwt_encode($payload, $key);
echo $token . PHP_EOL;
print_r(jwt_decode($token, $key));
diff --git a/tests/003.phpt b/tests/003.phpt
index 88f5b3b..b3eec55 100644
--- a/tests/003.phpt
+++ b/tests/003.phpt
@@ -32,7 +32,7 @@ UnR8tSdwUqI3119zAQIDAQAB
-----END PUBLIC KEY-----
EOD;
-$claims = array(
+$payload = array(
"data" => [
"name" => "ZiHang Gao",
"admin" => true
@@ -41,10 +41,10 @@ $claims = array(
"sub" => "1234567890",
);
-$token = jwt_encode($claims, $privateKey, 'RS256');
+$token = jwt_encode($payload, $privateKey, 'RS256');
echo $token . PHP_EOL;
-print_r(jwt_decode($token, $publicKey, 'RS256'));
+print_r(jwt_decode($token, $publicKey, ['algorithm' => 'RS256']));
?>
--EXPECT--
eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJkYXRhIjp7Im5hbWUiOiJaaUhhbmcgR2FvIiwiYWRtaW4iOnRydWV9LCJpc3MiOiJodHRwOlwvXC9leGFtcGxlLm9yZyIsInN1YiI6IjEyMzQ1Njc4OTAifQ.iSpiBRxW0RKDK0KZRDTwEQmllzkS-WQKMzQ8j3pSSA8NI3ukj0EjZCWIsWfgmb20xKViT5UhUAf_1UQwxwu5k0laEkJ8Ze04gj1P8KO--7BkxUFp4UerkDex49lwHSJmvTJBRZBy9t7BDqCaouEpUe0vZ6z_siMX95VMvVrk0g0
diff --git a/tests/004.phpt b/tests/004.phpt
index 01955d3..bbb4c2c 100644
--- a/tests/004.phpt
+++ b/tests/004.phpt
@@ -21,7 +21,7 @@ qN3tlSZmUEZ3w3c6KYJfK97PMOSZQaUdeydBoq/IOglQQOj8zLqubq5IpaaUiDQ5
-----END PUBLIC KEY-----
EOD;
-$claims = array(
+$payload = array(
"data" => [
"name" => "ZiHang Gao",
"admin" => true
@@ -30,8 +30,8 @@ $claims = array(
"sub" => "1234567890",
);
-$token = jwt_encode($claims, $privateKey, 'ES256');
-print_r(jwt_decode($token, $publicKey, 'ES256'));
+$token = jwt_encode($payload, $privateKey, 'ES256');
+print_r(jwt_decode($token, $publicKey, ['algorithm' => 'ES256']));
?>
--EXPECT--
Array
diff --git a/tests/005.phpt b/tests/005.phpt
new file mode 100644
index 0000000..46ab441
--- /dev/null
+++ b/tests/005.phpt
@@ -0,0 +1,35 @@
+--TEST--
+Check for jwt NONE algorithm
+--SKIPIF--
+
+--FILE--
+ [
+ "name" => "ZiHang Gao",
+ "admin" => true
+ ],
+ "iss" => "http://example.org",
+ "sub" => "1234567890",
+);
+
+// none algorithm
+$token = jwt_encode($payload, null, 'none');
+
+echo $token . PHP_EOL;
+print_r(jwt_decode($token, null, false));
+?>
+--EXPECT--
+eyJ0eXAiOiJKV1QiLCJhbGciOiJub25lIn0.eyJkYXRhIjp7Im5hbWUiOiJaaUhhbmcgR2FvIiwiYWRtaW4iOnRydWV9LCJpc3MiOiJodHRwOlwvXC9leGFtcGxlLm9yZyIsInN1YiI6IjEyMzQ1Njc4OTAifQ.
+Array
+(
+ [data] => Array
+ (
+ [name] => ZiHang Gao
+ [admin] => 1
+ )
+
+ [iss] => http://example.org
+ [sub] => 1234567890
+)
diff --git a/tests/006.phpt b/tests/006.phpt
new file mode 100644
index 0000000..82470e5
--- /dev/null
+++ b/tests/006.phpt
@@ -0,0 +1,28 @@
+--TEST--
+Check for jwt exp claim name (Expired token)
+--SKIPIF--
+
+--FILE--
+ 'data', 'exp' => time() - 10];
+$token = jwt_encode($payload, $hmackey, 'HS256');
+
+try {
+ $decoded_token = jwt_decode($token, $hmackey, ['algorithm' => 'HS256']);
+} catch (Exception $e) {
+ // Expired token
+ echo $e->getMessage() . "\n";
+}
+
+try {
+ $decoded_token = jwt_decode($token, $hmackey, ['leeway' => 30, 'algorithm' => 'HS256']);
+ echo "Success\n";
+} catch (Exception $e) {
+ // Expired token
+ echo $e->getMessage() . "\n";
+}
+?>
+--EXPECT--
+Expired token
+Success
diff --git a/tests/007.phpt b/tests/007.phpt
new file mode 100644
index 0000000..381d120
--- /dev/null
+++ b/tests/007.phpt
@@ -0,0 +1,27 @@
+--TEST--
+Check for jwt nbf claim name
+--SKIPIF--
+
+--FILE--
+ 'data', 'nbf' => time() + 10];
+$token = jwt_encode($payload, $hmackey, 'HS256');
+
+try {
+ $decoded_token = jwt_decode($token, $hmackey, ['algorithm' => 'HS256']);
+} catch (Exception $e) {
+ // Expired token
+ echo "FAIL\n";
+}
+
+try {
+ $decoded_token = jwt_decode($token, $hmackey, ['leeway' => 30, 'algorithm' => 'HS256']);
+ echo "SUCCESS\n";
+} catch (Exception $e) {
+ // Expired token
+}
+?>
+--EXPECT--
+FAIL
+SUCCESS
diff --git a/tests/008.phpt b/tests/008.phpt
new file mode 100644
index 0000000..c470e59
--- /dev/null
+++ b/tests/008.phpt
@@ -0,0 +1,20 @@
+--TEST--
+Check for jwt iat claim name
+--SKIPIF--
+
+--FILE--
+ 'data', 'iat' => time()];
+$token = jwt_encode($payload, $hmackey, 'HS256');
+
+try {
+ $decoded_token = jwt_decode($token, $hmackey, ['algorithm' => 'HS256']);
+ echo "SUCCESS\n";
+} catch (Exception $e) {
+ // Handle invalid token
+}
+?>
+--EXPECT--
+SUCCESS
+
diff --git a/tests/009.phpt b/tests/009.phpt
new file mode 100644
index 0000000..11fdd2f
--- /dev/null
+++ b/tests/009.phpt
@@ -0,0 +1,28 @@
+--TEST--
+Check for jwt iss claim name
+--SKIPIF--
+
+--FILE--
+ 'data', 'iss' => 'http://example.org'];
+
+$token = jwt_encode($payload, $hmackey, 'HS256');
+
+try {
+ $decoded_token = jwt_decode($token, $hmackey, ['iss' => 'http://example.org', 'algorithm' => 'HS256']);
+ echo "SUCCESS\n";
+} catch (Exception $e) {
+ // Handle invalid token
+}
+
+try {
+ $decoded_token = jwt_decode($token, $hmackey, ['iss' => 'test', 'algorithm' => 'HS256']);
+} catch (Exception $e) {
+ // Handle invalid token
+ echo "FAIL\n";
+}
+?>
+--EXPECT--
+SUCCESS
+FAIL
diff --git a/tests/010.phpt b/tests/010.phpt
new file mode 100644
index 0000000..f680a96
--- /dev/null
+++ b/tests/010.phpt
@@ -0,0 +1,20 @@
+--TEST--
+Check for jwt claim name
+--SKIPIF--
+
+--FILE--
+ 'data', 'sub' => '1234567890'];
+
+$token = jwt_encode($payload, $hmackey);
+
+try {
+ $decoded_token = jwt_decode($token, $hmackey, ['iss' => 'http://example.org']);
+ echo "SUCCESS\n";
+} catch (Exception $e) {
+ // Expired token
+}
+?>
+--EXPECT--
+SUCCESS