From 571467b20310bd6113d13b3e25e691a51cf50744 Mon Sep 17 00:00:00 2001 From: ace411 Date: Sat, 7 Sep 2019 09:43:33 +0300 Subject: [PATCH 01/44] Created ccpp version --- .gitignore | 40 ++++++++- Makefile | 135 ----------------------------- README.md | 2 - config.m4 | 11 +++ extjwt.cpp | 201 ++++++++++++++++++++++++++++++++++++++++++++ extjwt_cpp.ini | 1 - jwt.cpp | 82 ++++++++++++++++++ jwt.h | 15 ++++ main.cpp | 113 ------------------------- php_extjwt.h | 23 +++++ tests/001.phpt | 14 +++ tests/002.phpt | 10 +++ tests/0021.phpt | 14 +++ tests/0022.phpt | 14 +++ tests/003.phpt | 13 +++ tests/0031.phpt | 16 ++++ tests/0032.phpt | 12 +++ tests/004.phpt | 14 +++ tests/constants.php | 8 ++ 19 files changed, 486 insertions(+), 252 deletions(-) delete mode 100644 Makefile create mode 100644 config.m4 create mode 100644 extjwt.cpp delete mode 100644 extjwt_cpp.ini create mode 100644 jwt.cpp create mode 100644 jwt.h delete mode 100644 main.cpp create mode 100644 php_extjwt.h create mode 100644 tests/001.phpt create mode 100644 tests/002.phpt create mode 100644 tests/0021.phpt create mode 100644 tests/0022.phpt create mode 100644 tests/003.phpt create mode 100644 tests/0031.phpt create mode 100644 tests/0032.phpt create mode 100644 tests/004.phpt create mode 100644 tests/constants.php diff --git a/.gitignore b/.gitignore index 2da24eb..bd3acca 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,41 @@ +/TODO /*.so +/*.out +/*.php /*.o -/jwt-cpp/ \ No newline at end of file +/.vscode/ +/.libs/ +/jwt-cpp/ +/tests/00*.php + +/autom4*/ +/build/ +/include/ +/modules/ +/vendor/ + +*.guess +*.log +*.lo +*.la +*.out +*.exp +*.diff +*.sh + +/config.h* +/config.nice +/config.status +/config.sub +/configure +/configure.ac +/missing +/mkinstalldirs + +/install-sh +/libtool +/ltmain* +/Makefile* +/acinclude.m4 +/aclocal.m4 +/composer.* \ No newline at end of file diff --git a/Makefile b/Makefile deleted file mode 100644 index 8f53f60..0000000 --- a/Makefile +++ /dev/null @@ -1,135 +0,0 @@ -# -# Makefile template -# -# This is an example Makefile that can be used by anyone who is building -# his or her own PHP extensions using the PHP-CPP library. -# -# In the top part of this file we have included variables that can be -# altered to fit your configuration, near the bottom the instructions and -# dependencies for the compiler are defined. The deeper you get into this -# file, the less likely it is that you will have to change anything in it. -# - -# -# Name of your extension -# -# This is the name of your extension. Based on this extension name, the -# name of the library file (name.so) and the name of the config file (name.ini) -# are automatically generated -# - -NAME = extjwt_cpp - - -# -# Php.ini directories -# -# In the past, PHP used a single php.ini configuration file. Today, most -# PHP installations use a conf.d directory that holds a set of config files, -# one for each extension. Use this variable to specify this directory. -# - -INI_DIR = /etc/php/7.2/cli/conf.d - - -# -# The extension dirs -# -# This is normally a directory like /usr/lib/php5/20121221 (based on the -# PHP version that you use. We make use of the command line 'php-config' -# instruction to find out what the extension directory is, you can override -# this with a different fixed directory -# - -EXTENSION_DIR = $(shell php-config --extension-dir) - - -# -# The name of the extension and the name of the .ini file -# -# These two variables are based on the name of the extension. We simply add -# a certain extension to them (.so or .ini) -# - -EXTENSION = ${NAME}.so -INI = ${NAME}.ini - - -# -# Compiler -# -# By default, the GNU C++ compiler is used. If you want to use a different -# compiler, you can change that here. You can change this for both the -# compiler (the program that turns the c++ files into object files) and for -# the linker (the program that links all object files into the single .so -# library file. By default, g++ (the GNU C++ compiler) is used for both. -# - -COMPILER = g++ -LINKER = g++ - - -# -# Compiler and linker flags -# -# This variable holds the flags that are passed to the compiler. By default, -# we include the -O2 flag. This flag tells the compiler to optimize the code, -# but it makes debugging more difficult. So if you're debugging your application, -# you probably want to remove this -O2 flag. At the same time, you can then -# add the -g flag to instruct the compiler to include debug information in -# the library (but this will make the final libphpcpp.so file much bigger, so -# you want to leave that flag out on production servers). -# -# If your extension depends on other libraries (and it does at least depend on -# one: the PHP-CPP library), you should update the LINKER_DEPENDENCIES variable -# with a list of all flags that should be passed to the linker. -# - -COMPILER_FLAGS = -Wall -c -O2 -std=c++11 -fpic -o -LINKER_FLAGS = -shared -LINKER_DEPENDENCIES = -lphpcpp - - -# -# Command to remove files, copy files and create directories. -# -# I've never encountered a *nix environment in which these commands do not work. -# So you can probably leave this as it is -# - -RM = rm -f -CP = cp -f -MKDIR = mkdir -p - - -# -# All source files are simply all *.cpp files found in the current directory -# -# A builtin Makefile macro is used to scan the current directory and find -# all source files. The object files are all compiled versions of the source -# file, with the .cpp extension being replaced by .o. -# - -SOURCES = $(wildcard *.cpp) -OBJECTS = $(SOURCES:%.cpp=%.o) - - -# -# From here the build instructions start -# - -all: ${OBJECTS} ${EXTENSION} - -${EXTENSION}: ${OBJECTS} - ${LINKER} ${LINKER_FLAGS} -o $@ ${OBJECTS} ${LINKER_DEPENDENCIES} - -${OBJECTS}: - ${COMPILER} ${COMPILER_FLAGS} $@ ${@:%.o=%.cpp} - -install: - ${CP} ${EXTENSION} ${EXTENSION_DIR} - ${CP} ${INI} ${INI_DIR} - -clean: - ${RM} ${EXTENSION} ${OBJECTS} - diff --git a/README.md b/README.md index 875ee62..dfeda0d 100644 --- a/README.md +++ b/README.md @@ -4,10 +4,8 @@ A simple PHP extension for encoding and decoding JSON Web Tokens (JWTs). ## Requirements -- C++ 11 - OpenSSL - [gtest](https://github.com/google/googletest) (for jwt-cpp) -- [PHP-CPP](https://github.com/CopernicaMarketingSoftware/PHP-CPP) - [jwt-cpp](https://github.com/Thalhammer/jwt-cpp) package - PHP 7 or greater diff --git a/config.m4 b/config.m4 new file mode 100644 index 0000000..32fcd17 --- /dev/null +++ b/config.m4 @@ -0,0 +1,11 @@ +PHP_ARG_ENABLE(extjwt, whether to enable extjwt support, +dnl Make sure that the comment is aligned: +[ --enable-extjwt Enable extjwt support], no) + +if test "$PHP_EXTJWT" != "no"; then + CXXFLAGS="-std=c++11 -lssl -lcrypto" + PHP_REQUIRE_CXX() + PHP_SUBST(EXTJWT_SHARED_LIBADD) + PHP_ADD_LIBRARY(stdc++, 1, EXTJWT_SHARED_LIBADD) + PHP_NEW_EXTENSION(extjwt, extjwt.cpp jwt.cpp, $ext_shared) +fi \ No newline at end of file diff --git a/extjwt.cpp b/extjwt.cpp new file mode 100644 index 0000000..f0befa4 --- /dev/null +++ b/extjwt.cpp @@ -0,0 +1,201 @@ +#include "php_extjwt.h" +#include "jwt.cpp" + +#ifdef HAVE_SPL +#include "ext/spl/spl_exceptions.h" +#endif + +zend_class_entry *jwt_exception_ce; + +template +auto algoIsValid(L algo) -> bool +{ + switch (algo) + { + case JWT_ALGO_HS256: + case JWT_ALGO_HS384: + case JWT_ALGO_HS512: + return true; + break; + + default: + return false; + break; + } +} + +PHP_FUNCTION(jwt_encode) +{ + zend_string *secret; + long algo; + zval *claims; + + zend_string *key; + zval *claim; + strmap jwtClaims; + + ZEND_PARSE_PARAMETERS_START(0, 3) + Z_PARAM_STR(secret) + Z_PARAM_LONG(algo) + Z_PARAM_ARRAY(claims) + ZEND_PARSE_PARAMETERS_END(); + + if (zend_hash_num_elements(HASH_OF(claims)) == 0 || + ZSTR_LEN(secret) == 0) + { + zend_string_release(secret); + zend_throw_exception(jwt_exception_ce, + "Argument(s) cannot be empty", + 0 TSRMLS_CC); + RETURN_NULL(); + } + + if (algoIsValid(algo) == false) + { + zend_string_release(secret); + zend_throw_exception(jwt_exception_ce, + "Invalid algorithm detected", + 0 TSRMLS_CC); + RETURN_NULL(); + } + + ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(claims), key, claim) { + convert_to_string(claim); + + jwtClaims[ZSTR_VAL(key)] = Z_STRVAL_P(claim); + } ZEND_HASH_FOREACH_END(); + + auto retval = jwtEncode(std::string(ZSTR_VAL(secret)), + algo, + jwtClaims); + RETURN_STRING(retval.c_str()); + zend_string_release(key); + zend_string_release(secret); +} + +PHP_FUNCTION(jwt_decode) +{ + zend_string *secret; + zend_string *token; + long algo; + + zval *retval; + + ZEND_PARSE_PARAMETERS_START(0, 3) + Z_PARAM_STR(token) + Z_PARAM_STR(secret) + Z_PARAM_LONG(algo) + ZEND_PARSE_PARAMETERS_END(); + + if (algoIsValid(algo) == false) + { + zend_string_release(secret); + zend_string_release(token); + zend_throw_exception(jwt_exception_ce, + "Invalid algorithm detected", + 0 TSRMLS_CC); + RETURN_NULL(); + } + + try + { + auto claims = jwtDecode(std::string(ZSTR_VAL(token)), + algo, + std::string(ZSTR_VAL(secret))); + + array_init(retval); + zend_string_release(secret); + zend_string_release(token); + + for (auto &iter : claims) + { + add_assoc_string(retval, + iter.first.c_str(), + iter.second.c_str()); + } + + RETURN_ZVAL(retval, 1, 0); + } + catch (const std::exception &exp) + { + zend_string_release(secret); + zend_string_release(token); + zend_throw_exception(jwt_exception_ce, + exp.what(), + 0 TSRMLS_CC); + RETURN_NULL(); + } +} + +PHP_RINIT_FUNCTION(extjwt) +{ +#if defined(ZTS) && defined(COMPILE_DL_EXTJWT) + ZEND_TSRMLS_CACHE_UPDATE(); +#endif + + return SUCCESS; +} + +PHP_MINFO_FUNCTION(extjwt) +{ + php_info_print_table_start(); + php_info_print_table_header(2, "extjwt support", "enabled"); + php_info_print_table_end(); +} + +PHP_MINIT_FUNCTION(extjwt) +{ + zend_class_entry ce; + + INIT_CLASS_ENTRY(ce, "ExtJwtException", NULL); + +#ifdef HAVE_SPL + jwt_exception_ce = zend_register_internal_class_ex( + &ce, spl_ce_RuntimeException); +#else + jwt_exception_ce = zend_register_internal_class_ex( + &ce, zend_exception_get_default(TSRMLS_C)); +#endif + + REGISTER_LONG_CONSTANT("JWT_ALGO_HS256", JWT_ALGO_HS256, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("JWT_ALGO_HS384", JWT_ALGO_HS384, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("JWT_ALGO_HS512", JWT_ALGO_HS512, CONST_CS | CONST_PERSISTENT); + + return SUCCESS; +} + +ZEND_BEGIN_ARG_INFO_EX(arginfo_jwt_encode, 0, 0, 3) +ZEND_ARG_INFO(0, secret) +ZEND_ARG_INFO(0, algo) +ZEND_ARG_INFO(0, claims) +ZEND_END_ARG_INFO(); + +ZEND_BEGIN_ARG_INFO_EX(arginfo_jwt_decode, 0, 0, 3) +ZEND_ARG_INFO(0, token) +ZEND_ARG_INFO(0, secret) +ZEND_ARG_INFO(0, algo) +ZEND_END_ARG_INFO(); + +static const zend_function_entry extjwt_functions[] = { + PHP_FE(jwt_encode, arginfo_jwt_encode) + PHP_FE(jwt_decode, arginfo_jwt_decode) + PHP_FE_END}; + +zend_module_entry extjwt_module_entry = { + STANDARD_MODULE_HEADER, + PHP_EXTJWT_EXTNAME, + extjwt_functions, + PHP_MINIT(extjwt), + NULL, + PHP_RINIT(extjwt), + NULL, + PHP_MINFO(extjwt), + PHP_EXTJWT_EXTVER, + STANDARD_MODULE_PROPERTIES}; + +#ifdef COMPILE_DL_EXTJWT +extern "C" +{ + ZEND_GET_MODULE(extjwt) +} +#endif \ No newline at end of file diff --git a/extjwt_cpp.ini b/extjwt_cpp.ini deleted file mode 100644 index aef3ec2..0000000 --- a/extjwt_cpp.ini +++ /dev/null @@ -1 +0,0 @@ -extension=extjwt_cpp.so \ No newline at end of file diff --git a/jwt.cpp b/jwt.cpp new file mode 100644 index 0000000..57dd5cc --- /dev/null +++ b/jwt.cpp @@ -0,0 +1,82 @@ +#include +#include +#include "jwt.h" + +using namespace std; + +typedef initializer_list strList; + +template +auto signJwt(B obj, L algo, const S &secret) -> string +{ + string token(""); + + switch (algo) + { + case JWT_ALGO_HS256: + token += obj.sign(jwt::algorithm::hs256{secret}); + break; + + case JWT_ALGO_HS384: + token += obj.sign(jwt::algorithm::hs384{secret}); + break; + + case JWT_ALGO_HS512: + token += obj.sign(jwt::algorithm::hs512{secret}); + break; + + default: + break; + } + + return token; +} + +template +auto jwtEncode(const S &secret, L algo, I claims) -> S +{ + auto tokenObj = jwt::create() + .set_type("JWS"); + + for (auto &iter : claims) + { + tokenObj.set_payload_claim(iter.first, iter.second); + } + + if (claims.count("iss")) + { + tokenObj.set_issuer(claims["iss"]); + } + + return signJwt(tokenObj, + algo, + secret); +} + +template +auto jwtDecode(const S &token, L algo, const S &secret) -> strmap +{ + auto decoded = jwt::decode(token), verifier = jwt::verify(); + strmap claims; + + switch (algo) + { + case JWT_ALGO_HS256: + verifier.allow_algorithm(jwt::algorithm::hs256{secret}); + break; + + case JWT_ALGO_HS512: + verifier.allow_algorithm(jwt::algorithm::hs512{secret}); + break; + + default: + break; + } + + for (auto &iter : decoded.get_payload_claims()) + { + claims[iter.first] = iter.second.to_json().to_str(); + } + + return claims; +} \ No newline at end of file diff --git a/jwt.h b/jwt.h new file mode 100644 index 0000000..7b7640e --- /dev/null +++ b/jwt.h @@ -0,0 +1,15 @@ +#ifndef JWT_H +#define JWT_H + +#include +#include + +using namespace std; + +#define JWT_ALGO_HS256 1 +#define JWT_ALGO_HS384 2 +#define JWT_ALGO_HS512 3 + +typedef map strmap; + +#endif /* JWT_H */ \ No newline at end of file diff --git a/main.cpp b/main.cpp deleted file mode 100644 index b638b01..0000000 --- a/main.cpp +++ /dev/null @@ -1,113 +0,0 @@ -#include -#include -#include - -using namespace std; - -string jwths256 = "HS256"; -string jwths384 = "HS384"; -string jwths512 = "HS512"; - -//jwtEncode :: Secret -> Algo -> [Claims] -> Token -Php::Value jwtEncode(Php::Parameters ¶ms) -{ - string secret = params[0]; - string algo = params[1]; - Php::Value claims = params[2]; - string _token; - - auto token = jwt::create() - .set_type("JWS") - .set_algorithm(algo); - - for (auto &iter : claims) - { - string payloadClaim = iter.second; - token.set_payload_claim(iter.first, payloadClaim); - } - - if (algo == "HS256") - { - _token = token.sign(jwt::algorithm::hs256{secret}); - } - else if (algo == "HS384") - { - _token = token.sign(jwt::algorithm::hs384{secret}); - } - else if (algo == "HS512") - { - _token = token.sign(jwt::algorithm::hs512{secret}); - } else - { - throw Php::Exception("Invalid Algorithm. Supported algorithms are HS256, HS384, and HS512."); - } - return _token; -} - -//jwtDecode :: Token -> Secret -> Algo -> [Claims] -Php::Value jwtDecode(Php::Parameters ¶ms) -{ - string _jwt = params[0]; - string secret = params[1]; - string algo = params[2]; - Php::Value claims; - - try { - auto decoded = jwt::decode(_jwt); - auto verifier = jwt::verify(); - - if (algo == "HS256") - { - verifier.allow_algorithm(jwt::algorithm::hs256{secret}); - } - else if (algo == "HS384") - { - verifier.allow_algorithm(jwt::algorithm::hs384{secret}); - } - else if (algo == "HS512") - { - verifier.allow_algorithm(jwt::algorithm::hs512{secret}); - } - else - { - throw Php::Exception("Invalid Algorithm. Supported algorithms are HS256, HS384, and HS512."); - } - - for (auto &iter : decoded.get_payload_claims()) - { - claims[iter.first] = iter.second.to_json().to_str(); - } - return claims; - } - catch (const std::exception &e) - { - throw Php::Exception(e.what()); - } -} - -extern "C" { - PHPCPP_EXPORT void *get_module() - { - static Php::Extension extension("extjwt_cpp", "1.0"); - - extension.onStartup([=]() { - Php::define("JWT_ALGO_HS256", jwths256); - Php::define("JWT_ALGO_HS384", jwths384); - Php::define("JWT_ALGO_HS512", jwths512); - }); - - extension.add("jwt_encode", { - Php::ByVal("secret", Php::Type::String, true), - Php::ByVal("algorithm", Php::Type::String, true), - Php::ByVal("claims", Php::Type::Array, true) - }); - - extension.add("jwt_decode", { - Php::ByVal("token", Php::Type::String, true), - Php::ByVal("secret", Php::Type::String, true), - Php::ByVal("algorithm", Php::Type::String, true) - }); - - return extension; - } -} \ No newline at end of file diff --git a/php_extjwt.h b/php_extjwt.h new file mode 100644 index 0000000..b255fe4 --- /dev/null +++ b/php_extjwt.h @@ -0,0 +1,23 @@ +#ifndef PHP_EXTJWT_H +#define PHP_EXTJWT_H + +#define PHP_EXTJWT_EXTNAME "extjwt_cpp" +#define PHP_EXTJWT_EXTVER "0.1.0" + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +extern "C" +{ + #include "php.h" + #include "zend_smart_str.h" + #include "zend_exceptions.h" + #include "ext/standard/info.h" + #include "zend_exceptions.h" +} + +extern zend_module_entry extjwt_module_entry; +#define phpext_extjwt_ptr &extjwt_module_entry + +#endif /* PHP_EXTJWT_H */ \ No newline at end of file diff --git a/tests/001.phpt b/tests/001.phpt new file mode 100644 index 0000000..fdd734f --- /dev/null +++ b/tests/001.phpt @@ -0,0 +1,14 @@ +--TEST-- +Check if extension extjwt is loaded +--SKIPIF-- + +--FILE-- + +--EXPECT-- +The extension extjwt is available \ No newline at end of file diff --git a/tests/002.phpt b/tests/002.phpt new file mode 100644 index 0000000..0a594fa --- /dev/null +++ b/tests/002.phpt @@ -0,0 +1,10 @@ +--TEST-- +jwt_encode function outputs JSON Web Token +--FILE-- + +--EXPECT-- +string \ No newline at end of file diff --git a/tests/0021.phpt b/tests/0021.phpt new file mode 100644 index 0000000..2b338df --- /dev/null +++ b/tests/0021.phpt @@ -0,0 +1,14 @@ +--TEST-- +jwt_encode throws JwtException when invalid algorithm is detected +--FILE-- +getMessage(); +} +?> +--EXPECT-- +Invalid algorithm detected \ No newline at end of file diff --git a/tests/0022.phpt b/tests/0022.phpt new file mode 100644 index 0000000..465eccf --- /dev/null +++ b/tests/0022.phpt @@ -0,0 +1,14 @@ +--TEST-- +jwt_encode throws ExtJwtException when a parameter is empty +--FILE-- +getMessage(); +} +?> +--EXPECT-- +Argument(s) cannot be empty \ No newline at end of file diff --git a/tests/003.phpt b/tests/003.phpt new file mode 100644 index 0000000..4eb4a7a --- /dev/null +++ b/tests/003.phpt @@ -0,0 +1,13 @@ +--TEST-- +jwt_decode outputs JWT claims as hashtable +--FILE-- + +--EXPECT-- +array \ No newline at end of file diff --git a/tests/0031.phpt b/tests/0031.phpt new file mode 100644 index 0000000..714ec68 --- /dev/null +++ b/tests/0031.phpt @@ -0,0 +1,16 @@ +--TEST-- +jwt_decode throws JwtException when invalid algorithm is detected +--FILE-- +getMessage(); +} +?> +--EXPECT-- +Invalid algorithm detected \ No newline at end of file diff --git a/tests/0032.phpt b/tests/0032.phpt new file mode 100644 index 0000000..7f6d8be --- /dev/null +++ b/tests/0032.phpt @@ -0,0 +1,12 @@ +--TEST-- +jwt_decode throws ExtJwtException when a parameter is empty +--FILE-- +getMessage(); +} +?> +--EXPECT-- +invalid token supplied \ No newline at end of file diff --git a/tests/004.phpt b/tests/004.phpt new file mode 100644 index 0000000..7527893 --- /dev/null +++ b/tests/004.phpt @@ -0,0 +1,14 @@ +--TEST-- +jwt_decode throws ExtJwtException when decode error is encountered +--FILE-- +getMessage(); +} +?> +--EXPECT-- +invalid token supplied \ No newline at end of file diff --git a/tests/constants.php b/tests/constants.php new file mode 100644 index 0000000..28fbced --- /dev/null +++ b/tests/constants.php @@ -0,0 +1,8 @@ + '@ace411', + 'twitter' => '@agiroLoki' +]; From 229fa8f1bea159850771d65637f22f8e39027c5e Mon Sep 17 00:00:00 2001 From: ace411 Date: Sat, 7 Sep 2019 10:04:22 +0300 Subject: [PATCH 02/44] Modified README.md --- README.md | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index dfeda0d..1e379bf 100644 --- a/README.md +++ b/README.md @@ -21,12 +21,21 @@ Because ext-func is an extension built on top of PHP-CPP, installation of PHP-CP Once said tools are successfully installed on your system, type the following in a console to install extjwt-cpp on your machine. -``` -git clone https://github.com/ace411/extjwt-cpp.git -cd extjwt-cpp +```sh +git clone https://github.com/ace411/extjwt-cpp.git extjwt +cd extjwt +phpize +./configure --enable-extjwt CFLAGS="-lssl -lcrypto" make && sudo make install ``` +If you intend to run the tests in the tests directory, run the following command: + +```sh +make test +``` + + ## Usage ### Encoding a JWT From 355c1074ca9ba4fc60af0314d45f34b63a3761f2 Mon Sep 17 00:00:00 2001 From: ace411 Date: Sat, 7 Sep 2019 10:30:54 +0300 Subject: [PATCH 03/44] Added Travis config file --- .travis.yml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..362ff0f --- /dev/null +++ b/.travis.yml @@ -0,0 +1,16 @@ +language: php +php: + - 7.3 + +sudo: enabled +before_script: + - git clone https://github.com/arun11299/cpp-jwt.git + - cd cpp-jwt + - sudo mkdir build && cd build + - sudo cmake .. + - sudo make install + +script: + - phpize + - ./configure CFLAGS="-lssl -lcrypto" + - make test \ No newline at end of file From 5b5ea23a1867a3fc1ee676e7644921e2dec83d1c Mon Sep 17 00:00:00 2001 From: ace411 Date: Sat, 7 Sep 2019 10:33:45 +0300 Subject: [PATCH 04/44] Modified template --- jwt.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jwt.cpp b/jwt.cpp index 57dd5cc..dc3dfbc 100644 --- a/jwt.cpp +++ b/jwt.cpp @@ -7,7 +7,7 @@ using namespace std; typedef initializer_list strList; template -auto signJwt(B obj, L algo, const S &secret) -> string +auto signJwt(B obj, L algo, const S &secret) -> S { string token(""); From aec63030502b743d35c54562b08389758220e026 Mon Sep 17 00:00:00 2001 From: ace411 Date: Sat, 7 Sep 2019 10:41:38 +0300 Subject: [PATCH 05/44] Added dependency install directives --- .travis.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.travis.yml b/.travis.yml index 362ff0f..03bdd7c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,6 +4,12 @@ php: sudo: enabled before_script: + - sudo apt-get install cmake + - sudo apt-get install libgtest-dev + - cd /usr/src/gtest + - sudo mkdir build && cd build + - sudo cmake .. + - sudo make install - git clone https://github.com/arun11299/cpp-jwt.git - cd cpp-jwt - sudo mkdir build && cd build From 8cbdfabefd2d27d5fb47a498cc9d9e30c531e798 Mon Sep 17 00:00:00 2001 From: ace411 Date: Sat, 7 Sep 2019 12:05:40 +0300 Subject: [PATCH 06/44] Modified .travis.yml --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 03bdd7c..4aeb2d1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,8 +10,8 @@ before_script: - sudo mkdir build && cd build - sudo cmake .. - sudo make install - - git clone https://github.com/arun11299/cpp-jwt.git - - cd cpp-jwt + - git clone https://github.com/arun11299/cpp-jwt.git /usr/cpp-jwt + - cd /usr/cpp-jwt - sudo mkdir build && cd build - sudo cmake .. - sudo make install From 6bd0d34377bcdf584283b2c0ad9fc5ad0fdcd0ed Mon Sep 17 00:00:00 2001 From: ace411 Date: Sat, 7 Sep 2019 12:10:56 +0300 Subject: [PATCH 07/44] Updated .travis.yml --- .travis.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 4aeb2d1..b726f58 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,10 +6,6 @@ sudo: enabled before_script: - sudo apt-get install cmake - sudo apt-get install libgtest-dev - - cd /usr/src/gtest - - sudo mkdir build && cd build - - sudo cmake .. - - sudo make install - git clone https://github.com/arun11299/cpp-jwt.git /usr/cpp-jwt - cd /usr/cpp-jwt - sudo mkdir build && cd build From e91fef1dfbe8303ddfa7e2675c935f341fe47a3f Mon Sep 17 00:00:00 2001 From: ace411 Date: Sat, 7 Sep 2019 12:18:51 +0300 Subject: [PATCH 08/44] Modified .travis.yml --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index b726f58..049603c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,8 +6,8 @@ sudo: enabled before_script: - sudo apt-get install cmake - sudo apt-get install libgtest-dev - - git clone https://github.com/arun11299/cpp-jwt.git /usr/cpp-jwt - - cd /usr/cpp-jwt + - git clone https://github.com/arun11299/cpp-jwt.git + - cd cpp-jwt - sudo mkdir build && cd build - sudo cmake .. - sudo make install From 03b12eddb26550c65d180b7cc94d9fbaef0d3795 Mon Sep 17 00:00:00 2001 From: ace411 Date: Sun, 8 Sep 2019 11:31:41 +0300 Subject: [PATCH 09/44] Added jwt-cpp library check --- config.m4 | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/config.m4 b/config.m4 index 32fcd17..7eb1551 100644 --- a/config.m4 +++ b/config.m4 @@ -3,6 +3,13 @@ dnl Make sure that the comment is aligned: [ --enable-extjwt Enable extjwt support], no) if test "$PHP_EXTJWT" != "no"; then + JWT_CPP_PATH="/usr/include/jwt-cpp/jwt.h" + if test -s $JWT_CPP_PATH; then + AC_MSG_RESULT(jwt-cpp is installed) + else + AC_MSG_ERROR(jwt-cpp is not installed) + fi + CXXFLAGS="-std=c++11 -lssl -lcrypto" PHP_REQUIRE_CXX() PHP_SUBST(EXTJWT_SHARED_LIBADD) From 4e0609a3943d69b12f1c17a7cbc14a74f611be59 Mon Sep 17 00:00:00 2001 From: ace411 Date: Sun, 8 Sep 2019 11:34:08 +0300 Subject: [PATCH 10/44] Modified .travis.yml --- .travis.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.travis.yml b/.travis.yml index 049603c..03bdd7c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,6 +6,10 @@ sudo: enabled before_script: - sudo apt-get install cmake - sudo apt-get install libgtest-dev + - cd /usr/src/gtest + - sudo mkdir build && cd build + - sudo cmake .. + - sudo make install - git clone https://github.com/arun11299/cpp-jwt.git - cd cpp-jwt - sudo mkdir build && cd build From 525f7069a2bbd1ceb1db67936d39a41692d75db0 Mon Sep 17 00:00:00 2001 From: ace411 Date: Sun, 8 Sep 2019 11:34:29 +0300 Subject: [PATCH 11/44] Formatted header file --- php_extjwt.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/php_extjwt.h b/php_extjwt.h index b255fe4..9b9f97a 100644 --- a/php_extjwt.h +++ b/php_extjwt.h @@ -8,13 +8,13 @@ #include "config.h" #endif -extern "C" +extern "C" { - #include "php.h" - #include "zend_smart_str.h" - #include "zend_exceptions.h" - #include "ext/standard/info.h" - #include "zend_exceptions.h" +#include "php.h" +#include "zend_smart_str.h" +#include "zend_exceptions.h" +#include "ext/standard/info.h" +#include "zend_exceptions.h" } extern zend_module_entry extjwt_module_entry; From 8f457bc250f4d5d84e8c14b14e2a893d3cd8f414 Mon Sep 17 00:00:00 2001 From: ace411 Date: Fri, 13 Sep 2019 10:21:13 +0300 Subject: [PATCH 12/44] Modified jwt-cpp check routines --- config.m4 | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/config.m4 b/config.m4 index 7eb1551..44aa736 100644 --- a/config.m4 +++ b/config.m4 @@ -3,12 +3,16 @@ dnl Make sure that the comment is aligned: [ --enable-extjwt Enable extjwt support], no) if test "$PHP_EXTJWT" != "no"; then - JWT_CPP_PATH="/usr/include/jwt-cpp/jwt.h" - if test -s $JWT_CPP_PATH; then - AC_MSG_RESULT(jwt-cpp is installed) - else - AC_MSG_ERROR(jwt-cpp is not installed) - fi + JWT_CPP_INSTALL_DIR="/usr/include/jwt-cpp" + JWT_CPP_HEADERS="jwt.h picojson.h base.h" + + for iter in $JWT_CPP_HEADERS; do + if test -s $JWT_CPP_INSTALL_DIR/$iter; then + AC_MSG_RESULT($iter.h exists) + else + AC_MSG_ERROR($iter.h is missing. jwt-cpp is not properly installed) + fi + done CXXFLAGS="-std=c++11 -lssl -lcrypto" PHP_REQUIRE_CXX() From 8ee09feaad682cbd03f22ce8203cbdfb78e332be Mon Sep 17 00:00:00 2001 From: ace411 Date: Fri, 13 Sep 2019 10:22:12 +0300 Subject: [PATCH 13/44] Removed unnecessary code --- jwt.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/jwt.cpp b/jwt.cpp index dc3dfbc..4499d38 100644 --- a/jwt.cpp +++ b/jwt.cpp @@ -1,11 +1,8 @@ -#include #include #include "jwt.h" using namespace std; -typedef initializer_list strList; - template auto signJwt(B obj, L algo, const S &secret) -> S { From 66f001a201befc02a3a3d89f11ead599461fd27e Mon Sep 17 00:00:00 2001 From: ace411 Date: Sun, 15 Sep 2019 16:18:48 +0300 Subject: [PATCH 14/44] Fixed jwt-cpp check; Added lssl and lcrypto links --- README.md | 2 +- config.m4 | 85 ++++++++++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 75 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 1e379bf..80fa57b 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ Once said tools are successfully installed on your system, type the following in git clone https://github.com/ace411/extjwt-cpp.git extjwt cd extjwt phpize -./configure --enable-extjwt CFLAGS="-lssl -lcrypto" +./configure --enable-extjwt --with-jwtcpp=/path/to/jwtcpp --with-openssl=/path/to/openssl make && sudo make install ``` diff --git a/config.m4 b/config.m4 index 44aa736..75d16db 100644 --- a/config.m4 +++ b/config.m4 @@ -1,18 +1,81 @@ PHP_ARG_ENABLE(extjwt, whether to enable extjwt support, -dnl Make sure that the comment is aligned: [ --enable-extjwt Enable extjwt support], no) +PHP_ARG_WITH(jwtcpp, whether to add jwtcpp library path, +[ --with-jwtcpp[=DIR] Ignore jwtcpp library path check]) + +PHP_ARG_WITH(openssl, whether to add openssl library path, +[ --with-openssl=[=DIR] Ignore openssl library path check]) + if test "$PHP_EXTJWT" != "no"; then - JWT_CPP_INSTALL_DIR="/usr/include/jwt-cpp" - JWT_CPP_HEADERS="jwt.h picojson.h base.h" - - for iter in $JWT_CPP_HEADERS; do - if test -s $JWT_CPP_INSTALL_DIR/$iter; then - AC_MSG_RESULT($iter.h exists) - else - AC_MSG_ERROR($iter.h is missing. jwt-cpp is not properly installed) - fi - done + HEADER_INSTALL_DIRS="/usr/local /usr /usr/local/opt" + JWT_CPP_INCLUDE_DIR="include/jwt-cpp" + OPENSSL_INCLUDE_DIR="include/openssl" + JWT_CPP_HEADER="jwt.h" + OPENSSL_HEADER="ssl.h" + + dnl check if jwt-cpp extension is installed + AC_MSG_CHECKING([for jwt-cpp library]) + if test -s $PHP_JWTCPP/$JWT_CPP_HEADER; then + AC_MSG_RESULT(found $JWT_CPP_HEADER) + JWTCPP_LIB=$PHP_JWTCPP/$JWT_CPP_HEADER + else + for iter in $HEADER_INSTALL_DIRS; do + if test -s $iter/$JWT_CPP_INCLUDE_DIR/$JWT_CPP_HEADER; then + AC_MSG_RESULT(found $JWT_CPP_HEADER) + JWTCPP_LIB=$iter/$JWT_CPP_INCLUDE_DIR/$JWT_CPP_HEADER + fi + done + fi + + if test -z "$JWTCPP_LIB"; then + AC_MSG_RESULT(jwt-cpp is not properly installed) + AC_MSG_ERROR(Please install jwt-cpp) + fi + + dnl check if openssl is installed; link ssl and crypto headers + AC_MSG_CHECKING([for openssl]) + if test -s $PHP_OPENSSL/$OPENSSL_HEADER; then + AC_MSG_RESULT(found $JWT_CPP_HEADER) + OPENSSL_LIB=$PHP_OPENSSL/$OPENSSL_HEADER + OPENSSL_DIR=$PHP_OPENSSL + else + for iter in $HEADER_INSTALL_DIRS; do + if test -s $iter/$OPENSSL_INCLUDE_DIR/$OPENSSL_HEADER; then + AC_MSG_RESULT(found $OPENSSL_HEADER) + OPENSSL_LIB=$iter/$OPENSSL_INCLUDE_DIR/$OPENSSL_HEADER + OPENSSL_DIR=$iter + fi + done + fi + + if test -z "$OPENSSL_LIB"; then + AC_MSG_RESULT(openssl is not properly installed) + AC_MSG_ERROR(Please install openssl) + fi + + PHP_ADD_INCLUDE($OPENSSL_DIR/include) + AC_CHECK_HEADERS([openssl/$OPENSSL_HEADER openssl/crypto.h]) + + PHP_CHECK_LIBRARY(ssl, SSL_get_options, + [ + PHP_ADD_INCLUDE($OPENSSL_DIR/include) + PHP_ADD_LIBRARY_WITH_PATH(ssl, $OPENSSL_DIR/lib, EXTJWT_SHARED_LIBADD) + ],[ + AC_MSG_ERROR(Invalid openssl version installed) + ],[ + -L$OPENSSL_DIR/lib -lssl + ]) + + PHP_CHECK_LIBRARY(crypto, EVP_sha256, + [ + PHP_ADD_INCLUDE($OPENSSL_DIR/include) + PHP_ADD_LIBRARY_WITH_PATH(crypto, $OPENSSL_DIR/lib, EXTJWT_SHARED_LIBADD) + ],[ + AC_MSG_ERROR(Invalid openssl version installed) + ],[ + -L$OPENSSL_DIR/lib -lcrypto + ]) CXXFLAGS="-std=c++11 -lssl -lcrypto" PHP_REQUIRE_CXX() From 7ad073be1600d4344ca64a4cd6b3050656c1a8b6 Mon Sep 17 00:00:00 2001 From: ace411 Date: Sun, 15 Sep 2019 20:37:35 +0300 Subject: [PATCH 15/44] Enhanced encode-decode mechanism --- extjwt.cpp | 31 ++++++++++++++++++++++++++++--- php_extjwt.h | 1 + tests/0023.phpt | 16 ++++++++++++++++ tests/constants.php | 9 +++++++-- 4 files changed, 52 insertions(+), 5 deletions(-) create mode 100644 tests/0023.phpt diff --git a/extjwt.cpp b/extjwt.cpp index f0befa4..73885d5 100644 --- a/extjwt.cpp +++ b/extjwt.cpp @@ -60,9 +60,21 @@ PHP_FUNCTION(jwt_encode) } ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(claims), key, claim) { - convert_to_string(claim); - - jwtClaims[ZSTR_VAL(key)] = Z_STRVAL_P(claim); + if (Z_TYPE_P(claim) == IS_ARRAY) + { + smart_str jsonData = {0}; + php_json_encode(&jsonData, claim, 0); + smart_str_0(&jsonData); + + jwtClaims[ZSTR_VAL(key)] = ZSTR_VAL(jsonData.s); + smart_str_free(&jsonData); + } + else + { + convert_to_string(claim); + + jwtClaims[ZSTR_VAL(key)] = Z_STRVAL_P(claim); + } } ZEND_HASH_FOREACH_END(); auto retval = jwtEncode(std::string(ZSTR_VAL(secret)), @@ -79,6 +91,8 @@ PHP_FUNCTION(jwt_decode) zend_string *token; long algo; + zend_string *key; + zval *claim; zval *retval; ZEND_PARSE_PARAMETERS_START(0, 3) @@ -114,6 +128,17 @@ PHP_FUNCTION(jwt_decode) iter.second.c_str()); } + ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(retval), key, claim) { + if (php_json_decode_ex(claim, + Z_STRVAL_P(claim), + Z_STRLEN_P(claim), + PHP_JSON_OBJECT_AS_ARRAY, + 512) == FAILURE) + { + add_assoc_string(retval, ZSTR_VAL(key), Z_STRVAL_P(claim)); + } + } ZEND_HASH_FOREACH_END(); + RETURN_ZVAL(retval, 1, 0); } catch (const std::exception &exp) diff --git a/php_extjwt.h b/php_extjwt.h index 9b9f97a..b877373 100644 --- a/php_extjwt.h +++ b/php_extjwt.h @@ -13,6 +13,7 @@ extern "C" #include "php.h" #include "zend_smart_str.h" #include "zend_exceptions.h" +#include "ext/json/php_json.h" #include "ext/standard/info.h" #include "zend_exceptions.h" } diff --git a/tests/0023.phpt b/tests/0023.phpt new file mode 100644 index 0000000..26748b1 --- /dev/null +++ b/tests/0023.phpt @@ -0,0 +1,16 @@ +--TEST-- +jwt_encode encodes multi-dimensional array data +--FILE-- + +--EXPECT-- +string \ No newline at end of file diff --git a/tests/constants.php b/tests/constants.php index 28fbced..062ad07 100644 --- a/tests/constants.php +++ b/tests/constants.php @@ -1,8 +1,13 @@ '@ace411', 'twitter' => '@agiroLoki' ]; + +const TOKEN_CLAIMS_MULTIDIM = [ + 'social' => TOKEN_CLAIMS, + 'location' => 'kampala' +]; \ No newline at end of file From faf129b20e4b80be777990251e8e1af5e1075080 Mon Sep 17 00:00:00 2001 From: ace411 Date: Sun, 15 Sep 2019 20:51:19 +0300 Subject: [PATCH 16/44] Fixed errors in README.md --- README.md | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 80fa57b..6c94136 100644 --- a/README.md +++ b/README.md @@ -17,9 +17,7 @@ A simple PHP extension for encoding and decoding JSON Web Tokens (JWTs). ## Installation -Because ext-func is an extension built on top of PHP-CPP, installation of PHP-CPP is a mandatory prerequisite for using this tool. Also requirements are OpenSSL, and jwt-cpp. - -Once said tools are successfully installed on your system, type the following in a console to install extjwt-cpp on your machine. +extjwt-cpp requires tools specified in the [precursory text](#requirements). Once said tools are successfully installed on your system, type the following in a console to install extjwt-cpp on your machine. ```sh git clone https://github.com/ace411/extjwt-cpp.git extjwt @@ -44,9 +42,13 @@ Creating a cryptographically signed token from arbitrary claims. ```php const CLAIMS = [ - 'iss' => 'https://github.com/ace411/extjwt-cpp', - 'aud' => 'https://github.com/ace411/extjwt-cpp', - 'user' => 'ace411' + 'iss' => 'https://github.com/ace411/extjwt-cpp', + 'aud' => 'https://github.com/ace411/extjwt-cpp', + 'user' => [ + 'github' => '@ace411', + 'twitter' => '@agiroLoki' + ], + 'cpp_ver' => 11 ]; $token = jwt_encode('@loki', JWT_ALGO_HS512, CLAIMS); From 003a18350d7043122129a6d0a09a2241a6a60cb3 Mon Sep 17 00:00:00 2001 From: ace411 Date: Mon, 16 Sep 2019 12:19:57 +0300 Subject: [PATCH 17/44] Added additional support for non-string values --- extjwt.cpp | 36 ++++++++++++++++++++++++------------ tests/0033.phpt | 12 ++++++++++++ tests/0034.phpt | 23 +++++++++++++++++++++++ 3 files changed, 59 insertions(+), 12 deletions(-) create mode 100644 tests/0033.phpt create mode 100644 tests/0034.phpt diff --git a/extjwt.cpp b/extjwt.cpp index 73885d5..ce2293a 100644 --- a/extjwt.cpp +++ b/extjwt.cpp @@ -17,7 +17,7 @@ auto algoIsValid(L algo) -> bool case JWT_ALGO_HS512: return true; break; - + default: return false; break; @@ -40,7 +40,7 @@ PHP_FUNCTION(jwt_encode) Z_PARAM_ARRAY(claims) ZEND_PARSE_PARAMETERS_END(); - if (zend_hash_num_elements(HASH_OF(claims)) == 0 || + if (zend_hash_num_elements(HASH_OF(claims)) == 0 || ZSTR_LEN(secret) == 0) { zend_string_release(secret); @@ -59,7 +59,8 @@ PHP_FUNCTION(jwt_encode) RETURN_NULL(); } - ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(claims), key, claim) { + ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(claims), key, claim) + { if (Z_TYPE_P(claim) == IS_ARRAY) { smart_str jsonData = {0}; @@ -75,11 +76,12 @@ PHP_FUNCTION(jwt_encode) jwtClaims[ZSTR_VAL(key)] = Z_STRVAL_P(claim); } - } ZEND_HASH_FOREACH_END(); + } + ZEND_HASH_FOREACH_END(); auto retval = jwtEncode(std::string(ZSTR_VAL(secret)), - algo, - jwtClaims); + algo, + jwtClaims); RETURN_STRING(retval.c_str()); zend_string_release(key); zend_string_release(secret); @@ -128,16 +130,26 @@ PHP_FUNCTION(jwt_decode) iter.second.c_str()); } - ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(retval), key, claim) { + ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(retval), key, claim) + { if (php_json_decode_ex(claim, Z_STRVAL_P(claim), Z_STRLEN_P(claim), PHP_JSON_OBJECT_AS_ARRAY, 512) == FAILURE) { - add_assoc_string(retval, ZSTR_VAL(key), Z_STRVAL_P(claim)); + Z_STRVAL_P(claim) == std::string("1").c_str() ? add_assoc_bool(retval, + ZSTR_VAL(key), + 1) + : Z_STRLEN_P(claim) == 0 ? add_assoc_bool(retval, + ZSTR_VAL(key), + 0) + : add_assoc_string(retval, + ZSTR_VAL(key), + Z_STRVAL_P(claim)); } - } ZEND_HASH_FOREACH_END(); + } + ZEND_HASH_FOREACH_END(); RETURN_ZVAL(retval, 1, 0); } @@ -164,8 +176,8 @@ PHP_RINIT_FUNCTION(extjwt) PHP_MINFO_FUNCTION(extjwt) { php_info_print_table_start(); - php_info_print_table_header(2, "extjwt support", "enabled"); - php_info_print_table_end(); + php_info_print_table_header(2, "extjwt support", "enabled"); + php_info_print_table_end(); } PHP_MINIT_FUNCTION(extjwt) @@ -186,7 +198,7 @@ PHP_MINIT_FUNCTION(extjwt) REGISTER_LONG_CONSTANT("JWT_ALGO_HS384", JWT_ALGO_HS384, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("JWT_ALGO_HS512", JWT_ALGO_HS512, CONST_CS | CONST_PERSISTENT); - return SUCCESS; + return SUCCESS; } ZEND_BEGIN_ARG_INFO_EX(arginfo_jwt_encode, 0, 0, 3) diff --git a/tests/0033.phpt b/tests/0033.phpt new file mode 100644 index 0000000..ab01083 --- /dev/null +++ b/tests/0033.phpt @@ -0,0 +1,12 @@ +--TEST-- +jwt_decode decodes multi-dimensional array data +--FILE-- + +--EXPECT-- +array \ No newline at end of file diff --git a/tests/0034.phpt b/tests/0034.phpt new file mode 100644 index 0000000..58ad51a --- /dev/null +++ b/tests/0034.phpt @@ -0,0 +1,23 @@ +--TEST-- +jwt_decode decodes array with non-string values intact +--FILE-- + false, + 'cpp_ver' => 11, + 'pi' => 3.141 +]); + +var_dump(jwt_decode($jwt, TOKEN_SECRET, JWT_ALGO_HS256)); +?> +--EXPECT-- +array(3) { + ["cpp_ver"]=> + int(11) + ["hasInsta"]=> + bool(false) + ["pi"]=> + float(3.141) +} \ No newline at end of file From a76a3c82c56a483e43363a90eaef712f57bb36a2 Mon Sep 17 00:00:00 2001 From: ace411 Date: Fri, 20 Sep 2019 16:31:59 +0300 Subject: [PATCH 18/44] Aadded HS384 decode option --- jwt.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/jwt.cpp b/jwt.cpp index 4499d38..de1fd50 100644 --- a/jwt.cpp +++ b/jwt.cpp @@ -62,6 +62,10 @@ auto jwtDecode(const S &token, L algo, const S &secret) -> strmap verifier.allow_algorithm(jwt::algorithm::hs256{secret}); break; + case JWT_ALGO_HS384: + verifier.allow_algorithm(jwt::algorithm::hs384{secret}); + break; + case JWT_ALGO_HS512: verifier.allow_algorithm(jwt::algorithm::hs512{secret}); break; From 625bfed901ec0e3d4b7b6c11d8786c72ccc8a26a Mon Sep 17 00:00:00 2001 From: ace411 Date: Sun, 22 Sep 2019 13:25:16 +0300 Subject: [PATCH 19/44] Formatted code and added table info --- extjwt.cpp | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/extjwt.cpp b/extjwt.cpp index ce2293a..f426065 100644 --- a/extjwt.cpp +++ b/extjwt.cpp @@ -5,6 +5,8 @@ #include "ext/spl/spl_exceptions.h" #endif +using namespace std; + zend_class_entry *jwt_exception_ce; template @@ -79,7 +81,7 @@ PHP_FUNCTION(jwt_encode) } ZEND_HASH_FOREACH_END(); - auto retval = jwtEncode(std::string(ZSTR_VAL(secret)), + auto retval = jwtEncode(string(ZSTR_VAL(secret)), algo, jwtClaims); RETURN_STRING(retval.c_str()); @@ -115,9 +117,9 @@ PHP_FUNCTION(jwt_decode) try { - auto claims = jwtDecode(std::string(ZSTR_VAL(token)), + auto claims = jwtDecode(string(ZSTR_VAL(token)), algo, - std::string(ZSTR_VAL(secret))); + string(ZSTR_VAL(secret))); array_init(retval); zend_string_release(secret); @@ -138,7 +140,7 @@ PHP_FUNCTION(jwt_decode) PHP_JSON_OBJECT_AS_ARRAY, 512) == FAILURE) { - Z_STRVAL_P(claim) == std::string("1").c_str() ? add_assoc_bool(retval, + Z_STRVAL_P(claim) == string("1").c_str() ? add_assoc_bool(retval, ZSTR_VAL(key), 1) : Z_STRLEN_P(claim) == 0 ? add_assoc_bool(retval, @@ -153,7 +155,7 @@ PHP_FUNCTION(jwt_decode) RETURN_ZVAL(retval, 1, 0); } - catch (const std::exception &exp) + catch (const exception &exp) { zend_string_release(secret); zend_string_release(token); @@ -177,6 +179,8 @@ PHP_MINFO_FUNCTION(extjwt) { php_info_print_table_start(); php_info_print_table_header(2, "extjwt support", "enabled"); + php_info_print_table_header(2, "extjwt version", PHP_EXTJWT_EXTVER); + php_info_print_table_header(2, "supported algorithms", "HS256, HS384, HS512"); php_info_print_table_end(); } From 937bfd0f94b8231edc724f94356ff46f7d9f38f3 Mon Sep 17 00:00:00 2001 From: ace411 Date: Sun, 22 Sep 2019 13:46:52 +0300 Subject: [PATCH 20/44] Removed scope resolution on jwt calls --- jwt.cpp | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/jwt.cpp b/jwt.cpp index de1fd50..5c14143 100644 --- a/jwt.cpp +++ b/jwt.cpp @@ -2,6 +2,7 @@ #include "jwt.h" using namespace std; +using namespace jwt; template auto signJwt(B obj, L algo, const S &secret) -> S @@ -11,18 +12,15 @@ auto signJwt(B obj, L algo, const S &secret) -> S switch (algo) { case JWT_ALGO_HS256: - token += obj.sign(jwt::algorithm::hs256{secret}); + token += obj.sign(algorithm::hs256{secret}); break; case JWT_ALGO_HS384: - token += obj.sign(jwt::algorithm::hs384{secret}); + token += obj.sign(algorithm::hs384{secret}); break; case JWT_ALGO_HS512: - token += obj.sign(jwt::algorithm::hs512{secret}); - break; - - default: + token += obj.sign(algorithm::hs512{secret}); break; } @@ -32,7 +30,7 @@ auto signJwt(B obj, L algo, const S &secret) -> S template auto jwtEncode(const S &secret, L algo, I claims) -> S { - auto tokenObj = jwt::create() + auto tokenObj = create() .set_type("JWS"); for (auto &iter : claims) @@ -45,7 +43,7 @@ auto jwtEncode(const S &secret, L algo, I claims) -> S tokenObj.set_issuer(claims["iss"]); } - return signJwt(tokenObj, + return signJwt(tokenObj, algo, secret); } @@ -53,27 +51,29 @@ auto jwtEncode(const S &secret, L algo, I claims) -> S template auto jwtDecode(const S &token, L algo, const S &secret) -> strmap { - auto decoded = jwt::decode(token), verifier = jwt::verify(); + auto decoded = decode(token), verifier = verify(); strmap claims; switch (algo) { case JWT_ALGO_HS256: - verifier.allow_algorithm(jwt::algorithm::hs256{secret}); + verifier.allow_algorithm(algorithm::hs256{secret}); break; case JWT_ALGO_HS384: - verifier.allow_algorithm(jwt::algorithm::hs384{secret}); + verifier.allow_algorithm(algorithm::hs384{secret}); break; case JWT_ALGO_HS512: - verifier.allow_algorithm(jwt::algorithm::hs512{secret}); + verifier.allow_algorithm(algorithm::hs512{secret}); break; default: break; } + verifier.verify(decoded); + for (auto &iter : decoded.get_payload_claims()) { claims[iter.first] = iter.second.to_json().to_str(); From b0a6756c0b22f55340a0794273d56d4fe6991243 Mon Sep 17 00:00:00 2001 From: ace411 Date: Sun, 22 Sep 2019 13:47:30 +0300 Subject: [PATCH 21/44] Removed redundant code --- jwt.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/jwt.cpp b/jwt.cpp index 5c14143..d0be28e 100644 --- a/jwt.cpp +++ b/jwt.cpp @@ -38,11 +38,6 @@ auto jwtEncode(const S &secret, L algo, I claims) -> S tokenObj.set_payload_claim(iter.first, iter.second); } - if (claims.count("iss")) - { - tokenObj.set_issuer(claims["iss"]); - } - return signJwt(tokenObj, algo, secret); From a84608e03ca55d2eacc6a246b76f311af5efa87e Mon Sep 17 00:00:00 2001 From: ace411 Date: Mon, 23 Sep 2019 01:06:29 +0300 Subject: [PATCH 22/44] Added addClaims and JWT_CLAIM_REPRESENT --- extjwt.cpp | 14 +++++--------- jwt.cpp | 48 +++++++++++++++++++++++++++++++++++++----------- jwt.h | 6 ++++++ 3 files changed, 48 insertions(+), 20 deletions(-) diff --git a/extjwt.cpp b/extjwt.cpp index f426065..ce2293a 100644 --- a/extjwt.cpp +++ b/extjwt.cpp @@ -5,8 +5,6 @@ #include "ext/spl/spl_exceptions.h" #endif -using namespace std; - zend_class_entry *jwt_exception_ce; template @@ -81,7 +79,7 @@ PHP_FUNCTION(jwt_encode) } ZEND_HASH_FOREACH_END(); - auto retval = jwtEncode(string(ZSTR_VAL(secret)), + auto retval = jwtEncode(std::string(ZSTR_VAL(secret)), algo, jwtClaims); RETURN_STRING(retval.c_str()); @@ -117,9 +115,9 @@ PHP_FUNCTION(jwt_decode) try { - auto claims = jwtDecode(string(ZSTR_VAL(token)), + auto claims = jwtDecode(std::string(ZSTR_VAL(token)), algo, - string(ZSTR_VAL(secret))); + std::string(ZSTR_VAL(secret))); array_init(retval); zend_string_release(secret); @@ -140,7 +138,7 @@ PHP_FUNCTION(jwt_decode) PHP_JSON_OBJECT_AS_ARRAY, 512) == FAILURE) { - Z_STRVAL_P(claim) == string("1").c_str() ? add_assoc_bool(retval, + Z_STRVAL_P(claim) == std::string("1").c_str() ? add_assoc_bool(retval, ZSTR_VAL(key), 1) : Z_STRLEN_P(claim) == 0 ? add_assoc_bool(retval, @@ -155,7 +153,7 @@ PHP_FUNCTION(jwt_decode) RETURN_ZVAL(retval, 1, 0); } - catch (const exception &exp) + catch (const std::exception &exp) { zend_string_release(secret); zend_string_release(token); @@ -179,8 +177,6 @@ PHP_MINFO_FUNCTION(extjwt) { php_info_print_table_start(); php_info_print_table_header(2, "extjwt support", "enabled"); - php_info_print_table_header(2, "extjwt version", PHP_EXTJWT_EXTVER); - php_info_print_table_header(2, "supported algorithms", "HS256, HS384, HS512"); php_info_print_table_end(); } diff --git a/jwt.cpp b/jwt.cpp index d0be28e..b218433 100644 --- a/jwt.cpp +++ b/jwt.cpp @@ -3,6 +3,7 @@ using namespace std; using namespace jwt; +using timepoint = chrono::time_point; template auto signJwt(B obj, L algo, const S &secret) -> S @@ -27,20 +28,48 @@ auto signJwt(B obj, L algo, const S &secret) -> S return token; } -template -auto jwtEncode(const S &secret, L algo, I claims) -> S +template +auto addClaims(B obj, S claims) -> B { - auto tokenObj = create() - .set_type("JWS"); + auto strToTime = [](const string &timeval) { + const chrono::system_clock::duration timeT = chrono::seconds{stoi(timeval)}; + const timepoint duration(timeT); + return duration; }; - for (auto &iter : claims) + for (auto &claim : claims) { - tokenObj.set_payload_claim(iter.first, iter.second); + switch (JWT_CLAIM_REPRESENT[claim.first]) + { + case 1: + obj.set_issued_at(strToTime(claim.second)); + break; + + case 2: + obj.set_not_before(strToTime(claim.second)); + break; + + case 3: + obj.set_expires_at(strToTime(claim.second)); + break; + + default: + obj.set_payload_claim(claim.first, claim.second); + break; + } } + return obj; +} + +template +auto jwtEncode(const S &secret, L algo, I claims) -> S +{ + auto tokenObj = addClaims(create().set_type("JWS"), + claims); + return signJwt(tokenObj, - algo, - secret); + algo, + secret); } template @@ -62,9 +91,6 @@ auto jwtDecode(const S &token, L algo, const S &secret) -> strmap case JWT_ALGO_HS512: verifier.allow_algorithm(algorithm::hs512{secret}); break; - - default: - break; } verifier.verify(decoded); diff --git a/jwt.h b/jwt.h index 7b7640e..d2d51fc 100644 --- a/jwt.h +++ b/jwt.h @@ -3,12 +3,18 @@ #include #include +#include using namespace std; #define JWT_ALGO_HS256 1 #define JWT_ALGO_HS384 2 #define JWT_ALGO_HS512 3 +#define JWT_CLAIM_REPRESENT \ + map \ + { \ + {"iat", 1}, {"nbf", 2}, { "exp", 3 } \ + } typedef map strmap; From dec042ff1ac7ed054dba04395251fe9d0d5fd9bc Mon Sep 17 00:00:00 2001 From: ace411 Date: Mon, 23 Sep 2019 01:12:50 +0300 Subject: [PATCH 23/44] Removed redundant directive --- php_extjwt.h | 1 - 1 file changed, 1 deletion(-) diff --git a/php_extjwt.h b/php_extjwt.h index b877373..4e35b6d 100644 --- a/php_extjwt.h +++ b/php_extjwt.h @@ -15,7 +15,6 @@ extern "C" #include "zend_exceptions.h" #include "ext/json/php_json.h" #include "ext/standard/info.h" -#include "zend_exceptions.h" } extern zend_module_entry extjwt_module_entry; From 64bfcc9edcb5a5cfde3ec9438f3ce8c5df5fb69e Mon Sep 17 00:00:00 2001 From: ace411 Date: Mon, 23 Sep 2019 01:14:17 +0300 Subject: [PATCH 24/44] Added token expiry test --- tests/005.phpt | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 tests/005.phpt diff --git a/tests/005.phpt b/tests/005.phpt new file mode 100644 index 0000000..7eafb98 --- /dev/null +++ b/tests/005.phpt @@ -0,0 +1,19 @@ +--TEST-- +jwt_decode throws ExtJwtException when token expires +--FILE-- + 1568703793, + 'exp' => 1568703798 + ]); + sleep(10); + var_dump(jwt_decode($token, TOKEN_SECRET, JWT_ALGO_HS256)); +} catch (ExtJwtException $exp) { + echo $exp->getMessage(); +} +?> +--EXPECT-- +token verification failed: token expired \ No newline at end of file From ca0bd3322651cf48a977d99bc21b1c1a9a7aafe8 Mon Sep 17 00:00:00 2001 From: ace411 Date: Mon, 23 Sep 2019 01:27:03 +0300 Subject: [PATCH 25/44] Added more extension table information --- extjwt.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/extjwt.cpp b/extjwt.cpp index ce2293a..3bbcdfb 100644 --- a/extjwt.cpp +++ b/extjwt.cpp @@ -177,6 +177,8 @@ PHP_MINFO_FUNCTION(extjwt) { php_info_print_table_start(); php_info_print_table_header(2, "extjwt support", "enabled"); + php_info_print_table_header(2, "extjwt version", PHP_EXTJWT_EXTVER); + php_info_print_table_header(2, "supported algorithms", "HS256, HS384, HS512"); php_info_print_table_end(); } From 26484b79e82039142d5e563e9c4a799a91cb68b4 Mon Sep 17 00:00:00 2001 From: ace411 Date: Sat, 28 Sep 2019 01:59:43 +0300 Subject: [PATCH 26/44] Modified .travis.yml --- .travis.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 03bdd7c..b6db5ab 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,12 +7,14 @@ before_script: - sudo apt-get install cmake - sudo apt-get install libgtest-dev - cd /usr/src/gtest - - sudo mkdir build && cd build + - sudo mkdir _build + - cd _build - sudo cmake .. - sudo make install - git clone https://github.com/arun11299/cpp-jwt.git - cd cpp-jwt - - sudo mkdir build && cd build + - sudo mkdir _build + - cd _build - sudo cmake .. - sudo make install From f8b0bd350dcebb6885abd6c21ba693a0f06a8775 Mon Sep 17 00:00:00 2001 From: ace411 Date: Sat, 28 Sep 2019 03:01:02 +0300 Subject: [PATCH 27/44] Modified .travis.yml --- .travis.yml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 03bdd7c..7a6db1b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,12 +7,15 @@ before_script: - sudo apt-get install cmake - sudo apt-get install libgtest-dev - cd /usr/src/gtest - - sudo mkdir build && cd build + - sudo mkdir _build + - cd _build - sudo cmake .. - - sudo make install + - sudo make + - sudo cp libgtest* /usr/lib/ - git clone https://github.com/arun11299/cpp-jwt.git - cd cpp-jwt - - sudo mkdir build && cd build + - sudo mkdir _build + - cd _build - sudo cmake .. - sudo make install From 4770e7b5f84f6fc5d9a7419184237b33484e505b Mon Sep 17 00:00:00 2001 From: ace411 Date: Sat, 28 Sep 2019 03:53:09 +0300 Subject: [PATCH 28/44] Modified .travis.yml --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 7a6db1b..b69e3e6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,7 +12,7 @@ before_script: - sudo cmake .. - sudo make - sudo cp libgtest* /usr/lib/ - - git clone https://github.com/arun11299/cpp-jwt.git + - sudo git clone https://github.com/arun11299/cpp-jwt.git - cd cpp-jwt - sudo mkdir _build - cd _build @@ -21,5 +21,5 @@ before_script: script: - phpize - - ./configure CFLAGS="-lssl -lcrypto" + - ./configure --enable-extjwt - make test \ No newline at end of file From a113e747fe2753342afb4f1f33dcb853beae56ab Mon Sep 17 00:00:00 2001 From: ace411 Date: Sat, 28 Sep 2019 04:35:13 +0300 Subject: [PATCH 29/44] Modified .travis.yml --- .travis.yml | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index b69e3e6..39fff03 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,14 +12,11 @@ before_script: - sudo cmake .. - sudo make - sudo cp libgtest* /usr/lib/ - - sudo git clone https://github.com/arun11299/cpp-jwt.git - - cd cpp-jwt - - sudo mkdir _build - - cd _build - - sudo cmake .. - - sudo make install + - cd /usr/src + - sudo mkdir jwtcpp + - sudo git clone https://github.com/Thalhammer/jwt-cpp.git jwtcpp script: - phpize - - ./configure --enable-extjwt + - ./configure --enable-extjwt --with-jwtcpp=/usr/src/jwtcpp/include/jwt-cpp - make test \ No newline at end of file From 52d33939c2fa5664740f93434bb33d08ecfa2fb6 Mon Sep 17 00:00:00 2001 From: ace411 Date: Sat, 28 Sep 2019 08:12:38 +0300 Subject: [PATCH 30/44] Modified .travis.yml --- .travis.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 39fff03..dd6639f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,8 +3,12 @@ php: - 7.3 sudo: enabled -before_script: - - sudo apt-get install cmake +addons: + apt: + packages: + - cmake + +before_install: - sudo apt-get install libgtest-dev - cd /usr/src/gtest - sudo mkdir _build From 24b43c570d36c85954fb160bd85711cf1ffdc82e Mon Sep 17 00:00:00 2001 From: ace411 Date: Sat, 28 Sep 2019 08:23:36 +0300 Subject: [PATCH 31/44] Modified .travis.yml --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index dd6639f..17aab47 100644 --- a/.travis.yml +++ b/.travis.yml @@ -21,6 +21,7 @@ before_install: - sudo git clone https://github.com/Thalhammer/jwt-cpp.git jwtcpp script: + - cd $TRAVIS_BUILD_DIR - phpize - ./configure --enable-extjwt --with-jwtcpp=/usr/src/jwtcpp/include/jwt-cpp - make test \ No newline at end of file From 479ab8795478fc476f082f47117f603a6c04f9ae Mon Sep 17 00:00:00 2001 From: ace411 Date: Sat, 28 Sep 2019 08:41:17 +0300 Subject: [PATCH 32/44] Modified .travis.yml --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 17aab47..652be8f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,6 +9,7 @@ addons: - cmake before_install: + - sudo apt-get install libssl-dev - sudo apt-get install libgtest-dev - cd /usr/src/gtest - sudo mkdir _build From 05dbbd8a9cb3a300289886614ac9323f9ebba5da Mon Sep 17 00:00:00 2001 From: ace411 Date: Sat, 28 Sep 2019 09:36:44 +0300 Subject: [PATCH 33/44] Modified config.m4 --- config.m4 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.m4 b/config.m4 index 75d16db..d66c075 100644 --- a/config.m4 +++ b/config.m4 @@ -57,7 +57,7 @@ if test "$PHP_EXTJWT" != "no"; then PHP_ADD_INCLUDE($OPENSSL_DIR/include) AC_CHECK_HEADERS([openssl/$OPENSSL_HEADER openssl/crypto.h]) - PHP_CHECK_LIBRARY(ssl, SSL_get_options, + PHP_CHECK_LIBRARY(ssl, SSL_CTX_set_msg_callback, [ PHP_ADD_INCLUDE($OPENSSL_DIR/include) PHP_ADD_LIBRARY_WITH_PATH(ssl, $OPENSSL_DIR/lib, EXTJWT_SHARED_LIBADD) From 20bc6c79ec9ed5f717f4ad596c9cf5c019e7e581 Mon Sep 17 00:00:00 2001 From: ace411 Date: Sat, 28 Sep 2019 09:51:14 +0300 Subject: [PATCH 34/44] Modified .travis.yml --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 652be8f..f461c80 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,8 +18,8 @@ before_install: - sudo make - sudo cp libgtest* /usr/lib/ - cd /usr/src - - sudo mkdir jwtcpp - - sudo git clone https://github.com/Thalhammer/jwt-cpp.git jwtcpp + - sudo git clone https://github.com/Thalhammer/jwt-cpp.git + - sudo cp -r jwt-cpp/include/jwt-cpp $TRAVIS_BUILD_DIR/jwt-cpp script: - cd $TRAVIS_BUILD_DIR From d7d43a06b18ba4e9f38ecf2b8c915733438e3a24 Mon Sep 17 00:00:00 2001 From: ace411 Date: Sat, 28 Sep 2019 10:41:17 +0300 Subject: [PATCH 35/44] Modified .travis.yml --- .travis.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index f461c80..fae3625 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,8 +18,9 @@ before_install: - sudo make - sudo cp libgtest* /usr/lib/ - cd /usr/src - - sudo git clone https://github.com/Thalhammer/jwt-cpp.git - - sudo cp -r jwt-cpp/include/jwt-cpp $TRAVIS_BUILD_DIR/jwt-cpp + - sudo git clone https://github.com/Thalhammer/jwt-cpp.git jwt-cpp + - cd jwt-cpp + - sudo make install script: - cd $TRAVIS_BUILD_DIR From b29577919d7a3f5d5468a5fbffad0cd2570d9a8f Mon Sep 17 00:00:00 2001 From: ace411 Date: Sat, 28 Sep 2019 11:27:25 +0300 Subject: [PATCH 36/44] Modified .travis.yml --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index fae3625..6ca7ccd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,7 +20,7 @@ before_install: - cd /usr/src - sudo git clone https://github.com/Thalhammer/jwt-cpp.git jwt-cpp - cd jwt-cpp - - sudo make install + - make && sudo make install script: - cd $TRAVIS_BUILD_DIR From 64826e9ba9d18674ca5da58e54ce500226a0004f Mon Sep 17 00:00:00 2001 From: ace411 Date: Sat, 28 Sep 2019 11:32:15 +0300 Subject: [PATCH 37/44] Modified jwt.cpp --- jwt.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/jwt.cpp b/jwt.cpp index b218433..63b10d0 100644 --- a/jwt.cpp +++ b/jwt.cpp @@ -75,7 +75,8 @@ auto jwtEncode(const S &secret, L algo, I claims) -> S template auto jwtDecode(const S &token, L algo, const S &secret) -> strmap { - auto decoded = decode(token), verifier = verify(); + auto decoded = decode(token); + auto verifier = verify(); strmap claims; switch (algo) From 7fb2da72a22fa2a756c33a908188b9d0976c70e5 Mon Sep 17 00:00:00 2001 From: ace411 Date: Sat, 28 Sep 2019 11:35:45 +0300 Subject: [PATCH 38/44] Modified .travis.yml --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 6ca7ccd..f136a51 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,7 +20,7 @@ before_install: - cd /usr/src - sudo git clone https://github.com/Thalhammer/jwt-cpp.git jwt-cpp - cd jwt-cpp - - make && sudo make install + - sudo make && sudo make install script: - cd $TRAVIS_BUILD_DIR From 2701a92d013e9a2726c56a418aeacc9a1eb9e235 Mon Sep 17 00:00:00 2001 From: ace411 Date: Wed, 6 Nov 2019 18:53:14 +0300 Subject: [PATCH 39/44] Removed .travis.yml --- .travis.yml | 29 ----------------------------- 1 file changed, 29 deletions(-) delete mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index f136a51..0000000 --- a/.travis.yml +++ /dev/null @@ -1,29 +0,0 @@ -language: php -php: - - 7.3 - -sudo: enabled -addons: - apt: - packages: - - cmake - -before_install: - - sudo apt-get install libssl-dev - - sudo apt-get install libgtest-dev - - cd /usr/src/gtest - - sudo mkdir _build - - cd _build - - sudo cmake .. - - sudo make - - sudo cp libgtest* /usr/lib/ - - cd /usr/src - - sudo git clone https://github.com/Thalhammer/jwt-cpp.git jwt-cpp - - cd jwt-cpp - - sudo make && sudo make install - -script: - - cd $TRAVIS_BUILD_DIR - - phpize - - ./configure --enable-extjwt --with-jwtcpp=/usr/src/jwtcpp/include/jwt-cpp - - make test \ No newline at end of file From af1065914a0a33a4563eb2266115939fdd0dc501 Mon Sep 17 00:00:00 2001 From: ace411 Date: Wed, 6 Nov 2019 18:54:24 +0300 Subject: [PATCH 40/44] Added requirements info --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 6c94136..08d6c03 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,8 @@ A simple PHP extension for encoding and decoding JSON Web Tokens (JWTs). - [jwt-cpp](https://github.com/Thalhammer/jwt-cpp) package - PHP 7 or greater +**Note:** Only POSIX Operating Systems are currently supported. + ## Supported Algorithms - HS256 From c812a11e73ef335d8e298521ddb171638fd1e54f Mon Sep 17 00:00:00 2001 From: ace411 Date: Sun, 8 Mar 2020 12:45:27 +0300 Subject: [PATCH 41/44] Updated LICENSE --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index 261eeb9..3d80d6f 100644 --- a/LICENSE +++ b/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright [yyyy] [name of copyright owner] + Copyright 2019 Lochemem Bruno Michael Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. From ae70d401aeacb911d91bf2c1aeeb69bda3dfbb00 Mon Sep 17 00:00:00 2001 From: ace411 Date: Mon, 9 Mar 2020 14:33:00 +0300 Subject: [PATCH 42/44] Replaced Apache-2.0 with PHP License 3.01 --- LICENSE | 269 ++++++++++++++------------------------------------------ 1 file changed, 68 insertions(+), 201 deletions(-) diff --git a/LICENSE b/LICENSE index 3d80d6f..dedba12 100644 --- a/LICENSE +++ b/LICENSE @@ -1,201 +1,68 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright 2019 Lochemem Bruno Michael - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. +-------------------------------------------------------------------- + The PHP License, version 3.01 +Copyright (c) 1999 - 2019 The PHP Group. All rights reserved. +-------------------------------------------------------------------- + +Redistribution and use in source and binary forms, with or without +modification, is permitted provided that the following conditions +are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + 3. The name "PHP" must not be used to endorse or promote products + derived from this software without prior written permission. For + written permission, please contact group@php.net. + + 4. Products derived from this software may not be called "PHP", nor + may "PHP" appear in their name, without prior written permission + from group@php.net. You may indicate that your software works in + conjunction with PHP by saying "Foo for PHP" instead of calling + it "PHP Foo" or "phpfoo" + + 5. The PHP Group may publish revised and/or new versions of the + license from time to time. Each version will be given a + distinguishing version number. + Once covered code has been published under a particular version + of the license, you may always continue to use it under the terms + of that version. You may also choose to use such covered code + under the terms of any subsequent version of the license + published by the PHP Group. No one other than the PHP Group has + the right to modify the terms applicable to covered code created + under this License. + + 6. Redistributions of any form whatsoever must retain the following + acknowledgment: + "This product includes PHP software, freely available from + ". + +THIS SOFTWARE IS PROVIDED BY THE PHP DEVELOPMENT TEAM ``AS IS'' AND +ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PHP +DEVELOPMENT TEAM OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +OF THE POSSIBILITY OF SUCH DAMAGE. + +-------------------------------------------------------------------- + +This software consists of voluntary contributions made by many +individuals on behalf of the PHP Group. + +The PHP Group can be contacted via Email at group@php.net. + +For more information on the PHP Group and the PHP project, +please see . + +PHP includes the Zend Engine, freely available at +. \ No newline at end of file From 18e618239a55117810ac6581df27c764636f7325 Mon Sep 17 00:00:00 2001 From: ace411 Date: Mon, 9 Mar 2020 17:32:18 +0300 Subject: [PATCH 43/44] Added new algorithms --- README.md | 134 +++++++++++++++++++--- extjwt.cpp | 106 +++++++++++++---- jwt.cpp | 100 +++++++++++++++- jwt.h | 16 ++- php_extjwt.h | 8 +- tests/002.phpt | 10 -- tests/0021.phpt | 14 --- tests/0022.phpt | 14 --- tests/0023.phpt | 16 --- tests/003.phpt | 13 --- tests/0031.phpt | 16 --- tests/0032.phpt | 12 -- tests/0033.phpt | 12 -- tests/0034.phpt | 23 ---- tests/004.phpt | 14 --- tests/005.phpt | 19 --- tests/{001.phpt => check.phpt} | 0 tests/constants.php | 60 +++++++++- tests/decode/decode_hs.phpt | 29 +++++ tests/decode/decode_multidimensional.phpt | 21 ++++ tests/decode/decode_ps.phpt | 29 +++++ tests/decode/decode_rs.phpt | 29 +++++ tests/encode/encode_hs.phpt | 23 ++++ tests/encode/encode_multidimensional.phpt | 12 ++ tests/encode/encode_ps.phpt | 24 ++++ tests/encode/encode_rs.phpt | 24 ++++ tests/exceptions/algo_decode.phpt | 16 +++ tests/exceptions/algo_encode.phpt | 14 +++ tests/exceptions/algo_mismatch.phpt | 16 +++ tests/exceptions/empty_decode.phpt | 12 ++ tests/exceptions/empty_encode.phpt | 12 ++ tests/exceptions/expiry.phpt | 21 ++++ tests/functions.php | 19 +++ tests/simple_decode.phpt | 17 +++ tests/simple_encode.phpt | 10 ++ 35 files changed, 711 insertions(+), 204 deletions(-) delete mode 100644 tests/002.phpt delete mode 100644 tests/0021.phpt delete mode 100644 tests/0022.phpt delete mode 100644 tests/0023.phpt delete mode 100644 tests/003.phpt delete mode 100644 tests/0031.phpt delete mode 100644 tests/0032.phpt delete mode 100644 tests/0033.phpt delete mode 100644 tests/0034.phpt delete mode 100644 tests/004.phpt delete mode 100644 tests/005.phpt rename tests/{001.phpt => check.phpt} (100%) create mode 100644 tests/decode/decode_hs.phpt create mode 100644 tests/decode/decode_multidimensional.phpt create mode 100644 tests/decode/decode_ps.phpt create mode 100644 tests/decode/decode_rs.phpt create mode 100644 tests/encode/encode_hs.phpt create mode 100644 tests/encode/encode_multidimensional.phpt create mode 100644 tests/encode/encode_ps.phpt create mode 100644 tests/encode/encode_rs.phpt create mode 100644 tests/exceptions/algo_decode.phpt create mode 100644 tests/exceptions/algo_encode.phpt create mode 100644 tests/exceptions/algo_mismatch.phpt create mode 100644 tests/exceptions/empty_decode.phpt create mode 100644 tests/exceptions/empty_encode.phpt create mode 100644 tests/exceptions/expiry.phpt create mode 100644 tests/functions.php create mode 100644 tests/simple_decode.phpt create mode 100644 tests/simple_encode.phpt diff --git a/README.md b/README.md index 08d6c03..693a4f1 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,12 @@ A simple PHP extension for encoding and decoding JSON Web Tokens (JWTs). - HS256 - HS384 - HS512 +- RS256 +- RS384 +- RS512 +- PS256 +- PS384 +- PS512 ## Installation @@ -35,14 +41,14 @@ If you intend to run the tests in the tests directory, run the following command make test ``` +> Do not forget to enable the extension by adding `extension=extjwt` to your `php.ini` file. ## Usage -### Encoding a JWT - -Creating a cryptographically signed token from arbitrary claims. +Shown below is an example of how to encode and decode a JWT using the default HS256 signature, a payload (claims), and a secret. ```php +const SECRET = '@loki'; const CLAIMS = [ 'iss' => 'https://github.com/ace411/extjwt-cpp', 'aud' => 'https://github.com/ace411/extjwt-cpp', @@ -53,18 +59,9 @@ const CLAIMS = [ 'cpp_ver' => 11 ]; -$token = jwt_encode('@loki', JWT_ALGO_HS512, CLAIMS); -``` - -### Decoding a JWT +$token = jwt_encode(SECRET, CLAIMS); // outputs a string token -Decoding a JWT - print a hash table with encoded claims. - -```php -... -$claims = jwt_decode($token, '@loki', JWT_ALGO_HS512); - -assert($claims == CLAIMS, 'Claims are not the same'); +$claims = jwt_decode($token, SECRET); // outputs a PHP hashtable ``` **The supported algorithm constants** are: @@ -72,7 +69,112 @@ assert($claims == CLAIMS, 'Claims are not the same'); - ```JWT_ALGO_HS256``` - ```JWT_ALGO_HS384``` - ```JWT_ALGO_HS512``` +- ```JWT_ALGO_RS256``` +- ```JWT_ALGO_RS384``` +- ```JWT_ALGO_RS512``` +- ```JWT_ALGO_PS256``` +- ```JWT_ALGO_PS384``` +- ```JWT_ALGO_PS512``` + +## API + +- **`jwt_encode`** + +~~~ +jwt_encode(string $secret, array $claims, int $algorithm) +~~~ + +**Description:** This function creates a JSON Web Token. + +**Since:** + +- v0.1.0 + +**Argument(s):** + +- ***secret (string)*** - A discretionary secret key essential for JWT encipherment +> For algorithms prefixed with PS and RS, this is the private key + +- ***claims (array)*** - The JWT payload; a combination of registered, public, and private claims + +- ***algorithm (int)*** - The JWT signature algorithm + +**Usage:** + +~~~php +const RSA_PRIVATE_KEY = <<<'KEY' +-----BEGIN RSA PRIVATE KEY----- +MIICXAIBAAKBgQC8kGa1pSjbSYZVebtTRBLxBz5H4i2p/llLCrEeQhta5kaQu/Rn +vuER4W8oDH3+3iuIYW4VQAzyqFpwuzjkDI+17t5t0tyazyZ8JXw+KgXTxldMPEL9 +5+qVhgXvwtihXC1c5oGbRlEDvDF6Sa53rcFVsYJ4ehde/zUxo6UvS7UrBQIDAQAB +AoGAb/MXV46XxCFRxNuB8LyAtmLDgi/xRnTAlMHjSACddwkyKem8//8eZtw9fzxz +bWZ/1/doQOuHBGYZU8aDzzj59FZ78dyzNFoF91hbvZKkg+6wGyd/LrGVEB+Xre0J +Nil0GReM2AHDNZUYRv+HYJPIOrB0CRczLQsgFJ8K6aAD6F0CQQDzbpjYdx10qgK1 +cP59UHiHjPZYC0loEsk7s+hUmT3QHerAQJMZWC11Qrn2N+ybwwNblDKv+s5qgMQ5 +5tNoQ9IfAkEAxkyffU6ythpg/H0Ixe1I2rd0GbF05biIzO/i77Det3n4YsJVlDck +ZkcvY3SK2iRIL4c9yY6hlIhs+K9wXTtGWwJBAO9Dskl48mO7woPR9uD22jDpNSwe +k90OMepTjzSvlhjbfuPN1IdhqvSJTDychRwn1kIJ7LQZgQ8fVz9OCFZ/6qMCQGOb +qaGwHmUK6xzpUbbacnYrIM6nLSkXgOAwv7XXCojvY614ILTK3iXiLBOxPu5Eu13k +eUz9sHyD6vkgZzjtxXECQAkp4Xerf5TGfQXGXhxIX52yH+N2LtujCdkQZjXAsGdm +B2zNzvrlgRmgBrklMTrMYgm1NPcW+bRLGcwgW2PTvNM= +-----END RSA PRIVATE KEY----- +KEY; + +$token = jwt_encode([ + 'user' => 'ace411', + 'twitter' => '@agiroLoki', + 'iss' => 'example.org', + 'aud' => 'example.org', +], RSA_PRIVATE_KEY, JWT_ALGO_RS256); + +echo $token; +~~~ + +- **`jwt_decode`** + +~~~ +jwt_decode(string $token, string $secret, int $algorithm) +~~~ + +**Description:** This function decodes a JSON Web Token into a list of claims. + +**Since:** + +- v0.1.0 + +**Argument(s):** + +- ***token (string)*** - The JWT to decode + +- ***secret (string)*** - A discretionary secret key essential for JWT encipherment +> For algorithms prefixed with PS and RS, this is the private key + +- ***algorithm (int)*** - The JWT signature algorithm + +**Usage:** + +~~~php +// combine with previous snippet +const RSA_PUBLIC_KEY = <<<'KEY' +-----BEGIN PUBLIC KEY----- +MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC8kGa1pSjbSYZVebtTRBLxBz5H +4i2p/llLCrEeQhta5kaQu/RnvuER4W8oDH3+3iuIYW4VQAzyqFpwuzjkDI+17t5t +0tyazyZ8JXw+KgXTxldMPEL95+qVhgXvwtihXC1c5oGbRlEDvDF6Sa53rcFVsYJ4 +ehde/zUxo6UvS7UrBQIDAQAB +-----END PUBLIC KEY----- +KEY; + +$claims = jwt_decode($token, RSA_PUBLIC_KEY, JWT_ALGO_RS256); + +print_r($claims); +~~~ + +## Why install extjwt-cpp? + +PHP extensions, are by virtue of being C/C++ syntaxes, faster than regular PHP. extjwt is a PHP module which wraps around another C++ library and infuses robustness into the PHP userland. Although it is possible to craft high-quality idiomatic PHP solutions like [php-jwt](https://github.com/firebase/php-jwt), extjwt is more performant. + +## Contributing -## Note +Consider buying me a coffee if you appreciate the offerings of project. -The extjwt-cpp extension is merely a wrapper around a C++ package. Because PHP has robust OpenSSL bindings - it is possible to use one of either a tailor-made or off-shelf PHP userland implementation of JWT encoding-decoding software (tools like [php-jwt](https://github.com/firebase/php-jwt) come to mind). \ No newline at end of file +Buy Me A Coffee diff --git a/extjwt.cpp b/extjwt.cpp index 3bbcdfb..fdefb34 100644 --- a/extjwt.cpp +++ b/extjwt.cpp @@ -1,3 +1,9 @@ +/** + * @file extjwt.cpp + * @author Lochemem Bruno Michael (lochbm@gmail.com) + * @brief + * @version 0.1.0 + */ #include "php_extjwt.h" #include "jwt.cpp" @@ -7,6 +13,14 @@ zend_class_entry *jwt_exception_ce; +/** + * @brief + * + * @tparam L + * @param algo + * @return true + * @return false + */ template auto algoIsValid(L algo) -> bool { @@ -15,6 +29,12 @@ auto algoIsValid(L algo) -> bool case JWT_ALGO_HS256: case JWT_ALGO_HS384: case JWT_ALGO_HS512: + case JWT_ALGO_RS256: + case JWT_ALGO_RS384: + case JWT_ALGO_RS512: + case JWT_ALGO_PS256: + case JWT_ALGO_PS384: + case JWT_ALGO_PS512: return true; break; @@ -24,22 +44,43 @@ auto algoIsValid(L algo) -> bool } } +/** + * @brief + * + * @tparam S + * @param str + * @return S + */ +template +auto ucFirst(const S &str) -> S +{ + string fst(""); + fst += toupper(str.at(0)); + fst += str.substr(1); + + return fst; +} + +/* {{{ proto string jwt_encode(string secret, array claims, int algorithm) + Returns a JSON Web Token */ PHP_FUNCTION(jwt_encode) { zend_string *secret; - long algo; + long algo = JWT_ALGO_HS256; // default JWT encoding algorithm zval *claims; zend_string *key; zval *claim; strmap jwtClaims; - ZEND_PARSE_PARAMETERS_START(0, 3) + ZEND_PARSE_PARAMETERS_START(2, 3) + Z_PARAM_ARRAY(claims) Z_PARAM_STR(secret) + Z_PARAM_OPTIONAL Z_PARAM_LONG(algo) - Z_PARAM_ARRAY(claims) ZEND_PARSE_PARAMETERS_END(); + // throw a PHP exception if one of either the payload or secret is empty if (zend_hash_num_elements(HASH_OF(claims)) == 0 || ZSTR_LEN(secret) == 0) { @@ -50,6 +91,7 @@ PHP_FUNCTION(jwt_encode) RETURN_NULL(); } + // throw a PHP exception if the algorithm is invalid if (algoIsValid(algo) == false) { zend_string_release(secret); @@ -59,8 +101,10 @@ PHP_FUNCTION(jwt_encode) RETURN_NULL(); } + // iterate through the list of claims ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(claims), key, claim) { + // JSON encode all sub-array claims if (Z_TYPE_P(claim) == IS_ARRAY) { smart_str jsonData = {0}; @@ -72,6 +116,7 @@ PHP_FUNCTION(jwt_encode) } else { + // coerce claim to string value convert_to_string(claim); jwtClaims[ZSTR_VAL(key)] = Z_STRVAL_P(claim); @@ -86,20 +131,24 @@ PHP_FUNCTION(jwt_encode) zend_string_release(key); zend_string_release(secret); } +/* }}} */ +/* {{{ proto array jwt_decode(string token, string secret, int algorithm) + Returns JSON Web Token payload */ PHP_FUNCTION(jwt_decode) { zend_string *secret; zend_string *token; - long algo; + long algo(JWT_ALGO_HS256); zend_string *key; zval *claim; zval *retval; - ZEND_PARSE_PARAMETERS_START(0, 3) + ZEND_PARSE_PARAMETERS_START(2, 3) Z_PARAM_STR(token) Z_PARAM_STR(secret) + Z_PARAM_OPTIONAL Z_PARAM_LONG(algo) ZEND_PARSE_PARAMETERS_END(); @@ -119,34 +168,40 @@ PHP_FUNCTION(jwt_decode) algo, std::string(ZSTR_VAL(secret))); - array_init(retval); + array_init(retval); // cast the return value as an array zend_string_release(secret); zend_string_release(token); for (auto &iter : claims) { + // store each claim in the retval array as a string add_assoc_string(retval, iter.first.c_str(), iter.second.c_str()); } + // iterate through the retval array and resolve string claims to original uncoerced forms ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(retval), key, claim) { + // copy claim zval to duplicate container + zval dup; + ZVAL_COPY_VALUE(&dup, claim); + if (php_json_decode_ex(claim, - Z_STRVAL_P(claim), - Z_STRLEN_P(claim), + Z_STRVAL_P(&dup), + Z_STRLEN_P(&dup), PHP_JSON_OBJECT_AS_ARRAY, 512) == FAILURE) { - Z_STRVAL_P(claim) == std::string("1").c_str() ? add_assoc_bool(retval, - ZSTR_VAL(key), - 1) - : Z_STRLEN_P(claim) == 0 ? add_assoc_bool(retval, + Z_STRVAL_P(&dup) == std::string("1").c_str() ? add_assoc_bool(retval, + ZSTR_VAL(key), + 1) + : Z_STRLEN_P(&dup) == 0 ? add_assoc_bool(retval, + ZSTR_VAL(key), + 0) + : add_assoc_string(retval, ZSTR_VAL(key), - 0) - : add_assoc_string(retval, - ZSTR_VAL(key), - Z_STRVAL_P(claim)); + Z_STRVAL_P(&dup)); } } ZEND_HASH_FOREACH_END(); @@ -155,14 +210,16 @@ PHP_FUNCTION(jwt_decode) } catch (const std::exception &exp) { + // convert C++ exception to PHP exception zend_string_release(secret); zend_string_release(token); zend_throw_exception(jwt_exception_ce, - exp.what(), + ucFirst(exp.what()).c_str(), 0 TSRMLS_CC); RETURN_NULL(); } } +/* }}} */ PHP_RINIT_FUNCTION(extjwt) { @@ -196,23 +253,30 @@ PHP_MINIT_FUNCTION(extjwt) &ce, zend_exception_get_default(TSRMLS_C)); #endif + // register JWT algorithms as constants REGISTER_LONG_CONSTANT("JWT_ALGO_HS256", JWT_ALGO_HS256, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("JWT_ALGO_HS384", JWT_ALGO_HS384, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("JWT_ALGO_HS512", JWT_ALGO_HS512, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("JWT_ALGO_RS256", JWT_ALGO_RS256, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("JWT_ALGO_RS384", JWT_ALGO_RS384, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("JWT_ALGO_RS512", JWT_ALGO_RS512, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("JWT_ALGO_PS256", JWT_ALGO_PS256, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("JWT_ALGO_PS384", JWT_ALGO_PS384, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("JWT_ALGO_PS512", JWT_ALGO_PS512, CONST_CS | CONST_PERSISTENT); return SUCCESS; } ZEND_BEGIN_ARG_INFO_EX(arginfo_jwt_encode, 0, 0, 3) +ZEND_ARG_ARRAY_INFO(0, claims, 0) ZEND_ARG_INFO(0, secret) -ZEND_ARG_INFO(0, algo) -ZEND_ARG_INFO(0, claims) +ZEND_ARG_INFO(0, algorithm) ZEND_END_ARG_INFO(); ZEND_BEGIN_ARG_INFO_EX(arginfo_jwt_decode, 0, 0, 3) ZEND_ARG_INFO(0, token) ZEND_ARG_INFO(0, secret) -ZEND_ARG_INFO(0, algo) +ZEND_ARG_INFO(0, algorithm) ZEND_END_ARG_INFO(); static const zend_function_entry extjwt_functions[] = { @@ -237,4 +301,4 @@ extern "C" { ZEND_GET_MODULE(extjwt) } -#endif \ No newline at end of file +#endif diff --git a/jwt.cpp b/jwt.cpp index 63b10d0..382e4ff 100644 --- a/jwt.cpp +++ b/jwt.cpp @@ -1,10 +1,28 @@ +/** + * @file jwt.cpp + * @author Lochemem Bruno Michael (lochbm@gmail.com) + * @brief + * @version 0.1.0 + */ #include #include "jwt.h" using namespace std; using namespace jwt; + using timepoint = chrono::time_point; +/** + * @brief + * + * @tparam B + * @tparam L + * @tparam S + * @param obj jwt::builder object + * @param algo jwt algorithm + * @param S jwt secret key + * @return string + */ template auto signJwt(B obj, L algo, const S &secret) -> S { @@ -13,6 +31,7 @@ auto signJwt(B obj, L algo, const S &secret) -> S switch (algo) { case JWT_ALGO_HS256: + default: token += obj.sign(algorithm::hs256{secret}); break; @@ -23,14 +42,48 @@ auto signJwt(B obj, L algo, const S &secret) -> S case JWT_ALGO_HS512: token += obj.sign(algorithm::hs512{secret}); break; + + case JWT_ALGO_PS256: + token += obj.sign(algorithm::ps256("", secret, "", "")); + break; + + case JWT_ALGO_PS384: + token += obj.sign(algorithm::ps384("", secret, "", "")); + break; + + case JWT_ALGO_PS512: + token += obj.sign(algorithm::ps512("", secret, "", "")); + break; + + case JWT_ALGO_RS256: + token += obj.sign(algorithm::rs256("", secret, "", "")); + break; + + case JWT_ALGO_RS384: + token += obj.sign(algorithm::rs384("", secret, "", "")); + break; + + case JWT_ALGO_RS512: + token += obj.sign(algorithm::rs512("", secret, "", "")); + break; } return token; } +/** + * @brief + * + * @tparam B + * @tparam S + * @param obj + * @param claims + * @return B + */ template auto addClaims(B obj, S claims) -> B { + // convert string to C++ time auto strToTime = [](const string &timeval) { const chrono::system_clock::duration timeT = chrono::seconds{stoi(timeval)}; const timepoint duration(timeT); @@ -61,6 +114,17 @@ auto addClaims(B obj, S claims) -> B return obj; } +/** + * @brief + * + * @tparam S + * @tparam L + * @tparam I + * @param secret + * @param algo + * @param claims + * @return S + */ template auto jwtEncode(const S &secret, L algo, I claims) -> S { @@ -72,6 +136,16 @@ auto jwtEncode(const S &secret, L algo, I claims) -> S secret); } +/** + * @brief + * + * @tparam S + * @tparam L + * @param token + * @param algo + * @param secret + * @return strmap + */ template auto jwtDecode(const S &token, L algo, const S &secret) -> strmap { @@ -92,6 +166,30 @@ auto jwtDecode(const S &token, L algo, const S &secret) -> strmap case JWT_ALGO_HS512: verifier.allow_algorithm(algorithm::hs512{secret}); break; + + case JWT_ALGO_RS256: + verifier.allow_algorithm(algorithm::rs256(secret, "", "", "")); + break; + + case JWT_ALGO_RS384: + verifier.allow_algorithm(algorithm::rs384(secret, "", "", "")); + break; + + case JWT_ALGO_RS512: + verifier.allow_algorithm(algorithm::rs512(secret, "", "", "")); + break; + + case JWT_ALGO_PS256: + verifier.allow_algorithm(algorithm::ps256(secret, "", "", "")); + break; + + case JWT_ALGO_PS384: + verifier.allow_algorithm(algorithm::ps384(secret, "", "", "")); + break; + + case JWT_ALGO_PS512: + verifier.allow_algorithm(algorithm::ps512(secret, "", "", "")); + break; } verifier.verify(decoded); @@ -102,4 +200,4 @@ auto jwtDecode(const S &token, L algo, const S &secret) -> strmap } return claims; -} \ No newline at end of file +} diff --git a/jwt.h b/jwt.h index d2d51fc..05f4acf 100644 --- a/jwt.h +++ b/jwt.h @@ -1,15 +1,29 @@ +/** + * @file jwt.h + * @author Lochemem Bruno Michael (lochbm@gmail.com) + * @brief + * @version 0.1.0 + */ #ifndef JWT_H #define JWT_H #include #include #include +#include using namespace std; #define JWT_ALGO_HS256 1 #define JWT_ALGO_HS384 2 #define JWT_ALGO_HS512 3 +#define JWT_ALGO_RS256 4 +#define JWT_ALGO_RS384 5 +#define JWT_ALGO_RS512 6 +#define JWT_ALGO_PS256 7 +#define JWT_ALGO_PS384 8 +#define JWT_ALGO_PS512 9 + #define JWT_CLAIM_REPRESENT \ map \ { \ @@ -18,4 +32,4 @@ using namespace std; typedef map strmap; -#endif /* JWT_H */ \ No newline at end of file +#endif /* JWT_H */ diff --git a/php_extjwt.h b/php_extjwt.h index 4e35b6d..246324b 100644 --- a/php_extjwt.h +++ b/php_extjwt.h @@ -1,3 +1,9 @@ +/** + * @file php_extjwt.h + * @author Lochemem Bruno Michael (lochbm@gmail.com) + * @brief + * @version 0.1.0 + */ #ifndef PHP_EXTJWT_H #define PHP_EXTJWT_H @@ -20,4 +26,4 @@ extern "C" extern zend_module_entry extjwt_module_entry; #define phpext_extjwt_ptr &extjwt_module_entry -#endif /* PHP_EXTJWT_H */ \ No newline at end of file +#endif /* PHP_EXTJWT_H */ diff --git a/tests/002.phpt b/tests/002.phpt deleted file mode 100644 index 0a594fa..0000000 --- a/tests/002.phpt +++ /dev/null @@ -1,10 +0,0 @@ ---TEST-- -jwt_encode function outputs JSON Web Token ---FILE-- - ---EXPECT-- -string \ No newline at end of file diff --git a/tests/0021.phpt b/tests/0021.phpt deleted file mode 100644 index 2b338df..0000000 --- a/tests/0021.phpt +++ /dev/null @@ -1,14 +0,0 @@ ---TEST-- -jwt_encode throws JwtException when invalid algorithm is detected ---FILE-- -getMessage(); -} -?> ---EXPECT-- -Invalid algorithm detected \ No newline at end of file diff --git a/tests/0022.phpt b/tests/0022.phpt deleted file mode 100644 index 465eccf..0000000 --- a/tests/0022.phpt +++ /dev/null @@ -1,14 +0,0 @@ ---TEST-- -jwt_encode throws ExtJwtException when a parameter is empty ---FILE-- -getMessage(); -} -?> ---EXPECT-- -Argument(s) cannot be empty \ No newline at end of file diff --git a/tests/0023.phpt b/tests/0023.phpt deleted file mode 100644 index 26748b1..0000000 --- a/tests/0023.phpt +++ /dev/null @@ -1,16 +0,0 @@ ---TEST-- -jwt_encode encodes multi-dimensional array data ---FILE-- - ---EXPECT-- -string \ No newline at end of file diff --git a/tests/003.phpt b/tests/003.phpt deleted file mode 100644 index 4eb4a7a..0000000 --- a/tests/003.phpt +++ /dev/null @@ -1,13 +0,0 @@ ---TEST-- -jwt_decode outputs JWT claims as hashtable ---FILE-- - ---EXPECT-- -array \ No newline at end of file diff --git a/tests/0031.phpt b/tests/0031.phpt deleted file mode 100644 index 714ec68..0000000 --- a/tests/0031.phpt +++ /dev/null @@ -1,16 +0,0 @@ ---TEST-- -jwt_decode throws JwtException when invalid algorithm is detected ---FILE-- -getMessage(); -} -?> ---EXPECT-- -Invalid algorithm detected \ No newline at end of file diff --git a/tests/0032.phpt b/tests/0032.phpt deleted file mode 100644 index 7f6d8be..0000000 --- a/tests/0032.phpt +++ /dev/null @@ -1,12 +0,0 @@ ---TEST-- -jwt_decode throws ExtJwtException when a parameter is empty ---FILE-- -getMessage(); -} -?> ---EXPECT-- -invalid token supplied \ No newline at end of file diff --git a/tests/0033.phpt b/tests/0033.phpt deleted file mode 100644 index ab01083..0000000 --- a/tests/0033.phpt +++ /dev/null @@ -1,12 +0,0 @@ ---TEST-- -jwt_decode decodes multi-dimensional array data ---FILE-- - ---EXPECT-- -array \ No newline at end of file diff --git a/tests/0034.phpt b/tests/0034.phpt deleted file mode 100644 index 58ad51a..0000000 --- a/tests/0034.phpt +++ /dev/null @@ -1,23 +0,0 @@ ---TEST-- -jwt_decode decodes array with non-string values intact ---FILE-- - false, - 'cpp_ver' => 11, - 'pi' => 3.141 -]); - -var_dump(jwt_decode($jwt, TOKEN_SECRET, JWT_ALGO_HS256)); -?> ---EXPECT-- -array(3) { - ["cpp_ver"]=> - int(11) - ["hasInsta"]=> - bool(false) - ["pi"]=> - float(3.141) -} \ No newline at end of file diff --git a/tests/004.phpt b/tests/004.phpt deleted file mode 100644 index 7527893..0000000 --- a/tests/004.phpt +++ /dev/null @@ -1,14 +0,0 @@ ---TEST-- -jwt_decode throws ExtJwtException when decode error is encountered ---FILE-- -getMessage(); -} -?> ---EXPECT-- -invalid token supplied \ No newline at end of file diff --git a/tests/005.phpt b/tests/005.phpt deleted file mode 100644 index 7eafb98..0000000 --- a/tests/005.phpt +++ /dev/null @@ -1,19 +0,0 @@ ---TEST-- -jwt_decode throws ExtJwtException when token expires ---FILE-- - 1568703793, - 'exp' => 1568703798 - ]); - sleep(10); - var_dump(jwt_decode($token, TOKEN_SECRET, JWT_ALGO_HS256)); -} catch (ExtJwtException $exp) { - echo $exp->getMessage(); -} -?> ---EXPECT-- -token verification failed: token expired \ No newline at end of file diff --git a/tests/001.phpt b/tests/check.phpt similarity index 100% rename from tests/001.phpt rename to tests/check.phpt diff --git a/tests/constants.php b/tests/constants.php index 062ad07..45d2e86 100644 --- a/tests/constants.php +++ b/tests/constants.php @@ -1,13 +1,71 @@ '@ace411', 'twitter' => '@agiroLoki' ]; +/** + * @var array TOKEN_CLAIMS_MULTIDIM + */ const TOKEN_CLAIMS_MULTIDIM = [ 'social' => TOKEN_CLAIMS, 'location' => 'kampala' -]; \ No newline at end of file +]; + +/** + * @var string RSA_PUBLIC_KEY + */ +const RSA_PUBLIC_KEY = <<<'KEY' +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuGbXWiK3dQTyCbX5xdE4 +yCuYp0AF2d15Qq1JSXT/lx8CEcXb9RbDddl8jGDv+spi5qPa8qEHiK7FwV2KpRE9 +83wGPnYsAm9BxLFb4YrLYcDFOIGULuk2FtrPS512Qea1bXASuvYXEpQNpGbnTGVs +WXI9C+yjHztqyL2h8P6mlThPY9E9ue2fCqdgixfTFIF9Dm4SLHbphUS2iw7w1JgT +69s7of9+I9l5lsJ9cozf1rxrXX4V1u/SotUuNB3Fp8oB4C1fLBEhSlMcUJirz1E8 +AziMCxS+VrRPDM+zfvpIJg3JljAh3PJHDiLu902v9w+Iplu1WyoB2aPfitxEhRN0 +YwIDAQAB +-----END PUBLIC KEY----- +KEY; + +/** + * @var string RSA_PRIVATE_KEY + */ +const RSA_PRIVATE_KEY = <<<'KEY' +-----BEGIN PRIVATE KEY----- +MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQC4ZtdaIrd1BPIJ +tfnF0TjIK5inQAXZ3XlCrUlJdP+XHwIRxdv1FsN12XyMYO/6ymLmo9ryoQeIrsXB +XYqlET3zfAY+diwCb0HEsVvhisthwMU4gZQu6TYW2s9LnXZB5rVtcBK69hcSlA2k +ZudMZWxZcj0L7KMfO2rIvaHw/qaVOE9j0T257Z8Kp2CLF9MUgX0ObhIsdumFRLaL +DvDUmBPr2zuh/34j2XmWwn1yjN/WvGtdfhXW79Ki1S40HcWnygHgLV8sESFKUxxQ +mKvPUTwDOIwLFL5WtE8Mz7N++kgmDcmWMCHc8kcOIu73Ta/3D4imW7VbKgHZo9+K +3ESFE3RjAgMBAAECggEBAJTEIyjMqUT24G2FKiS1TiHvShBkTlQdoR5xvpZMlYbN +tVWxUmrAGqCQ/TIjYnfpnzCDMLhdwT48Ab6mQJw69MfiXwc1PvwX1e9hRscGul36 +ryGPKIVQEBsQG/zc4/L2tZe8ut+qeaK7XuYrPp8bk/X1e9qK5m7j+JpKosNSLgJj +NIbYsBkG2Mlq671irKYj2hVZeaBQmWmZxK4fw0Istz2WfN5nUKUeJhTwpR+JLUg4 +ELYYoB7EO0Cej9UBG30hbgu4RyXA+VbptJ+H042K5QJROUbtnLWuuWosZ5ATldwO +u03dIXL0SH0ao5NcWBzxU4F2sBXZRGP2x/jiSLHcqoECgYEA4qD7mXQpu1b8XO8U +6abpKloJCatSAHzjgdR2eRDRx5PMvloipfwqA77pnbjTUFajqWQgOXsDTCjcdQui +wf5XAaWu+TeAVTytLQbSiTsBhrnoqVrr3RoyDQmdnwHT8aCMouOgcC5thP9vQ8Us +rVdjvRRbnJpg3BeSNimH+u9AHgsCgYEA0EzcbOltCWPHRAY7B3Ge/AKBjBQr86Kv +TdpTlxePBDVIlH+BM6oct2gaSZZoHbqPjbq5v7yf0fKVcXE4bSVgqfDJ/sZQu9Lp +PTeV7wkk0OsAMKk7QukEpPno5q6tOTNnFecpUhVLLlqbfqkB2baYYwLJR3IRzboJ +FQbLY93E8gkCgYB+zlC5VlQbbNqcLXJoImqItgQkkuW5PCgYdwcrSov2ve5r/Acz +FNt1aRdSlx4176R3nXyibQA1Vw+ztiUFowiP9WLoM3PtPZwwe4bGHmwGNHPIfwVG +m+exf9XgKKespYbLhc45tuC08DATnXoYK7O1EnUINSFJRS8cezSI5eHcbQKBgQDC +PgqHXZ2aVftqCc1eAaxaIRQhRmY+CgUjumaczRFGwVFveP9I6Gdi+Kca3DE3F9Pq +PKgejo0SwP5vDT+rOGHN14bmGJUMsX9i4MTmZUZ5s8s3lXh3ysfT+GAhTd6nKrIE +kM3Nh6HWFhROptfc6BNusRh1kX/cspDplK5x8EpJ0QKBgQDWFg6S2je0KtbV5PYe +RultUEe2C0jYMDQx+JYxbPmtcopvZQrFEur3WKVuLy5UAy7EBvwMnZwIG7OOohJb +vkSpADK6VPn9lbqq7O8cTedEHttm6otmLt8ZyEl3hZMaL3hbuRj6ysjmoFKx6CrX +rK0/Ikt5ybqUzKCMJZg2VKGTxg== +-----END PRIVATE KEY----- +KEY; diff --git a/tests/decode/decode_hs.phpt b/tests/decode/decode_hs.phpt new file mode 100644 index 0000000..b82f02b --- /dev/null +++ b/tests/decode/decode_hs.phpt @@ -0,0 +1,29 @@ +--TEST-- +jwt_decode decodes JWTs signed with algorithms prefixed with HS +--FILE-- + +--EXPECT-- +Array +( + [github] => @ace411 + [twitter] => @agiroLoki +) \ No newline at end of file diff --git a/tests/decode/decode_multidimensional.phpt b/tests/decode/decode_multidimensional.phpt new file mode 100644 index 0000000..80b6e51 --- /dev/null +++ b/tests/decode/decode_multidimensional.phpt @@ -0,0 +1,21 @@ +--TEST-- +jwt_decode decodes token with multi-dimensional array data +--FILE-- + +--EXPECT-- +Array +( + [location] => kampala + [social] => Array + ( + [github] => @ace411 + [twitter] => @agiroLoki + ) + +) \ No newline at end of file diff --git a/tests/decode/decode_ps.phpt b/tests/decode/decode_ps.phpt new file mode 100644 index 0000000..7fc312a --- /dev/null +++ b/tests/decode/decode_ps.phpt @@ -0,0 +1,29 @@ +--TEST-- +jwt_decode decodes JWTs signed with algorithms prefixed with PS +--FILE-- + +--EXPECT-- +Array +( + [github] => @ace411 + [twitter] => @agiroLoki +) \ No newline at end of file diff --git a/tests/decode/decode_rs.phpt b/tests/decode/decode_rs.phpt new file mode 100644 index 0000000..f991d23 --- /dev/null +++ b/tests/decode/decode_rs.phpt @@ -0,0 +1,29 @@ +--TEST-- +jwt_decode decodes JWTs signed with algorithms prefixed with RS +--FILE-- + +--EXPECT-- +Array +( + [github] => @ace411 + [twitter] => @agiroLoki +) \ No newline at end of file diff --git a/tests/encode/encode_hs.phpt b/tests/encode/encode_hs.phpt new file mode 100644 index 0000000..c8bac4b --- /dev/null +++ b/tests/encode/encode_hs.phpt @@ -0,0 +1,23 @@ +--TEST-- +jwt_encode signs JWTs with algorithms prefixed with HS +--FILE-- + $encode(JWT_ALGO_HS256), + 'hs384' => $encode(JWT_ALGO_HS384), + 'hs512' => $encode(JWT_ALGO_HS512) +]; + +print_r($tokens); +?> +--EXPECT-- +Array +( + [hs256] => eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXUyJ9.eyJnaXRodWIiOiJAYWNlNDExIiwidHdpdHRlciI6IkBhZ2lyb0xva2kifQ.sxRKwGM5W8hirRvWXpofT1_SoRQvUnAfynWs39ZKFCY + [hs384] => eyJhbGciOiJIUzM4NCIsInR5cCI6IkpXUyJ9.eyJnaXRodWIiOiJAYWNlNDExIiwidHdpdHRlciI6IkBhZ2lyb0xva2kifQ.FQipugiod7bLPCQ757BusDNv50BQZ8LsyBHvXo3X-U43ppjQmsMwV3-qFdnrKvM9 + [hs512] => eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXUyJ9.eyJnaXRodWIiOiJAYWNlNDExIiwidHdpdHRlciI6IkBhZ2lyb0xva2kifQ.-MY1yEGluzqggyoqjAkT0_OxIKfoAun4s1mll0fXyHAlv6FuEqWkrqpC3fwEjI8ri4WBEI83IdJBtBCV2PBW5g +) \ No newline at end of file diff --git a/tests/encode/encode_multidimensional.phpt b/tests/encode/encode_multidimensional.phpt new file mode 100644 index 0000000..211cdaa --- /dev/null +++ b/tests/encode/encode_multidimensional.phpt @@ -0,0 +1,12 @@ +--TEST-- +jwt_encode encodes multi-dimensional array data +--FILE-- + +--EXPECT-- +eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXUyJ9.eyJsb2NhdGlvbiI6ImthbXBhbGEiLCJzb2NpYWwiOiJ7XCJnaXRodWJcIjpcIkBhY2U0MTFcIixcInR3aXR0ZXJcIjpcIkBhZ2lyb0xva2lcIn0ifQ.sHFxpicUZ7IfgXk6Q32bgyJLDT3bL-fqAdG0qU027gI \ No newline at end of file diff --git a/tests/encode/encode_ps.phpt b/tests/encode/encode_ps.phpt new file mode 100644 index 0000000..3f80480 --- /dev/null +++ b/tests/encode/encode_ps.phpt @@ -0,0 +1,24 @@ +--TEST-- +jwt_encode signs JWTs with algorithms prefixed with PS +--FILE-- + $encode(JWT_ALGO_PS256), + 'ps384' => $encode(JWT_ALGO_PS384), + 'ps512' => $encode(JWT_ALGO_PS512) +]; + +$isValid = array_reduce($tokens, function ($acc, $token) { + $acc = preg_match('/([\w\d]*)(.){1}([\w\d\W]*)/', $token) ? true : false; + + return $acc; +}, false); + +echo $isValid ? 'true' : 'false'; +?> +--EXPECT-- +true \ No newline at end of file diff --git a/tests/encode/encode_rs.phpt b/tests/encode/encode_rs.phpt new file mode 100644 index 0000000..152ab37 --- /dev/null +++ b/tests/encode/encode_rs.phpt @@ -0,0 +1,24 @@ +--TEST-- +jwt_encode signs JWTs with algorithms prefixed with RS +--FILE-- + $encode(JWT_ALGO_PS256), + 'rs384' => $encode(JWT_ALGO_PS384), + 'rs512' => $encode(JWT_ALGO_PS512) +]; + +$isValid = array_reduce($tokens, function ($acc, $token) { + $acc = preg_match('/([\w\d]*)(.){1}([\w\d\W]*)/', $token) ? true : false; + + return $acc; +}, false); + +echo $isValid ? 'true' : 'false'; +?> +--EXPECT-- +true \ No newline at end of file diff --git a/tests/exceptions/algo_decode.phpt b/tests/exceptions/algo_decode.phpt new file mode 100644 index 0000000..610a4c7 --- /dev/null +++ b/tests/exceptions/algo_decode.phpt @@ -0,0 +1,16 @@ +--TEST-- +jwt_decode throws ExtJwtException when it encounters an invalid algorithm +--FILE-- +getMessage(); +} +?> +--EXPECT-- +Invalid algorithm detected \ No newline at end of file diff --git a/tests/exceptions/algo_encode.phpt b/tests/exceptions/algo_encode.phpt new file mode 100644 index 0000000..4fdc6ce --- /dev/null +++ b/tests/exceptions/algo_encode.phpt @@ -0,0 +1,14 @@ +--TEST-- +jwt_encode throws an exception when it encounters an invalid algorithm +--FILE-- +getMessage(); +} +?> +--EXPECT-- +Invalid algorithm detected \ No newline at end of file diff --git a/tests/exceptions/algo_mismatch.phpt b/tests/exceptions/algo_mismatch.phpt new file mode 100644 index 0000000..6d0e0ba --- /dev/null +++ b/tests/exceptions/algo_mismatch.phpt @@ -0,0 +1,16 @@ +--TEST-- +jwt_decode throws an exception when algorithm mismatch is encountered +--FILE-- +getMessage(); +} +?> +--EXPECT-- +Token verification failed: wrong algorithm \ No newline at end of file diff --git a/tests/exceptions/empty_decode.phpt b/tests/exceptions/empty_decode.phpt new file mode 100644 index 0000000..40d1be4 --- /dev/null +++ b/tests/exceptions/empty_decode.phpt @@ -0,0 +1,12 @@ +--TEST-- +jwt_decode throws exception whenever empty parameters are detected +--FILE-- +getMessage(); +} +?> +--EXPECT-- +Invalid token supplied \ No newline at end of file diff --git a/tests/exceptions/empty_encode.phpt b/tests/exceptions/empty_encode.phpt new file mode 100644 index 0000000..95b7cc4 --- /dev/null +++ b/tests/exceptions/empty_encode.phpt @@ -0,0 +1,12 @@ +--TEST-- +jwt_encode throws exception whenever empty parameters are detected +--FILE-- +getMessage(); +} +?> +--EXPECT-- +Argument(s) cannot be empty \ No newline at end of file diff --git a/tests/exceptions/expiry.phpt b/tests/exceptions/expiry.phpt new file mode 100644 index 0000000..e99d630 --- /dev/null +++ b/tests/exceptions/expiry.phpt @@ -0,0 +1,21 @@ +--TEST-- +extension throws an exception upon detection of token expiry +--FILE-- + time(), + 'exp' => time() + 5 + ]), TOKEN_SECRET); + + sleep(6); + + print_r(jwt_decode($token, TOKEN_SECRET)); +} catch (ExtJwtException $exp) { + echo $exp->getMessage(); +} +?> +--EXPECT-- +Token verification failed: token expired \ No newline at end of file diff --git a/tests/functions.php b/tests/functions.php new file mode 100644 index 0000000..4b22037 --- /dev/null +++ b/tests/functions.php @@ -0,0 +1,19 @@ + +--EXPECT-- +Array +( + [github] => @ace411 + [twitter] => @agiroLoki +) \ No newline at end of file diff --git a/tests/simple_encode.phpt b/tests/simple_encode.phpt new file mode 100644 index 0000000..147c6ed --- /dev/null +++ b/tests/simple_encode.phpt @@ -0,0 +1,10 @@ +--TEST-- +jwt_encode function outputs JSON Web Token +--FILE-- + +--EXPECT-- +eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXUyJ9.eyJnaXRodWIiOiJAYWNlNDExIiwidHdpdHRlciI6IkBhZ2lyb0xva2kifQ.sxRKwGM5W8hirRvWXpofT1_SoRQvUnAfynWs39ZKFCY \ No newline at end of file From 7b07d5f132345ebebf30614ca638e9b52b72d449 Mon Sep 17 00:00:00 2001 From: ace411 Date: Mon, 9 Mar 2020 17:51:54 +0300 Subject: [PATCH 44/44] Added new algorithms --- README.md | 134 +++++++++++++++++++--- extjwt.cpp | 108 +++++++++++++---- jwt.cpp | 100 +++++++++++++++- jwt.h | 16 ++- php_extjwt.h | 8 +- tests/002.phpt | 10 -- tests/0021.phpt | 14 --- tests/0022.phpt | 14 --- tests/0023.phpt | 16 --- tests/003.phpt | 13 --- tests/0031.phpt | 16 --- tests/0032.phpt | 12 -- tests/0033.phpt | 12 -- tests/0034.phpt | 23 ---- tests/004.phpt | 14 --- tests/005.phpt | 19 --- tests/{001.phpt => check.phpt} | 0 tests/constants.php | 60 +++++++++- tests/decode/decode_hs.phpt | 29 +++++ tests/decode/decode_multidimensional.phpt | 21 ++++ tests/decode/decode_ps.phpt | 29 +++++ tests/decode/decode_rs.phpt | 29 +++++ tests/encode/encode_hs.phpt | 23 ++++ tests/encode/encode_multidimensional.phpt | 12 ++ tests/encode/encode_ps.phpt | 24 ++++ tests/encode/encode_rs.phpt | 24 ++++ tests/exceptions/algo_decode.phpt | 16 +++ tests/exceptions/algo_encode.phpt | 14 +++ tests/exceptions/algo_mismatch.phpt | 16 +++ tests/exceptions/empty_decode.phpt | 12 ++ tests/exceptions/empty_encode.phpt | 12 ++ tests/exceptions/expiry.phpt | 21 ++++ tests/functions.php | 19 +++ tests/simple_decode.phpt | 17 +++ tests/simple_encode.phpt | 10 ++ 35 files changed, 712 insertions(+), 205 deletions(-) delete mode 100644 tests/002.phpt delete mode 100644 tests/0021.phpt delete mode 100644 tests/0022.phpt delete mode 100644 tests/0023.phpt delete mode 100644 tests/003.phpt delete mode 100644 tests/0031.phpt delete mode 100644 tests/0032.phpt delete mode 100644 tests/0033.phpt delete mode 100644 tests/0034.phpt delete mode 100644 tests/004.phpt delete mode 100644 tests/005.phpt rename tests/{001.phpt => check.phpt} (100%) create mode 100644 tests/decode/decode_hs.phpt create mode 100644 tests/decode/decode_multidimensional.phpt create mode 100644 tests/decode/decode_ps.phpt create mode 100644 tests/decode/decode_rs.phpt create mode 100644 tests/encode/encode_hs.phpt create mode 100644 tests/encode/encode_multidimensional.phpt create mode 100644 tests/encode/encode_ps.phpt create mode 100644 tests/encode/encode_rs.phpt create mode 100644 tests/exceptions/algo_decode.phpt create mode 100644 tests/exceptions/algo_encode.phpt create mode 100644 tests/exceptions/algo_mismatch.phpt create mode 100644 tests/exceptions/empty_decode.phpt create mode 100644 tests/exceptions/empty_encode.phpt create mode 100644 tests/exceptions/expiry.phpt create mode 100644 tests/functions.php create mode 100644 tests/simple_decode.phpt create mode 100644 tests/simple_encode.phpt diff --git a/README.md b/README.md index 08d6c03..f4d9121 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,12 @@ A simple PHP extension for encoding and decoding JSON Web Tokens (JWTs). - HS256 - HS384 - HS512 +- RS256 +- RS384 +- RS512 +- PS256 +- PS384 +- PS512 ## Installation @@ -35,14 +41,14 @@ If you intend to run the tests in the tests directory, run the following command make test ``` +> Do not forget to enable the extension by adding `extension=extjwt` to your `php.ini` file. ## Usage -### Encoding a JWT - -Creating a cryptographically signed token from arbitrary claims. +Shown below is an example of how to encode and decode a JWT using the default HS256 signature, a payload (claims), and a secret. ```php +const SECRET = '@loki'; const CLAIMS = [ 'iss' => 'https://github.com/ace411/extjwt-cpp', 'aud' => 'https://github.com/ace411/extjwt-cpp', @@ -53,18 +59,9 @@ const CLAIMS = [ 'cpp_ver' => 11 ]; -$token = jwt_encode('@loki', JWT_ALGO_HS512, CLAIMS); -``` - -### Decoding a JWT +$token = jwt_encode(SECRET, CLAIMS); // outputs a string token -Decoding a JWT - print a hash table with encoded claims. - -```php -... -$claims = jwt_decode($token, '@loki', JWT_ALGO_HS512); - -assert($claims == CLAIMS, 'Claims are not the same'); +$claims = jwt_decode($token, SECRET); // outputs a PHP hashtable ``` **The supported algorithm constants** are: @@ -72,7 +69,112 @@ assert($claims == CLAIMS, 'Claims are not the same'); - ```JWT_ALGO_HS256``` - ```JWT_ALGO_HS384``` - ```JWT_ALGO_HS512``` +- ```JWT_ALGO_RS256``` +- ```JWT_ALGO_RS384``` +- ```JWT_ALGO_RS512``` +- ```JWT_ALGO_PS256``` +- ```JWT_ALGO_PS384``` +- ```JWT_ALGO_PS512``` + +## API + +- **`jwt_encode`** + +~~~ +jwt_encode(string $secret, array $claims, int $algorithm) +~~~ + +**Description:** This function creates a JSON Web Token. + +**Since:** + +- v0.1.0 + +**Argument(s):** + +- ***secret (string)*** - A discretionary secret key essential for JWT encipherment +> For algorithms prefixed with PS and RS, this is the private key + +- ***claims (array)*** - The JWT payload; a combination of registered, public, and private claims + +- ***algorithm (int)*** - The JWT signature algorithm + +**Usage:** + +~~~php +const RSA_PRIVATE_KEY = <<<'KEY' +-----BEGIN RSA PRIVATE KEY----- +MIICXAIBAAKBgQC8kGa1pSjbSYZVebtTRBLxBz5H4i2p/llLCrEeQhta5kaQu/Rn +vuER4W8oDH3+3iuIYW4VQAzyqFpwuzjkDI+17t5t0tyazyZ8JXw+KgXTxldMPEL9 +5+qVhgXvwtihXC1c5oGbRlEDvDF6Sa53rcFVsYJ4ehde/zUxo6UvS7UrBQIDAQAB +AoGAb/MXV46XxCFRxNuB8LyAtmLDgi/xRnTAlMHjSACddwkyKem8//8eZtw9fzxz +bWZ/1/doQOuHBGYZU8aDzzj59FZ78dyzNFoF91hbvZKkg+6wGyd/LrGVEB+Xre0J +Nil0GReM2AHDNZUYRv+HYJPIOrB0CRczLQsgFJ8K6aAD6F0CQQDzbpjYdx10qgK1 +cP59UHiHjPZYC0loEsk7s+hUmT3QHerAQJMZWC11Qrn2N+ybwwNblDKv+s5qgMQ5 +5tNoQ9IfAkEAxkyffU6ythpg/H0Ixe1I2rd0GbF05biIzO/i77Det3n4YsJVlDck +ZkcvY3SK2iRIL4c9yY6hlIhs+K9wXTtGWwJBAO9Dskl48mO7woPR9uD22jDpNSwe +k90OMepTjzSvlhjbfuPN1IdhqvSJTDychRwn1kIJ7LQZgQ8fVz9OCFZ/6qMCQGOb +qaGwHmUK6xzpUbbacnYrIM6nLSkXgOAwv7XXCojvY614ILTK3iXiLBOxPu5Eu13k +eUz9sHyD6vkgZzjtxXECQAkp4Xerf5TGfQXGXhxIX52yH+N2LtujCdkQZjXAsGdm +B2zNzvrlgRmgBrklMTrMYgm1NPcW+bRLGcwgW2PTvNM= +-----END RSA PRIVATE KEY----- +KEY; + +$token = jwt_encode([ + 'user' => 'ace411', + 'twitter' => '@agiroLoki', + 'iss' => 'example.org', + 'aud' => 'example.org', +], RSA_PRIVATE_KEY, JWT_ALGO_RS256); + +echo $token; +~~~ + +- **`jwt_decode`** + +~~~ +jwt_decode(string $token, string $secret, int $algorithm) +~~~ + +**Description:** This function decodes a JSON Web Token into a list of claims. + +**Since:** + +- v0.1.0 + +**Argument(s):** + +- ***token (string)*** - The JWT to decode + +- ***secret (string)*** - A discretionary secret key essential for JWT encipherment +> For algorithms prefixed with PS and RS, this is the public key + +- ***algorithm (int)*** - The JWT signature algorithm + +**Usage:** + +~~~php +// combine with previous snippet +const RSA_PUBLIC_KEY = <<<'KEY' +-----BEGIN PUBLIC KEY----- +MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC8kGa1pSjbSYZVebtTRBLxBz5H +4i2p/llLCrEeQhta5kaQu/RnvuER4W8oDH3+3iuIYW4VQAzyqFpwuzjkDI+17t5t +0tyazyZ8JXw+KgXTxldMPEL95+qVhgXvwtihXC1c5oGbRlEDvDF6Sa53rcFVsYJ4 +ehde/zUxo6UvS7UrBQIDAQAB +-----END PUBLIC KEY----- +KEY; + +$claims = jwt_decode($token, RSA_PUBLIC_KEY, JWT_ALGO_RS256); + +print_r($claims); +~~~ + +## Why install extjwt-cpp? + +PHP extensions, are by virtue of being C/C++ syntaxes, faster than regular PHP. extjwt is a PHP module which wraps around another C++ library and infuses robustness into the PHP userland. Although it is possible to craft high-quality idiomatic PHP solutions like [php-jwt](https://github.com/firebase/php-jwt), extjwt is more performant. + +## Contributing -## Note +Consider buying me a coffee if you appreciate the offerings of project. -The extjwt-cpp extension is merely a wrapper around a C++ package. Because PHP has robust OpenSSL bindings - it is possible to use one of either a tailor-made or off-shelf PHP userland implementation of JWT encoding-decoding software (tools like [php-jwt](https://github.com/firebase/php-jwt) come to mind). \ No newline at end of file +Buy Me A Coffee diff --git a/extjwt.cpp b/extjwt.cpp index 3bbcdfb..38b89c2 100644 --- a/extjwt.cpp +++ b/extjwt.cpp @@ -1,3 +1,9 @@ +/** + * @file extjwt.cpp + * @author Lochemem Bruno Michael (lochbm@gmail.com) + * @brief + * @version 0.1.0 + */ #include "php_extjwt.h" #include "jwt.cpp" @@ -7,6 +13,14 @@ zend_class_entry *jwt_exception_ce; +/** + * @brief + * + * @tparam L + * @param algo + * @return true + * @return false + */ template auto algoIsValid(L algo) -> bool { @@ -15,6 +29,12 @@ auto algoIsValid(L algo) -> bool case JWT_ALGO_HS256: case JWT_ALGO_HS384: case JWT_ALGO_HS512: + case JWT_ALGO_RS256: + case JWT_ALGO_RS384: + case JWT_ALGO_RS512: + case JWT_ALGO_PS256: + case JWT_ALGO_PS384: + case JWT_ALGO_PS512: return true; break; @@ -24,22 +44,43 @@ auto algoIsValid(L algo) -> bool } } +/** + * @brief + * + * @tparam S + * @param str + * @return S + */ +template +auto ucFirst(const S &str) -> S +{ + string fst(""); + fst += toupper(str.at(0)); + fst += str.substr(1); + + return fst; +} + +/* {{{ proto string jwt_encode(string secret, array claims, int algorithm) + Returns a JSON Web Token */ PHP_FUNCTION(jwt_encode) { zend_string *secret; - long algo; + long algo = JWT_ALGO_HS256; // default JWT encoding algorithm zval *claims; zend_string *key; zval *claim; strmap jwtClaims; - ZEND_PARSE_PARAMETERS_START(0, 3) + ZEND_PARSE_PARAMETERS_START(2, 3) + Z_PARAM_ARRAY(claims) Z_PARAM_STR(secret) + Z_PARAM_OPTIONAL Z_PARAM_LONG(algo) - Z_PARAM_ARRAY(claims) ZEND_PARSE_PARAMETERS_END(); + // throw a PHP exception if one of either the payload or secret is empty if (zend_hash_num_elements(HASH_OF(claims)) == 0 || ZSTR_LEN(secret) == 0) { @@ -50,6 +91,7 @@ PHP_FUNCTION(jwt_encode) RETURN_NULL(); } + // throw a PHP exception if the algorithm is invalid if (algoIsValid(algo) == false) { zend_string_release(secret); @@ -59,8 +101,10 @@ PHP_FUNCTION(jwt_encode) RETURN_NULL(); } + // iterate through the list of claims ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(claims), key, claim) { + // JSON encode all sub-array claims if (Z_TYPE_P(claim) == IS_ARRAY) { smart_str jsonData = {0}; @@ -72,6 +116,7 @@ PHP_FUNCTION(jwt_encode) } else { + // coerce claim to string value convert_to_string(claim); jwtClaims[ZSTR_VAL(key)] = Z_STRVAL_P(claim); @@ -86,20 +131,24 @@ PHP_FUNCTION(jwt_encode) zend_string_release(key); zend_string_release(secret); } +/* }}} */ +/* {{{ proto array jwt_decode(string token, string secret, int algorithm) + Returns JSON Web Token payload */ PHP_FUNCTION(jwt_decode) { zend_string *secret; zend_string *token; - long algo; + long algo(JWT_ALGO_HS256); zend_string *key; zval *claim; zval *retval; - ZEND_PARSE_PARAMETERS_START(0, 3) + ZEND_PARSE_PARAMETERS_START(2, 3) Z_PARAM_STR(token) Z_PARAM_STR(secret) + Z_PARAM_OPTIONAL Z_PARAM_LONG(algo) ZEND_PARSE_PARAMETERS_END(); @@ -119,34 +168,40 @@ PHP_FUNCTION(jwt_decode) algo, std::string(ZSTR_VAL(secret))); - array_init(retval); + array_init(retval); // cast the return value as an array zend_string_release(secret); zend_string_release(token); for (auto &iter : claims) { + // store each claim in the retval array as a string add_assoc_string(retval, iter.first.c_str(), iter.second.c_str()); } + // iterate through the retval array and resolve string claims to original uncoerced forms ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(retval), key, claim) { + // copy claim zval to duplicate container + zval dup; + ZVAL_COPY_VALUE(&dup, claim); + if (php_json_decode_ex(claim, - Z_STRVAL_P(claim), - Z_STRLEN_P(claim), + Z_STRVAL_P(&dup), + Z_STRLEN_P(&dup), PHP_JSON_OBJECT_AS_ARRAY, 512) == FAILURE) { - Z_STRVAL_P(claim) == std::string("1").c_str() ? add_assoc_bool(retval, - ZSTR_VAL(key), - 1) - : Z_STRLEN_P(claim) == 0 ? add_assoc_bool(retval, + Z_STRVAL_P(&dup) == std::string("1").c_str() ? add_assoc_bool(retval, + ZSTR_VAL(key), + 1) + : Z_STRLEN_P(&dup) == 0 ? add_assoc_bool(retval, + ZSTR_VAL(key), + 0) + : add_assoc_string(retval, ZSTR_VAL(key), - 0) - : add_assoc_string(retval, - ZSTR_VAL(key), - Z_STRVAL_P(claim)); + Z_STRVAL_P(&dup)); } } ZEND_HASH_FOREACH_END(); @@ -155,14 +210,16 @@ PHP_FUNCTION(jwt_decode) } catch (const std::exception &exp) { + // convert C++ exception to PHP exception zend_string_release(secret); zend_string_release(token); zend_throw_exception(jwt_exception_ce, - exp.what(), + ucFirst(exp.what()).c_str(), 0 TSRMLS_CC); RETURN_NULL(); } } +/* }}} */ PHP_RINIT_FUNCTION(extjwt) { @@ -178,7 +235,7 @@ PHP_MINFO_FUNCTION(extjwt) php_info_print_table_start(); php_info_print_table_header(2, "extjwt support", "enabled"); php_info_print_table_header(2, "extjwt version", PHP_EXTJWT_EXTVER); - php_info_print_table_header(2, "supported algorithms", "HS256, HS384, HS512"); + php_info_print_table_header(2, "supported algorithms", "HS256, HS384, HS512 PS256 PS384 PS512 RS256 RS384 RS512"); php_info_print_table_end(); } @@ -196,23 +253,30 @@ PHP_MINIT_FUNCTION(extjwt) &ce, zend_exception_get_default(TSRMLS_C)); #endif + // register JWT algorithms as constants REGISTER_LONG_CONSTANT("JWT_ALGO_HS256", JWT_ALGO_HS256, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("JWT_ALGO_HS384", JWT_ALGO_HS384, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("JWT_ALGO_HS512", JWT_ALGO_HS512, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("JWT_ALGO_RS256", JWT_ALGO_RS256, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("JWT_ALGO_RS384", JWT_ALGO_RS384, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("JWT_ALGO_RS512", JWT_ALGO_RS512, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("JWT_ALGO_PS256", JWT_ALGO_PS256, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("JWT_ALGO_PS384", JWT_ALGO_PS384, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("JWT_ALGO_PS512", JWT_ALGO_PS512, CONST_CS | CONST_PERSISTENT); return SUCCESS; } ZEND_BEGIN_ARG_INFO_EX(arginfo_jwt_encode, 0, 0, 3) +ZEND_ARG_ARRAY_INFO(0, claims, 0) ZEND_ARG_INFO(0, secret) -ZEND_ARG_INFO(0, algo) -ZEND_ARG_INFO(0, claims) +ZEND_ARG_INFO(0, algorithm) ZEND_END_ARG_INFO(); ZEND_BEGIN_ARG_INFO_EX(arginfo_jwt_decode, 0, 0, 3) ZEND_ARG_INFO(0, token) ZEND_ARG_INFO(0, secret) -ZEND_ARG_INFO(0, algo) +ZEND_ARG_INFO(0, algorithm) ZEND_END_ARG_INFO(); static const zend_function_entry extjwt_functions[] = { @@ -237,4 +301,4 @@ extern "C" { ZEND_GET_MODULE(extjwt) } -#endif \ No newline at end of file +#endif diff --git a/jwt.cpp b/jwt.cpp index 63b10d0..382e4ff 100644 --- a/jwt.cpp +++ b/jwt.cpp @@ -1,10 +1,28 @@ +/** + * @file jwt.cpp + * @author Lochemem Bruno Michael (lochbm@gmail.com) + * @brief + * @version 0.1.0 + */ #include #include "jwt.h" using namespace std; using namespace jwt; + using timepoint = chrono::time_point; +/** + * @brief + * + * @tparam B + * @tparam L + * @tparam S + * @param obj jwt::builder object + * @param algo jwt algorithm + * @param S jwt secret key + * @return string + */ template auto signJwt(B obj, L algo, const S &secret) -> S { @@ -13,6 +31,7 @@ auto signJwt(B obj, L algo, const S &secret) -> S switch (algo) { case JWT_ALGO_HS256: + default: token += obj.sign(algorithm::hs256{secret}); break; @@ -23,14 +42,48 @@ auto signJwt(B obj, L algo, const S &secret) -> S case JWT_ALGO_HS512: token += obj.sign(algorithm::hs512{secret}); break; + + case JWT_ALGO_PS256: + token += obj.sign(algorithm::ps256("", secret, "", "")); + break; + + case JWT_ALGO_PS384: + token += obj.sign(algorithm::ps384("", secret, "", "")); + break; + + case JWT_ALGO_PS512: + token += obj.sign(algorithm::ps512("", secret, "", "")); + break; + + case JWT_ALGO_RS256: + token += obj.sign(algorithm::rs256("", secret, "", "")); + break; + + case JWT_ALGO_RS384: + token += obj.sign(algorithm::rs384("", secret, "", "")); + break; + + case JWT_ALGO_RS512: + token += obj.sign(algorithm::rs512("", secret, "", "")); + break; } return token; } +/** + * @brief + * + * @tparam B + * @tparam S + * @param obj + * @param claims + * @return B + */ template auto addClaims(B obj, S claims) -> B { + // convert string to C++ time auto strToTime = [](const string &timeval) { const chrono::system_clock::duration timeT = chrono::seconds{stoi(timeval)}; const timepoint duration(timeT); @@ -61,6 +114,17 @@ auto addClaims(B obj, S claims) -> B return obj; } +/** + * @brief + * + * @tparam S + * @tparam L + * @tparam I + * @param secret + * @param algo + * @param claims + * @return S + */ template auto jwtEncode(const S &secret, L algo, I claims) -> S { @@ -72,6 +136,16 @@ auto jwtEncode(const S &secret, L algo, I claims) -> S secret); } +/** + * @brief + * + * @tparam S + * @tparam L + * @param token + * @param algo + * @param secret + * @return strmap + */ template auto jwtDecode(const S &token, L algo, const S &secret) -> strmap { @@ -92,6 +166,30 @@ auto jwtDecode(const S &token, L algo, const S &secret) -> strmap case JWT_ALGO_HS512: verifier.allow_algorithm(algorithm::hs512{secret}); break; + + case JWT_ALGO_RS256: + verifier.allow_algorithm(algorithm::rs256(secret, "", "", "")); + break; + + case JWT_ALGO_RS384: + verifier.allow_algorithm(algorithm::rs384(secret, "", "", "")); + break; + + case JWT_ALGO_RS512: + verifier.allow_algorithm(algorithm::rs512(secret, "", "", "")); + break; + + case JWT_ALGO_PS256: + verifier.allow_algorithm(algorithm::ps256(secret, "", "", "")); + break; + + case JWT_ALGO_PS384: + verifier.allow_algorithm(algorithm::ps384(secret, "", "", "")); + break; + + case JWT_ALGO_PS512: + verifier.allow_algorithm(algorithm::ps512(secret, "", "", "")); + break; } verifier.verify(decoded); @@ -102,4 +200,4 @@ auto jwtDecode(const S &token, L algo, const S &secret) -> strmap } return claims; -} \ No newline at end of file +} diff --git a/jwt.h b/jwt.h index d2d51fc..05f4acf 100644 --- a/jwt.h +++ b/jwt.h @@ -1,15 +1,29 @@ +/** + * @file jwt.h + * @author Lochemem Bruno Michael (lochbm@gmail.com) + * @brief + * @version 0.1.0 + */ #ifndef JWT_H #define JWT_H #include #include #include +#include using namespace std; #define JWT_ALGO_HS256 1 #define JWT_ALGO_HS384 2 #define JWT_ALGO_HS512 3 +#define JWT_ALGO_RS256 4 +#define JWT_ALGO_RS384 5 +#define JWT_ALGO_RS512 6 +#define JWT_ALGO_PS256 7 +#define JWT_ALGO_PS384 8 +#define JWT_ALGO_PS512 9 + #define JWT_CLAIM_REPRESENT \ map \ { \ @@ -18,4 +32,4 @@ using namespace std; typedef map strmap; -#endif /* JWT_H */ \ No newline at end of file +#endif /* JWT_H */ diff --git a/php_extjwt.h b/php_extjwt.h index 4e35b6d..246324b 100644 --- a/php_extjwt.h +++ b/php_extjwt.h @@ -1,3 +1,9 @@ +/** + * @file php_extjwt.h + * @author Lochemem Bruno Michael (lochbm@gmail.com) + * @brief + * @version 0.1.0 + */ #ifndef PHP_EXTJWT_H #define PHP_EXTJWT_H @@ -20,4 +26,4 @@ extern "C" extern zend_module_entry extjwt_module_entry; #define phpext_extjwt_ptr &extjwt_module_entry -#endif /* PHP_EXTJWT_H */ \ No newline at end of file +#endif /* PHP_EXTJWT_H */ diff --git a/tests/002.phpt b/tests/002.phpt deleted file mode 100644 index 0a594fa..0000000 --- a/tests/002.phpt +++ /dev/null @@ -1,10 +0,0 @@ ---TEST-- -jwt_encode function outputs JSON Web Token ---FILE-- - ---EXPECT-- -string \ No newline at end of file diff --git a/tests/0021.phpt b/tests/0021.phpt deleted file mode 100644 index 2b338df..0000000 --- a/tests/0021.phpt +++ /dev/null @@ -1,14 +0,0 @@ ---TEST-- -jwt_encode throws JwtException when invalid algorithm is detected ---FILE-- -getMessage(); -} -?> ---EXPECT-- -Invalid algorithm detected \ No newline at end of file diff --git a/tests/0022.phpt b/tests/0022.phpt deleted file mode 100644 index 465eccf..0000000 --- a/tests/0022.phpt +++ /dev/null @@ -1,14 +0,0 @@ ---TEST-- -jwt_encode throws ExtJwtException when a parameter is empty ---FILE-- -getMessage(); -} -?> ---EXPECT-- -Argument(s) cannot be empty \ No newline at end of file diff --git a/tests/0023.phpt b/tests/0023.phpt deleted file mode 100644 index 26748b1..0000000 --- a/tests/0023.phpt +++ /dev/null @@ -1,16 +0,0 @@ ---TEST-- -jwt_encode encodes multi-dimensional array data ---FILE-- - ---EXPECT-- -string \ No newline at end of file diff --git a/tests/003.phpt b/tests/003.phpt deleted file mode 100644 index 4eb4a7a..0000000 --- a/tests/003.phpt +++ /dev/null @@ -1,13 +0,0 @@ ---TEST-- -jwt_decode outputs JWT claims as hashtable ---FILE-- - ---EXPECT-- -array \ No newline at end of file diff --git a/tests/0031.phpt b/tests/0031.phpt deleted file mode 100644 index 714ec68..0000000 --- a/tests/0031.phpt +++ /dev/null @@ -1,16 +0,0 @@ ---TEST-- -jwt_decode throws JwtException when invalid algorithm is detected ---FILE-- -getMessage(); -} -?> ---EXPECT-- -Invalid algorithm detected \ No newline at end of file diff --git a/tests/0032.phpt b/tests/0032.phpt deleted file mode 100644 index 7f6d8be..0000000 --- a/tests/0032.phpt +++ /dev/null @@ -1,12 +0,0 @@ ---TEST-- -jwt_decode throws ExtJwtException when a parameter is empty ---FILE-- -getMessage(); -} -?> ---EXPECT-- -invalid token supplied \ No newline at end of file diff --git a/tests/0033.phpt b/tests/0033.phpt deleted file mode 100644 index ab01083..0000000 --- a/tests/0033.phpt +++ /dev/null @@ -1,12 +0,0 @@ ---TEST-- -jwt_decode decodes multi-dimensional array data ---FILE-- - ---EXPECT-- -array \ No newline at end of file diff --git a/tests/0034.phpt b/tests/0034.phpt deleted file mode 100644 index 58ad51a..0000000 --- a/tests/0034.phpt +++ /dev/null @@ -1,23 +0,0 @@ ---TEST-- -jwt_decode decodes array with non-string values intact ---FILE-- - false, - 'cpp_ver' => 11, - 'pi' => 3.141 -]); - -var_dump(jwt_decode($jwt, TOKEN_SECRET, JWT_ALGO_HS256)); -?> ---EXPECT-- -array(3) { - ["cpp_ver"]=> - int(11) - ["hasInsta"]=> - bool(false) - ["pi"]=> - float(3.141) -} \ No newline at end of file diff --git a/tests/004.phpt b/tests/004.phpt deleted file mode 100644 index 7527893..0000000 --- a/tests/004.phpt +++ /dev/null @@ -1,14 +0,0 @@ ---TEST-- -jwt_decode throws ExtJwtException when decode error is encountered ---FILE-- -getMessage(); -} -?> ---EXPECT-- -invalid token supplied \ No newline at end of file diff --git a/tests/005.phpt b/tests/005.phpt deleted file mode 100644 index 7eafb98..0000000 --- a/tests/005.phpt +++ /dev/null @@ -1,19 +0,0 @@ ---TEST-- -jwt_decode throws ExtJwtException when token expires ---FILE-- - 1568703793, - 'exp' => 1568703798 - ]); - sleep(10); - var_dump(jwt_decode($token, TOKEN_SECRET, JWT_ALGO_HS256)); -} catch (ExtJwtException $exp) { - echo $exp->getMessage(); -} -?> ---EXPECT-- -token verification failed: token expired \ No newline at end of file diff --git a/tests/001.phpt b/tests/check.phpt similarity index 100% rename from tests/001.phpt rename to tests/check.phpt diff --git a/tests/constants.php b/tests/constants.php index 062ad07..45d2e86 100644 --- a/tests/constants.php +++ b/tests/constants.php @@ -1,13 +1,71 @@ '@ace411', 'twitter' => '@agiroLoki' ]; +/** + * @var array TOKEN_CLAIMS_MULTIDIM + */ const TOKEN_CLAIMS_MULTIDIM = [ 'social' => TOKEN_CLAIMS, 'location' => 'kampala' -]; \ No newline at end of file +]; + +/** + * @var string RSA_PUBLIC_KEY + */ +const RSA_PUBLIC_KEY = <<<'KEY' +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuGbXWiK3dQTyCbX5xdE4 +yCuYp0AF2d15Qq1JSXT/lx8CEcXb9RbDddl8jGDv+spi5qPa8qEHiK7FwV2KpRE9 +83wGPnYsAm9BxLFb4YrLYcDFOIGULuk2FtrPS512Qea1bXASuvYXEpQNpGbnTGVs +WXI9C+yjHztqyL2h8P6mlThPY9E9ue2fCqdgixfTFIF9Dm4SLHbphUS2iw7w1JgT +69s7of9+I9l5lsJ9cozf1rxrXX4V1u/SotUuNB3Fp8oB4C1fLBEhSlMcUJirz1E8 +AziMCxS+VrRPDM+zfvpIJg3JljAh3PJHDiLu902v9w+Iplu1WyoB2aPfitxEhRN0 +YwIDAQAB +-----END PUBLIC KEY----- +KEY; + +/** + * @var string RSA_PRIVATE_KEY + */ +const RSA_PRIVATE_KEY = <<<'KEY' +-----BEGIN PRIVATE KEY----- +MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQC4ZtdaIrd1BPIJ +tfnF0TjIK5inQAXZ3XlCrUlJdP+XHwIRxdv1FsN12XyMYO/6ymLmo9ryoQeIrsXB +XYqlET3zfAY+diwCb0HEsVvhisthwMU4gZQu6TYW2s9LnXZB5rVtcBK69hcSlA2k +ZudMZWxZcj0L7KMfO2rIvaHw/qaVOE9j0T257Z8Kp2CLF9MUgX0ObhIsdumFRLaL +DvDUmBPr2zuh/34j2XmWwn1yjN/WvGtdfhXW79Ki1S40HcWnygHgLV8sESFKUxxQ +mKvPUTwDOIwLFL5WtE8Mz7N++kgmDcmWMCHc8kcOIu73Ta/3D4imW7VbKgHZo9+K +3ESFE3RjAgMBAAECggEBAJTEIyjMqUT24G2FKiS1TiHvShBkTlQdoR5xvpZMlYbN +tVWxUmrAGqCQ/TIjYnfpnzCDMLhdwT48Ab6mQJw69MfiXwc1PvwX1e9hRscGul36 +ryGPKIVQEBsQG/zc4/L2tZe8ut+qeaK7XuYrPp8bk/X1e9qK5m7j+JpKosNSLgJj +NIbYsBkG2Mlq671irKYj2hVZeaBQmWmZxK4fw0Istz2WfN5nUKUeJhTwpR+JLUg4 +ELYYoB7EO0Cej9UBG30hbgu4RyXA+VbptJ+H042K5QJROUbtnLWuuWosZ5ATldwO +u03dIXL0SH0ao5NcWBzxU4F2sBXZRGP2x/jiSLHcqoECgYEA4qD7mXQpu1b8XO8U +6abpKloJCatSAHzjgdR2eRDRx5PMvloipfwqA77pnbjTUFajqWQgOXsDTCjcdQui +wf5XAaWu+TeAVTytLQbSiTsBhrnoqVrr3RoyDQmdnwHT8aCMouOgcC5thP9vQ8Us +rVdjvRRbnJpg3BeSNimH+u9AHgsCgYEA0EzcbOltCWPHRAY7B3Ge/AKBjBQr86Kv +TdpTlxePBDVIlH+BM6oct2gaSZZoHbqPjbq5v7yf0fKVcXE4bSVgqfDJ/sZQu9Lp +PTeV7wkk0OsAMKk7QukEpPno5q6tOTNnFecpUhVLLlqbfqkB2baYYwLJR3IRzboJ +FQbLY93E8gkCgYB+zlC5VlQbbNqcLXJoImqItgQkkuW5PCgYdwcrSov2ve5r/Acz +FNt1aRdSlx4176R3nXyibQA1Vw+ztiUFowiP9WLoM3PtPZwwe4bGHmwGNHPIfwVG +m+exf9XgKKespYbLhc45tuC08DATnXoYK7O1EnUINSFJRS8cezSI5eHcbQKBgQDC +PgqHXZ2aVftqCc1eAaxaIRQhRmY+CgUjumaczRFGwVFveP9I6Gdi+Kca3DE3F9Pq +PKgejo0SwP5vDT+rOGHN14bmGJUMsX9i4MTmZUZ5s8s3lXh3ysfT+GAhTd6nKrIE +kM3Nh6HWFhROptfc6BNusRh1kX/cspDplK5x8EpJ0QKBgQDWFg6S2je0KtbV5PYe +RultUEe2C0jYMDQx+JYxbPmtcopvZQrFEur3WKVuLy5UAy7EBvwMnZwIG7OOohJb +vkSpADK6VPn9lbqq7O8cTedEHttm6otmLt8ZyEl3hZMaL3hbuRj6ysjmoFKx6CrX +rK0/Ikt5ybqUzKCMJZg2VKGTxg== +-----END PRIVATE KEY----- +KEY; diff --git a/tests/decode/decode_hs.phpt b/tests/decode/decode_hs.phpt new file mode 100644 index 0000000..b82f02b --- /dev/null +++ b/tests/decode/decode_hs.phpt @@ -0,0 +1,29 @@ +--TEST-- +jwt_decode decodes JWTs signed with algorithms prefixed with HS +--FILE-- + +--EXPECT-- +Array +( + [github] => @ace411 + [twitter] => @agiroLoki +) \ No newline at end of file diff --git a/tests/decode/decode_multidimensional.phpt b/tests/decode/decode_multidimensional.phpt new file mode 100644 index 0000000..80b6e51 --- /dev/null +++ b/tests/decode/decode_multidimensional.phpt @@ -0,0 +1,21 @@ +--TEST-- +jwt_decode decodes token with multi-dimensional array data +--FILE-- + +--EXPECT-- +Array +( + [location] => kampala + [social] => Array + ( + [github] => @ace411 + [twitter] => @agiroLoki + ) + +) \ No newline at end of file diff --git a/tests/decode/decode_ps.phpt b/tests/decode/decode_ps.phpt new file mode 100644 index 0000000..7fc312a --- /dev/null +++ b/tests/decode/decode_ps.phpt @@ -0,0 +1,29 @@ +--TEST-- +jwt_decode decodes JWTs signed with algorithms prefixed with PS +--FILE-- + +--EXPECT-- +Array +( + [github] => @ace411 + [twitter] => @agiroLoki +) \ No newline at end of file diff --git a/tests/decode/decode_rs.phpt b/tests/decode/decode_rs.phpt new file mode 100644 index 0000000..f991d23 --- /dev/null +++ b/tests/decode/decode_rs.phpt @@ -0,0 +1,29 @@ +--TEST-- +jwt_decode decodes JWTs signed with algorithms prefixed with RS +--FILE-- + +--EXPECT-- +Array +( + [github] => @ace411 + [twitter] => @agiroLoki +) \ No newline at end of file diff --git a/tests/encode/encode_hs.phpt b/tests/encode/encode_hs.phpt new file mode 100644 index 0000000..c8bac4b --- /dev/null +++ b/tests/encode/encode_hs.phpt @@ -0,0 +1,23 @@ +--TEST-- +jwt_encode signs JWTs with algorithms prefixed with HS +--FILE-- + $encode(JWT_ALGO_HS256), + 'hs384' => $encode(JWT_ALGO_HS384), + 'hs512' => $encode(JWT_ALGO_HS512) +]; + +print_r($tokens); +?> +--EXPECT-- +Array +( + [hs256] => eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXUyJ9.eyJnaXRodWIiOiJAYWNlNDExIiwidHdpdHRlciI6IkBhZ2lyb0xva2kifQ.sxRKwGM5W8hirRvWXpofT1_SoRQvUnAfynWs39ZKFCY + [hs384] => eyJhbGciOiJIUzM4NCIsInR5cCI6IkpXUyJ9.eyJnaXRodWIiOiJAYWNlNDExIiwidHdpdHRlciI6IkBhZ2lyb0xva2kifQ.FQipugiod7bLPCQ757BusDNv50BQZ8LsyBHvXo3X-U43ppjQmsMwV3-qFdnrKvM9 + [hs512] => eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXUyJ9.eyJnaXRodWIiOiJAYWNlNDExIiwidHdpdHRlciI6IkBhZ2lyb0xva2kifQ.-MY1yEGluzqggyoqjAkT0_OxIKfoAun4s1mll0fXyHAlv6FuEqWkrqpC3fwEjI8ri4WBEI83IdJBtBCV2PBW5g +) \ No newline at end of file diff --git a/tests/encode/encode_multidimensional.phpt b/tests/encode/encode_multidimensional.phpt new file mode 100644 index 0000000..211cdaa --- /dev/null +++ b/tests/encode/encode_multidimensional.phpt @@ -0,0 +1,12 @@ +--TEST-- +jwt_encode encodes multi-dimensional array data +--FILE-- + +--EXPECT-- +eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXUyJ9.eyJsb2NhdGlvbiI6ImthbXBhbGEiLCJzb2NpYWwiOiJ7XCJnaXRodWJcIjpcIkBhY2U0MTFcIixcInR3aXR0ZXJcIjpcIkBhZ2lyb0xva2lcIn0ifQ.sHFxpicUZ7IfgXk6Q32bgyJLDT3bL-fqAdG0qU027gI \ No newline at end of file diff --git a/tests/encode/encode_ps.phpt b/tests/encode/encode_ps.phpt new file mode 100644 index 0000000..3f80480 --- /dev/null +++ b/tests/encode/encode_ps.phpt @@ -0,0 +1,24 @@ +--TEST-- +jwt_encode signs JWTs with algorithms prefixed with PS +--FILE-- + $encode(JWT_ALGO_PS256), + 'ps384' => $encode(JWT_ALGO_PS384), + 'ps512' => $encode(JWT_ALGO_PS512) +]; + +$isValid = array_reduce($tokens, function ($acc, $token) { + $acc = preg_match('/([\w\d]*)(.){1}([\w\d\W]*)/', $token) ? true : false; + + return $acc; +}, false); + +echo $isValid ? 'true' : 'false'; +?> +--EXPECT-- +true \ No newline at end of file diff --git a/tests/encode/encode_rs.phpt b/tests/encode/encode_rs.phpt new file mode 100644 index 0000000..152ab37 --- /dev/null +++ b/tests/encode/encode_rs.phpt @@ -0,0 +1,24 @@ +--TEST-- +jwt_encode signs JWTs with algorithms prefixed with RS +--FILE-- + $encode(JWT_ALGO_PS256), + 'rs384' => $encode(JWT_ALGO_PS384), + 'rs512' => $encode(JWT_ALGO_PS512) +]; + +$isValid = array_reduce($tokens, function ($acc, $token) { + $acc = preg_match('/([\w\d]*)(.){1}([\w\d\W]*)/', $token) ? true : false; + + return $acc; +}, false); + +echo $isValid ? 'true' : 'false'; +?> +--EXPECT-- +true \ No newline at end of file diff --git a/tests/exceptions/algo_decode.phpt b/tests/exceptions/algo_decode.phpt new file mode 100644 index 0000000..610a4c7 --- /dev/null +++ b/tests/exceptions/algo_decode.phpt @@ -0,0 +1,16 @@ +--TEST-- +jwt_decode throws ExtJwtException when it encounters an invalid algorithm +--FILE-- +getMessage(); +} +?> +--EXPECT-- +Invalid algorithm detected \ No newline at end of file diff --git a/tests/exceptions/algo_encode.phpt b/tests/exceptions/algo_encode.phpt new file mode 100644 index 0000000..4fdc6ce --- /dev/null +++ b/tests/exceptions/algo_encode.phpt @@ -0,0 +1,14 @@ +--TEST-- +jwt_encode throws an exception when it encounters an invalid algorithm +--FILE-- +getMessage(); +} +?> +--EXPECT-- +Invalid algorithm detected \ No newline at end of file diff --git a/tests/exceptions/algo_mismatch.phpt b/tests/exceptions/algo_mismatch.phpt new file mode 100644 index 0000000..6d0e0ba --- /dev/null +++ b/tests/exceptions/algo_mismatch.phpt @@ -0,0 +1,16 @@ +--TEST-- +jwt_decode throws an exception when algorithm mismatch is encountered +--FILE-- +getMessage(); +} +?> +--EXPECT-- +Token verification failed: wrong algorithm \ No newline at end of file diff --git a/tests/exceptions/empty_decode.phpt b/tests/exceptions/empty_decode.phpt new file mode 100644 index 0000000..40d1be4 --- /dev/null +++ b/tests/exceptions/empty_decode.phpt @@ -0,0 +1,12 @@ +--TEST-- +jwt_decode throws exception whenever empty parameters are detected +--FILE-- +getMessage(); +} +?> +--EXPECT-- +Invalid token supplied \ No newline at end of file diff --git a/tests/exceptions/empty_encode.phpt b/tests/exceptions/empty_encode.phpt new file mode 100644 index 0000000..95b7cc4 --- /dev/null +++ b/tests/exceptions/empty_encode.phpt @@ -0,0 +1,12 @@ +--TEST-- +jwt_encode throws exception whenever empty parameters are detected +--FILE-- +getMessage(); +} +?> +--EXPECT-- +Argument(s) cannot be empty \ No newline at end of file diff --git a/tests/exceptions/expiry.phpt b/tests/exceptions/expiry.phpt new file mode 100644 index 0000000..e99d630 --- /dev/null +++ b/tests/exceptions/expiry.phpt @@ -0,0 +1,21 @@ +--TEST-- +extension throws an exception upon detection of token expiry +--FILE-- + time(), + 'exp' => time() + 5 + ]), TOKEN_SECRET); + + sleep(6); + + print_r(jwt_decode($token, TOKEN_SECRET)); +} catch (ExtJwtException $exp) { + echo $exp->getMessage(); +} +?> +--EXPECT-- +Token verification failed: token expired \ No newline at end of file diff --git a/tests/functions.php b/tests/functions.php new file mode 100644 index 0000000..4b22037 --- /dev/null +++ b/tests/functions.php @@ -0,0 +1,19 @@ + +--EXPECT-- +Array +( + [github] => @ace411 + [twitter] => @agiroLoki +) \ No newline at end of file diff --git a/tests/simple_encode.phpt b/tests/simple_encode.phpt new file mode 100644 index 0000000..147c6ed --- /dev/null +++ b/tests/simple_encode.phpt @@ -0,0 +1,10 @@ +--TEST-- +jwt_encode function outputs JSON Web Token +--FILE-- + +--EXPECT-- +eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXUyJ9.eyJnaXRodWIiOiJAYWNlNDExIiwidHdpdHRlciI6IkBhZ2lyb0xva2kifQ.sxRKwGM5W8hirRvWXpofT1_SoRQvUnAfynWs39ZKFCY \ No newline at end of file