├── .gitignore ├── JWT_AUTH.php ├── License ├── README.md ├── assets ├── css │ └── settings.css └── img │ └── jwticon.png ├── banner-1544x500.png ├── banner-772x250.png ├── icon-128x128.png ├── icon-256x256.png ├── lib ├── JWT_AUTH_Admin.php ├── JWT_AUTH_Options.php ├── JWT_AUTH_Settings_Section.php ├── JWT_AUTH_UserProcessor.php ├── JWT_AUTH_UsersRepo.php └── php-jwt │ ├── Authentication │ └── JWT.php │ ├── Exceptions │ ├── BeforeValidException.php │ ├── ExpiredException.php │ └── SignatureInvalidException.php │ ├── LICENSE │ ├── README.md │ ├── composer.json │ ├── package.xml │ ├── phpunit.xml.dist │ ├── run-tests.sh │ └── tests │ ├── JWTTest.php │ ├── autoload.php.dist │ └── bootstrap.php ├── readme.txt └── templates └── settings.php /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store -------------------------------------------------------------------------------- /JWT_AUTH.php: -------------------------------------------------------------------------------- 1 | Settings'; 86 | array_unshift($links, $settings_link); 87 | 88 | return $links; 89 | } 90 | 91 | public static function getPluginDirUrl() 92 | { 93 | return plugin_dir_url( __FILE__ ); 94 | } 95 | 96 | } 97 | 98 | JWT_AUTH::Init(); 99 | -------------------------------------------------------------------------------- /License: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Auth0 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DEPRECATED - This library is no longer maintained/supported. 2 | 3 | This library was built as an example for a session in a PHP conference, and is no longer supported. 4 | 5 | ![](https://raw.githubusercontent.com/auth0/wp-jwt-auth/master/banner-1544x500.png) 6 | 7 | #Wordpress JWT Authentication 8 | 9 | Authenticate your APIs with JWT easily. 10 | 11 | ##Configuration 12 | - **Aud**: represents the client id which the JWT was sent. 13 | - **Secret**: used to verify the JWT signature 14 | - **Base64 Secret Encoded**: it must be active if the secret is base64 encoded and needs to be decoded before checkig the signature. 15 | - **User Property**: is the property which much match with the JWT attribute to determine the user. 16 | - **JWT Attribute**: should match the User Property to determine the user. 17 | 18 | ##How it works 19 | 20 | This plugin will check the headers of the requests and if there is an `Authorization` header will try to log in a user that matches. So it is as easy to check for the current user to authenticate users in your own API. 21 | 22 | Also, it provides support for the following plugins: 23 | - http://wp-api.org/ 24 | - http://docs.woothemes.com/document/woocommerce-rest-api/ 25 | 26 | And any other that extends `Wp Rest Api` like http://tweichart.github.io/JSON-API-for-BuddyPress/ for BuddyPress. 27 | 28 | 29 | ##Overriding the User Repository logic 30 | The user repository is the responsible of retriving the user based on the JWT. By default, it looks in the user database to match the *User Property* and the *JWT Attribute*. 31 | 32 | If you need to override the way the user matching is made (ie: you need to look to another table in the database) you can create your own User Repostory and match the user as you need. 33 | 34 | To accomplish this, you need to add a filter: 35 | 36 | ``` 37 | add_filter( 'wp_jwt_auth_get_user', array( __CLASS__, 'get_user' ),10); 38 | ``` 39 | 40 | To see an example, check the [UsersRepo](https://github.com/auth0/wp-jwt-auth/blob/master/lib/JWT_AUTH_UsersRepo.php). 41 | 42 | > When the plugin is using a User Repository the *User Property* and *JWT Property* settings are ignored. 43 | -------------------------------------------------------------------------------- /assets/css/settings.css: -------------------------------------------------------------------------------- 1 | input[type=text] { 2 | width: 70%; 3 | } 4 | 5 | textarea { 6 | width: 70%; 7 | height: 100px; 8 | } 9 | 10 | span.description { 11 | width: 70%; 12 | display: inline-block; 13 | } -------------------------------------------------------------------------------- /assets/img/jwticon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/auth0/wp-jwt-auth/e9dd7274e71eed35df20c3474942809f9e61352f/assets/img/jwticon.png -------------------------------------------------------------------------------- /banner-1544x500.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/auth0/wp-jwt-auth/e9dd7274e71eed35df20c3474942809f9e61352f/banner-1544x500.png -------------------------------------------------------------------------------- /banner-772x250.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/auth0/wp-jwt-auth/e9dd7274e71eed35df20c3474942809f9e61352f/banner-772x250.png -------------------------------------------------------------------------------- /icon-128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/auth0/wp-jwt-auth/e9dd7274e71eed35df20c3474942809f9e61352f/icon-128x128.png -------------------------------------------------------------------------------- /icon-256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/auth0/wp-jwt-auth/e9dd7274e71eed35df20c3474942809f9e61352f/icon-256x256.png -------------------------------------------------------------------------------- /lib/JWT_AUTH_Admin.php: -------------------------------------------------------------------------------- 1 | $setting['id']) 37 | ); 38 | } 39 | } 40 | 41 | public static function render_settings_description(){ 42 | 43 | } 44 | 45 | public static function init_admin(){ 46 | 47 | /* ------------------------- BASIC ------------------------- */ 48 | 49 | self::init_option_section('Settings', array( 50 | 51 | array('id' => 'jwt_auth_aud', 'name' => 'Aud', 'function' => 'render_aud'), 52 | array('id' => 'jwt_auth_secret', 'name' => 'Secret', 'function' => 'render_secret'), 53 | array('id' => 'jwt_auth_secret_base64_encoded', 'name' => 'Base64 Secret encoded', 'function' => 'render_secret_base64_encoded'), 54 | array('id' => 'jwt_auth_user_property', 'name' => 'User Property', 'function' => 'render_user_property'), 55 | array('id' => 'jwt_auth_jwt_attribute', 'name' => 'JWT Attribute', 'function' => 'render_jwt_attribute'), 56 | 57 | )); 58 | 59 | register_setting(JWT_AUTH_Options::OPTIONS_NAME, JWT_AUTH_Options::OPTIONS_NAME, array(__CLASS__, 'input_validator')); 60 | } 61 | 62 | 63 | public static function render_aud(){ 64 | $v = JWT_AUTH_Options::get( 'aud' ); 65 | echo ''; 66 | echo '
' . __('JWT Audience (aud) represents the client id to which it is intended.', JWT_AUTH_LANG) . ''; 67 | } 68 | public static function render_secret(){ 69 | $v = JWT_AUTH_Options::get( 'secret' ); 70 | echo ''; 71 | echo '
' . __('Secret value to verify the JWT signature.', JWT_AUTH_LANG) . ''; 72 | } 73 | public static function render_secret_base64_encoded(){ 74 | $v = JWT_AUTH_Options::get( 'secret_base64_encoded' ); 75 | echo ''; 76 | echo ''; 77 | echo ' '; 78 | echo ''; 79 | echo ''; 80 | } 81 | public static function render_user_property(){ 82 | $v = JWT_AUTH_Options::get( 'user_property' ); 83 | echo ''; 84 | echo '
' . __('WP User property which the plugin should look to find the related user.', JWT_AUTH_LANG) . ''; 85 | } 86 | public static function render_jwt_attribute(){ 87 | $v = JWT_AUTH_Options::get( 'jwt_attribute' ); 88 | echo ''; 89 | echo '
' . __('JWT Attribute the plugin should use to match the users.', JWT_AUTH_LANG) . ''; 90 | } 91 | 92 | public static function render_settings_page(){ 93 | include JWT_AUTH_PLUGIN_DIR . 'templates/settings.php'; 94 | } 95 | 96 | protected static function add_validation_error($error) 97 | { 98 | add_settings_error( 99 | JWT_AUTH_Options::OPTIONS_NAME, 100 | JWT_AUTH_Options::OPTIONS_NAME, 101 | $error, 102 | 'error' 103 | ); 104 | } 105 | 106 | public static function input_validator( $input ){ 107 | 108 | $input['secret_base64_encoded'] = ($input['secret_base64_encoded'] == 1); 109 | 110 | return $input; 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /lib/JWT_AUTH_Options.php: -------------------------------------------------------------------------------- 1 | 1, 37 | 'aud' => '', 38 | 'secret' => '', 39 | 'user_property' => 'id', 40 | 'jwt_attribute' => 'sub', 41 | 'secret_base64_encoded' => false, 42 | ); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /lib/JWT_AUTH_Settings_Section.php: -------------------------------------------------------------------------------- 1 | getMessage(); 75 | return null; 76 | } 77 | 78 | $objuser = self::findUser($token, $authorization); 79 | 80 | if (!$objuser) { 81 | $wp_json_basic_auth_error = 'Invalid user'; 82 | return null; 83 | } 84 | 85 | if ($returnUserObj) { 86 | $user = $objuser; 87 | } 88 | else { 89 | $user = $objuser->ID; 90 | } 91 | } 92 | 93 | $wp_json_basic_auth_error = true; 94 | 95 | return $user; 96 | } 97 | 98 | protected static function decodeJWT($encUser) 99 | { 100 | require_once JWT_AUTH_PLUGIN_DIR . 'lib/php-jwt/Exceptions/BeforeValidException.php'; 101 | require_once JWT_AUTH_PLUGIN_DIR . 'lib/php-jwt/Exceptions/ExpiredException.php'; 102 | require_once JWT_AUTH_PLUGIN_DIR . 'lib/php-jwt/Exceptions/SignatureInvalidException.php'; 103 | require_once JWT_AUTH_PLUGIN_DIR . 'lib/php-jwt/Authentication/JWT.php'; 104 | 105 | $aud = JWT_AUTH_Options::get( 'aud' ); 106 | $secret = JWT_AUTH_Options::get( 'secret' ); 107 | $secret_base64_encoded = JWT_AUTH_Options::get( 'secret_base64_encoded' ); 108 | 109 | if ($secret_base64_encoded) { 110 | $secret = base64_decode(strtr($secret, '-_', '+/')); 111 | } 112 | 113 | try { 114 | // Decode the user 115 | $decodedToken = \JWT::decode($encUser, $secret, ['HS256']); 116 | 117 | // validate that this JWT was made for us 118 | if ($decodedToken->aud != $aud) { 119 | throw new Exception("This token is not intended for us."); 120 | } 121 | } catch(\UnexpectedValueException $e) { 122 | throw new Exception($e->getMessage()); 123 | } 124 | 125 | return $decodedToken; 126 | } 127 | 128 | } 129 | -------------------------------------------------------------------------------- /lib/JWT_AUTH_UsersRepo.php: -------------------------------------------------------------------------------- 1 | $jwt_attribute; 20 | 21 | $sql = 'SELECT u.* 22 | FROM ' . $wpdb->users . ' 23 | WHERE '.$user_property.' = %s'; 24 | 25 | $userRow = $wpdb->get_row($wpdb->prepare($sql, $id)); 26 | 27 | if (is_null($userRow)) { 28 | return null; 29 | }elseif($userRow instanceof WP_Error ) { 30 | self::insertAuth0Error('findAuth0User',$userRow); 31 | return null; 32 | } 33 | $user = new WP_User(); 34 | $user->init($userRow); 35 | return $user; 36 | 37 | } 38 | 39 | 40 | } 41 | -------------------------------------------------------------------------------- /lib/php-jwt/Authentication/JWT.php: -------------------------------------------------------------------------------- 1 | 12 | * @author Anant Narayanan 13 | * @license http://opensource.org/licenses/BSD-3-Clause 3-clause BSD 14 | * @link https://github.com/firebase/php-jwt 15 | */ 16 | class JWT 17 | { 18 | public static $supported_algs = array( 19 | 'HS256' => array('hash_hmac', 'SHA256'), 20 | 'HS512' => array('hash_hmac', 'SHA512'), 21 | 'HS384' => array('hash_hmac', 'SHA384'), 22 | 'RS256' => array('openssl', 'SHA256'), 23 | ); 24 | 25 | /** 26 | * Decodes a JWT string into a PHP object. 27 | * 28 | * @param string $jwt The JWT 29 | * @param string|Array|null $key The secret key, or map of keys 30 | * @param Array $allowed_algs List of supported verification algorithms 31 | * 32 | * @return object The JWT's payload as a PHP object 33 | * 34 | * @throws DomainException Algorithm was not provided 35 | * @throws UnexpectedValueException Provided JWT was invalid 36 | * @throws SignatureInvalidException Provided JWT was invalid because the signature verification failed 37 | * @throws BeforeValidException Provided JWT is trying to be used before it's eligible as defined by 'nbf' 38 | * @throws BeforeValidException Provided JWT is trying to be used before it's been created as defined by 'iat' 39 | * @throws ExpiredException Provided JWT has since expired, as defined by the 'exp' claim 40 | * 41 | * @uses jsonDecode 42 | * @uses urlsafeB64Decode 43 | */ 44 | public static function decode($jwt, $key = null, $allowed_algs = array()) 45 | { 46 | $tks = explode('.', $jwt); 47 | if (count($tks) != 3) { 48 | throw new UnexpectedValueException('Wrong number of segments'); 49 | } 50 | list($headb64, $bodyb64, $cryptob64) = $tks; 51 | if (null === ($header = JWT::jsonDecode(JWT::urlsafeB64Decode($headb64)))) { 52 | throw new UnexpectedValueException('Invalid header encoding'); 53 | } 54 | if (null === $payload = JWT::jsonDecode(JWT::urlsafeB64Decode($bodyb64))) { 55 | throw new UnexpectedValueException('Invalid claims encoding'); 56 | } 57 | $sig = JWT::urlsafeB64Decode($cryptob64); 58 | if (isset($key)) { 59 | if (empty($header->alg)) { 60 | throw new DomainException('Empty algorithm'); 61 | } 62 | if (empty(self::$supported_algs[$header->alg])) { 63 | throw new DomainException('Algorithm not supported'); 64 | } 65 | if (!is_array($allowed_algs) || !in_array($header->alg, $allowed_algs)) { 66 | throw new DomainException('Algorithm not allowed'); 67 | } 68 | if (is_array($key)) { 69 | if (isset($header->kid)) { 70 | $key = $key[$header->kid]; 71 | } else { 72 | throw new DomainException('"kid" empty, unable to lookup correct key'); 73 | } 74 | } 75 | 76 | // Check the signature 77 | if (!JWT::verify("$headb64.$bodyb64", $sig, $key, $header->alg)) { 78 | throw new SignatureInvalidException('Signature verification failed'); 79 | } 80 | 81 | // Check if the nbf if it is defined. This is the time that the 82 | // token can actually be used. If it's not yet that time, abort. 83 | if (isset($payload->nbf) && $payload->nbf > time()) { 84 | throw new BeforeValidException( 85 | 'Cannot handle token prior to ' . date(DateTime::ISO8601, $payload->nbf) 86 | ); 87 | } 88 | 89 | // Check that this token has been created before 'now'. This prevents 90 | // using tokens that have been created for later use (and haven't 91 | // correctly used the nbf claim). 92 | if (isset($payload->iat) && $payload->iat > time()) { 93 | throw new BeforeValidException( 94 | 'Cannot handle token prior to ' . date(DateTime::ISO8601, $payload->iat) 95 | ); 96 | } 97 | 98 | // Check if this token has expired. 99 | if (isset($payload->exp) && time() >= $payload->exp) { 100 | throw new ExpiredException('Expired token'); 101 | } 102 | } 103 | 104 | return $payload; 105 | } 106 | 107 | /** 108 | * Converts and signs a PHP object or array into a JWT string. 109 | * 110 | * @param object|array $payload PHP object or array 111 | * @param string $key The secret key 112 | * @param string $alg The signing algorithm. Supported 113 | * algorithms are 'HS256', 'HS384' and 'HS512' 114 | * 115 | * @return string A signed JWT 116 | * @uses jsonEncode 117 | * @uses urlsafeB64Encode 118 | */ 119 | public static function encode($payload, $key, $alg = 'HS256', $keyId = null) 120 | { 121 | $header = array('typ' => 'JWT', 'alg' => $alg); 122 | if ($keyId !== null) { 123 | $header['kid'] = $keyId; 124 | } 125 | $segments = array(); 126 | $segments[] = JWT::urlsafeB64Encode(JWT::jsonEncode($header)); 127 | $segments[] = JWT::urlsafeB64Encode(JWT::jsonEncode($payload)); 128 | $signing_input = implode('.', $segments); 129 | 130 | $signature = JWT::sign($signing_input, $key, $alg); 131 | $segments[] = JWT::urlsafeB64Encode($signature); 132 | 133 | return implode('.', $segments); 134 | } 135 | 136 | /** 137 | * Sign a string with a given key and algorithm. 138 | * 139 | * @param string $msg The message to sign 140 | * @param string|resource $key The secret key 141 | * @param string $alg The signing algorithm. Supported algorithms 142 | * are 'HS256', 'HS384', 'HS512' and 'RS256' 143 | * 144 | * @return string An encrypted message 145 | * @throws DomainException Unsupported algorithm was specified 146 | */ 147 | public static function sign($msg, $key, $alg = 'HS256') 148 | { 149 | if (empty(self::$supported_algs[$alg])) { 150 | throw new DomainException('Algorithm not supported'); 151 | } 152 | list($function, $algorithm) = self::$supported_algs[$alg]; 153 | switch($function) { 154 | case 'hash_hmac': 155 | return hash_hmac($algorithm, $msg, $key, true); 156 | case 'openssl': 157 | $signature = ''; 158 | $success = openssl_sign($msg, $signature, $key, $algorithm); 159 | if (!$success) { 160 | throw new DomainException("OpenSSL unable to sign data"); 161 | } else { 162 | return $signature; 163 | } 164 | } 165 | } 166 | 167 | /** 168 | * Verify a signature with the mesage, key and method. Not all methods 169 | * are symmetric, so we must have a separate verify and sign method. 170 | * @param string $msg the original message 171 | * @param string $signature 172 | * @param string|resource $key for HS*, a string key works. for RS*, must be a resource of an openssl public key 173 | * @param string $alg 174 | * @return bool 175 | * @throws DomainException Invalid Algorithm or OpenSSL failure 176 | */ 177 | private static function verify($msg, $signature, $key, $alg) 178 | { 179 | if (empty(self::$supported_algs[$alg])) { 180 | throw new DomainException('Algorithm not supported'); 181 | } 182 | 183 | list($function, $algorithm) = self::$supported_algs[$alg]; 184 | switch($function) { 185 | case 'openssl': 186 | $success = openssl_verify($msg, $signature, $key, $algorithm); 187 | if (!$success) { 188 | throw new DomainException("OpenSSL unable to verify data: " . openssl_error_string()); 189 | } else { 190 | return $signature; 191 | } 192 | case 'hash_hmac': 193 | default: 194 | $hash = hash_hmac($algorithm, $msg, $key, true); 195 | if (function_exists('hash_equals')) { 196 | return hash_equals($signature, $hash); 197 | } 198 | $len = min(self::safeStrlen($signature), self::safeStrlen($hash)); 199 | 200 | $status = 0; 201 | for ($i = 0; $i < $len; $i++) { 202 | $status |= (ord($signature[$i]) ^ ord($hash[$i])); 203 | } 204 | $status |= (self::safeStrlen($signature) ^ self::safeStrlen($hash)); 205 | 206 | return ($status === 0); 207 | } 208 | } 209 | 210 | /** 211 | * Decode a JSON string into a PHP object. 212 | * 213 | * @param string $input JSON string 214 | * 215 | * @return object Object representation of JSON string 216 | * @throws DomainException Provided string was invalid JSON 217 | */ 218 | public static function jsonDecode($input) 219 | { 220 | if (version_compare(PHP_VERSION, '5.4.0', '>=') && !(defined('JSON_C_VERSION') && PHP_INT_SIZE > 4)) { 221 | /** In PHP >=5.4.0, json_decode() accepts an options parameter, that allows you 222 | * to specify that large ints (like Steam Transaction IDs) should be treated as 223 | * strings, rather than the PHP default behaviour of converting them to floats. 224 | */ 225 | $obj = json_decode($input, false, 512, JSON_BIGINT_AS_STRING); 226 | } else { 227 | /** Not all servers will support that, however, so for older versions we must 228 | * manually detect large ints in the JSON string and quote them (thus converting 229 | *them to strings) before decoding, hence the preg_replace() call. 230 | */ 231 | $max_int_length = strlen((string) PHP_INT_MAX) - 1; 232 | $json_without_bigints = preg_replace('/:\s*(-?\d{'.$max_int_length.',})/', ': "$1"', $input); 233 | $obj = json_decode($json_without_bigints); 234 | } 235 | 236 | if (function_exists('json_last_error') && $errno = json_last_error()) { 237 | JWT::handleJsonError($errno); 238 | } elseif ($obj === null && $input !== 'null') { 239 | throw new DomainException('Null result with non-null input'); 240 | } 241 | return $obj; 242 | } 243 | 244 | /** 245 | * Encode a PHP object into a JSON string. 246 | * 247 | * @param object|array $input A PHP object or array 248 | * 249 | * @return string JSON representation of the PHP object or array 250 | * @throws DomainException Provided object could not be encoded to valid JSON 251 | */ 252 | public static function jsonEncode($input) 253 | { 254 | $json = json_encode($input); 255 | if (function_exists('json_last_error') && $errno = json_last_error()) { 256 | JWT::handleJsonError($errno); 257 | } elseif ($json === 'null' && $input !== null) { 258 | throw new DomainException('Null result with non-null input'); 259 | } 260 | return $json; 261 | } 262 | 263 | /** 264 | * Decode a string with URL-safe Base64. 265 | * 266 | * @param string $input A Base64 encoded string 267 | * 268 | * @return string A decoded string 269 | */ 270 | public static function urlsafeB64Decode($input) 271 | { 272 | $remainder = strlen($input) % 4; 273 | if ($remainder) { 274 | $padlen = 4 - $remainder; 275 | $input .= str_repeat('=', $padlen); 276 | } 277 | return base64_decode(strtr($input, '-_', '+/')); 278 | } 279 | 280 | /** 281 | * Encode a string with URL-safe Base64. 282 | * 283 | * @param string $input The string you want encoded 284 | * 285 | * @return string The base64 encode of what you passed in 286 | */ 287 | public static function urlsafeB64Encode($input) 288 | { 289 | return str_replace('=', '', strtr(base64_encode($input), '+/', '-_')); 290 | } 291 | 292 | /** 293 | * Helper method to create a JSON error. 294 | * 295 | * @param int $errno An error number from json_last_error() 296 | * 297 | * @return void 298 | */ 299 | private static function handleJsonError($errno) 300 | { 301 | $messages = array( 302 | JSON_ERROR_DEPTH => 'Maximum stack depth exceeded', 303 | JSON_ERROR_CTRL_CHAR => 'Unexpected control character found', 304 | JSON_ERROR_SYNTAX => 'Syntax error, malformed JSON' 305 | ); 306 | throw new DomainException( 307 | isset($messages[$errno]) 308 | ? $messages[$errno] 309 | : 'Unknown JSON error: ' . $errno 310 | ); 311 | } 312 | 313 | /** 314 | * Get the number of bytes in cryptographic strings. 315 | * 316 | * @param string 317 | * @return int 318 | */ 319 | private static function safeStrlen($str) 320 | { 321 | if (function_exists('mb_strlen')) { 322 | return mb_strlen($str, '8bit'); 323 | } 324 | return strlen($str); 325 | } 326 | } 327 | -------------------------------------------------------------------------------- /lib/php-jwt/Exceptions/BeforeValidException.php: -------------------------------------------------------------------------------- 1 | "http://example.org", 25 | "aud" => "http://example.com", 26 | "iat" => 1356999524, 27 | "nbf" => 1357000000 28 | ); 29 | 30 | /** 31 | * IMPORTANT: 32 | * You must specify supported algorithms for your application. See 33 | * https://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-40 34 | * for a list of spec-compliant algorithms. 35 | */ 36 | $jwt = JWT::encode($token, $key); 37 | $decoded = JWT::decode($jwt, $key, array('HS256')); 38 | 39 | print_r($decoded); 40 | 41 | /* 42 | NOTE: This will now be an object instead of an associative array. To get 43 | an associative array, you will need to cast it as such: 44 | */ 45 | 46 | $decoded_array = (array) $decoded; 47 | 48 | ?> 49 | ``` 50 | 51 | Changelog 52 | --------- 53 | 54 | #### 2.0.0 / 2015-04-01 55 | - **Note**: It is strongly recommended that you update to > v2.0.0 to address 56 | known security vulnerabilities in prior versions when both symmetric and 57 | asymmetric keys are used together. 58 | - Update signature for `JWT::decode(...)` to require an array of supported 59 | algorithms to use when verifying token signatures. 60 | 61 | 62 | Tests 63 | ----- 64 | Run the tests using phpunit: 65 | 66 | ```bash 67 | $ pear install PHPUnit 68 | $ phpunit --configuration phpunit.xml.dist 69 | PHPUnit 3.7.10 by Sebastian Bergmann. 70 | ..... 71 | Time: 0 seconds, Memory: 2.50Mb 72 | OK (5 tests, 5 assertions) 73 | ``` 74 | 75 | License 76 | ------- 77 | [3-Clause BSD](http://opensource.org/licenses/BSD-3-Clause). 78 | -------------------------------------------------------------------------------- /lib/php-jwt/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "firebase/php-jwt", 3 | "description": "A simple library to encode and decode JSON Web Tokens (JWT) in PHP. Should conform to the current spec.", 4 | "homepage": "https://github.com/firebase/php-jwt", 5 | "authors": [ 6 | { 7 | "name": "Neuman Vong", 8 | "email": "neuman+pear@twilio.com", 9 | "role": "Developer" 10 | }, 11 | { 12 | "name": "Anant Narayanan", 13 | "email": "anant@php.net", 14 | "role": "Developer" 15 | } 16 | ], 17 | "license": "BSD-3-Clause", 18 | "require": { 19 | "php": ">=5.2.0" 20 | }, 21 | "autoload": { 22 | "classmap": ["Authentication/", "Exceptions/"] 23 | }, 24 | "minimum-stability": "dev" 25 | } 26 | -------------------------------------------------------------------------------- /lib/php-jwt/package.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | JWT 7 | pear.php.net 8 | A JWT encoder/decoder. 9 | A JWT encoder/decoder library for PHP. 10 | 11 | Neuman Vong 12 | lcfrs 13 | neuman+pear@twilio.com 14 | yes 15 | 16 | 17 | Anant Narayanan 18 | anant 19 | anant@php.net 20 | yes 21 | 22 | 2015-04-01 23 | 24 | 2.0.0 25 | 2.0.0 26 | 27 | 28 | beta 29 | beta 30 | 31 | BSD 3-Clause License 32 | 33 | Initial release with basic support for JWT encoding, decoding and signature verification. 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 5.1 47 | 48 | 49 | 1.7.0 50 | 51 | 52 | json 53 | 54 | 55 | hash 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 0.1.0 64 | 0.1.0 65 | 66 | 67 | beta 68 | beta 69 | 70 | 2015-04-01 71 | BSD 3-Clause License 72 | 73 | Initial release with basic support for JWT encoding, decoding and signature verification. 74 | 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /lib/php-jwt/phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 14 | 15 | 16 | ./tests 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /lib/php-jwt/run-tests.sh: -------------------------------------------------------------------------------- 1 | 2 | #!/usr/bin/env bash 3 | gpg --fingerprint D8406D0D82947747293778314AA394086372C20A 4 | if [ $? -ne 0 ]; then 5 | echo -e "\033[33mDownloading PGP Public Key...\033[0m" 6 | gpg --recv-keys D8406D0D82947747293778314AA394086372C20A 7 | # Sebastian Bergmann 8 | gpg --fingerprint D8406D0D82947747293778314AA394086372C20A 9 | if [ $? -ne 0 ]; then 10 | echo -e "\033[31mCould not download PGP public key for verification\033[0m" 11 | exit 12 | fi 13 | fi 14 | 15 | # Let's grab the latest release and its signature 16 | if [ ! -f phpunit.phar ]; then 17 | wget https://phar.phpunit.de/phpunit.phar 18 | fi 19 | if [ ! -f phpunit.phar.asc ]; then 20 | wget https://phar.phpunit.de/phpunit.phar.asc 21 | fi 22 | 23 | # Verify before running 24 | gpg --verify phpunit.phar.asc phpunit.phar 25 | if [ $? -eq 0 ]; then 26 | echo 27 | echo -e "\033[33mBegin Unit Testing\033[0m" 28 | # Run the testing suite 29 | php --version 30 | php phpunit.phar --configuration phpunit.xml.dist 31 | else 32 | echo 33 | chmod -x phpunit.phar 34 | mv phpunit.phar /tmp/bad-phpunit.phar 35 | mv phpunit.phar.asc /tmp/bad-phpunit.phar.asc 36 | echo -e "\033[31mSignature did not match! PHPUnit has been moved to /tmp/bad-phpunit.phar\033[0m" 37 | exit 1 38 | fi 39 | -------------------------------------------------------------------------------- /lib/php-jwt/tests/JWTTest.php: -------------------------------------------------------------------------------- 1 | assertEquals(JWT::decode($msg, 'my_key', array('HS256')), 'abc'); 9 | } 10 | 11 | public function testDecodeFromPython() 12 | { 13 | $msg = 'eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9.Iio6aHR0cDovL2FwcGxpY2F0aW9uL2NsaWNreT9ibGFoPTEuMjMmZi5vbz00NTYgQUMwMDAgMTIzIg.E_U8X2YpMT5K1cEiT_3-IvBYfrdIFIeVYeOqre_Z5Cg'; 14 | $this->assertEquals( 15 | JWT::decode($msg, 'my_key', array('HS256')), 16 | '*:http://application/clicky?blah=1.23&f.oo=456 AC000 123' 17 | ); 18 | } 19 | 20 | public function testUrlSafeCharacters() 21 | { 22 | $encoded = JWT::encode('f?', 'a'); 23 | $this->assertEquals('f?', JWT::decode($encoded, 'a', array('HS256'))); 24 | } 25 | 26 | public function testMalformedUtf8StringsFail() 27 | { 28 | $this->setExpectedException('DomainException'); 29 | JWT::encode(pack('c', 128), 'a'); 30 | } 31 | 32 | public function testMalformedJsonThrowsException() 33 | { 34 | $this->setExpectedException('DomainException'); 35 | JWT::jsonDecode('this is not valid JSON string'); 36 | } 37 | 38 | public function testExpiredToken() 39 | { 40 | $this->setExpectedException('ExpiredException'); 41 | $payload = array( 42 | "message" => "abc", 43 | "exp" => time() - 20); // time in the past 44 | $encoded = JWT::encode($payload, 'my_key'); 45 | JWT::decode($encoded, 'my_key', array('HS256')); 46 | } 47 | 48 | public function testBeforeValidTokenWithNbf() 49 | { 50 | $this->setExpectedException('BeforeValidException'); 51 | $payload = array( 52 | "message" => "abc", 53 | "nbf" => time() + 20); // time in the future 54 | $encoded = JWT::encode($payload, 'my_key'); 55 | JWT::decode($encoded, 'my_key', array('HS256')); 56 | } 57 | 58 | public function testBeforeValidTokenWithIat() 59 | { 60 | $this->setExpectedException('BeforeValidException'); 61 | $payload = array( 62 | "message" => "abc", 63 | "iat" => time() + 20); // time in the future 64 | $encoded = JWT::encode($payload, 'my_key'); 65 | JWT::decode($encoded, 'my_key', array('HS256')); 66 | } 67 | 68 | public function testValidToken() 69 | { 70 | $payload = array( 71 | "message" => "abc", 72 | "exp" => time() + 20); // time in the future 73 | $encoded = JWT::encode($payload, 'my_key'); 74 | $decoded = JWT::decode($encoded, 'my_key', array('HS256')); 75 | $this->assertEquals($decoded->message, 'abc'); 76 | } 77 | 78 | public function testValidTokenWithList() 79 | { 80 | $payload = array( 81 | "message" => "abc", 82 | "exp" => time() + 20); // time in the future 83 | $encoded = JWT::encode($payload, 'my_key'); 84 | $decoded = JWT::decode($encoded, 'my_key', array('HS256', 'HS512')); 85 | $this->assertEquals($decoded->message, 'abc'); 86 | } 87 | 88 | public function testValidTokenWithNbf() 89 | { 90 | $payload = array( 91 | "message" => "abc", 92 | "iat" => time(), 93 | "exp" => time() + 20, // time in the future 94 | "nbf" => time() - 20); 95 | $encoded = JWT::encode($payload, 'my_key'); 96 | $decoded = JWT::decode($encoded, 'my_key', array('HS256')); 97 | $this->assertEquals($decoded->message, 'abc'); 98 | } 99 | 100 | public function testInvalidToken() 101 | { 102 | $payload = array( 103 | "message" => "abc", 104 | "exp" => time() + 20); // time in the future 105 | $encoded = JWT::encode($payload, 'my_key'); 106 | $this->setExpectedException('SignatureInvalidException'); 107 | $decoded = JWT::decode($encoded, 'my_key2', array('HS256')); 108 | } 109 | 110 | public function testRSEncodeDecode() 111 | { 112 | $privKey = openssl_pkey_new(array('digest_alg' => 'sha256', 113 | 'private_key_bits' => 1024, 114 | 'private_key_type' => OPENSSL_KEYTYPE_RSA)); 115 | $msg = JWT::encode('abc', $privKey, 'RS256'); 116 | $pubKey = openssl_pkey_get_details($privKey); 117 | $pubKey = $pubKey['key']; 118 | $decoded = JWT::decode($msg, $pubKey, array('RS256')); 119 | $this->assertEquals($decoded, 'abc'); 120 | } 121 | 122 | public function testKIDChooser() 123 | { 124 | $keys = array('1' => 'my_key', '2' => 'my_key2'); 125 | $msg = JWT::encode('abc', $keys['1'], 'HS256', '1'); 126 | $decoded = JWT::decode($msg, $keys, array('HS256')); 127 | $this->assertEquals($decoded, 'abc'); 128 | } 129 | 130 | public function testNoneAlgorithm() 131 | { 132 | $msg = JWT::encode('abc', 'my_key'); 133 | $this->setExpectedException('DomainException'); 134 | JWT::decode($msg, 'my_key', array('none')); 135 | } 136 | 137 | public function testIncorrectAlgorithm() 138 | { 139 | $msg = JWT::encode('abc', 'my_key'); 140 | $this->setExpectedException('DomainException'); 141 | JWT::decode($msg, 'my_key', array('RS256')); 142 | } 143 | 144 | public function testMissingAlgorithm() 145 | { 146 | $msg = JWT::encode('abc', 'my_key'); 147 | $this->setExpectedException('DomainException'); 148 | JWT::decode($msg, 'my_key'); 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /lib/php-jwt/tests/autoload.php.dist: -------------------------------------------------------------------------------- 1 | 2 | 3 |

4 | 5 |
6 |

7 |
8 | 9 | 10 |
11 | 12 | 13 | 14 |
15 | 16 | --------------------------------------------------------------------------------