├── LICENSE ├── composer.json ├── config └── config.php └── src ├── Blacklist.php ├── Claims ├── Audience.php ├── Claim.php ├── Collection.php ├── Custom.php ├── DatetimeTrait.php ├── Expiration.php ├── Factory.php ├── IssuedAt.php ├── Issuer.php ├── JwtId.php ├── NotBefore.php └── Subject.php ├── Console └── JWTGenerateSecretCommand.php ├── Contracts ├── Claim.php ├── Http │ └── Parser.php ├── JWTSubject.php ├── Providers │ ├── Auth.php │ ├── JWT.php │ └── Storage.php └── Validator.php ├── Exceptions ├── InvalidClaimException.php ├── JWTException.php ├── PayloadException.php ├── TokenBlacklistedException.php ├── TokenExpiredException.php ├── TokenInvalidException.php └── UserNotDefinedException.php ├── Facades ├── JWTAuth.php ├── JWTFactory.php └── JWTProvider.php ├── Factory.php ├── Http ├── Middleware │ ├── Authenticate.php │ ├── AuthenticateAndRenew.php │ ├── BaseMiddleware.php │ ├── Check.php │ └── RefreshToken.php └── Parser │ ├── AuthHeaders.php │ ├── Cookies.php │ ├── InputSource.php │ ├── KeyTrait.php │ ├── LumenRouteParams.php │ ├── Parser.php │ ├── QueryString.php │ └── RouteParams.php ├── JWT.php ├── JWTAuth.php ├── JWTGuard.php ├── Manager.php ├── Payload.php ├── Providers ├── AbstractServiceProvider.php ├── Auth │ └── Illuminate.php ├── JWT │ ├── Lcobucci.php │ └── Provider.php ├── LaravelServiceProvider.php ├── LumenServiceProvider.php └── Storage │ └── Illuminate.php ├── Support ├── CustomClaims.php ├── RefreshFlow.php └── Utils.php ├── Token.php └── Validators ├── PayloadValidator.php ├── TokenValidator.php └── Validator.php /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) Sean Tymon 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 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tymon/jwt-auth", 3 | "description": "JSON Web Token Authentication for Laravel and Lumen", 4 | "keywords": [ 5 | "auth", 6 | "authentication", 7 | "json web token", 8 | "jwt", 9 | "laravel" 10 | ], 11 | "homepage": "https://github.com/tymondesigns/jwt-auth", 12 | "support": { 13 | "issues": "https://github.com/tymondesigns/jwt-auth/issues", 14 | "source": "https://github.com/tymondesigns/jwt-auth" 15 | }, 16 | "license": "MIT", 17 | "authors": [ 18 | { 19 | "name": "Sean Tymon", 20 | "email": "tymon148@gmail.com", 21 | "homepage": "https://tymon.xyz", 22 | "role": "Developer" 23 | } 24 | ], 25 | "require": { 26 | "php": "^8.0", 27 | "illuminate/auth": "^9.0|^10.0|^11.0|^12.0", 28 | "illuminate/contracts": "^9.0|^10.0|^11.0|^12.0", 29 | "illuminate/http": "^9.0|^10.0|^11.0|^12.0", 30 | "illuminate/support": "^9.0|^10.0|^11.0|^12.0", 31 | "lcobucci/jwt": "^4.0", 32 | "nesbot/carbon": "^2.69|^3.0" 33 | }, 34 | "require-dev": { 35 | "illuminate/console": "^9.0|^10.0|^11.0|^12.0", 36 | "illuminate/database": "^9.0|^10.0|^11.0|^12.0", 37 | "illuminate/routing": "^9.0|^10.0|^11.0|^12.0", 38 | "mockery/mockery": "^1.6", 39 | "phpunit/phpunit": "^9.4" 40 | }, 41 | "autoload": { 42 | "psr-4": { 43 | "Tymon\\JWTAuth\\": "src/" 44 | } 45 | }, 46 | "autoload-dev": { 47 | "psr-4": { 48 | "Tymon\\JWTAuth\\Test\\": "tests/" 49 | } 50 | }, 51 | "extra": { 52 | "branch-alias": { 53 | "dev-develop": "1.0-dev", 54 | "dev-2.x": "2.0-dev" 55 | }, 56 | "laravel": { 57 | "aliases": { 58 | "JWTAuth": "Tymon\\JWTAuth\\Facades\\JWTAuth", 59 | "JWTFactory": "Tymon\\JWTAuth\\Facades\\JWTFactory" 60 | }, 61 | "providers": [ 62 | "Tymon\\JWTAuth\\Providers\\LaravelServiceProvider" 63 | ] 64 | } 65 | }, 66 | "config": { 67 | "sort-packages": true 68 | }, 69 | "prefer-stable": true, 70 | "minimum-stability": "dev", 71 | "scripts": { 72 | "test": "phpunit --colors=always", 73 | "test:ci": "composer test -- --verbose --coverage-text --coverage-clover=coverage.xml" 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /config/config.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | return [ 13 | 14 | /* 15 | |-------------------------------------------------------------------------- 16 | | JWT Authentication Secret 17 | |-------------------------------------------------------------------------- 18 | | 19 | | Don't forget to set this in your .env file, as it will be used to sign 20 | | your tokens. A helper command is provided for this: 21 | | `php artisan jwt:secret` 22 | | 23 | | Note: This will be used for Symmetric algorithms only (HMAC), 24 | | since RSA and ECDSA use a private/public key combo (See below). 25 | | 26 | */ 27 | 28 | 'secret' => env('JWT_SECRET'), 29 | 30 | /* 31 | |-------------------------------------------------------------------------- 32 | | JWT Authentication Keys 33 | |-------------------------------------------------------------------------- 34 | | 35 | | The algorithm you are using, will determine whether your tokens are 36 | | signed with a random string (defined in `JWT_SECRET`) or using the 37 | | following public & private keys. 38 | | 39 | | Symmetric Algorithms: 40 | | HS256, HS384 & HS512 will use `JWT_SECRET`. 41 | | 42 | | Asymmetric Algorithms: 43 | | RS256, RS384 & RS512 / ES256, ES384 & ES512 will use the keys below. 44 | | 45 | */ 46 | 47 | 'keys' => [ 48 | 49 | /* 50 | |-------------------------------------------------------------------------- 51 | | Public Key 52 | |-------------------------------------------------------------------------- 53 | | 54 | | A path or resource to your public key. 55 | | 56 | | E.g. 'file://path/to/public/key' 57 | | 58 | */ 59 | 60 | 'public' => env('JWT_PUBLIC_KEY'), 61 | 62 | /* 63 | |-------------------------------------------------------------------------- 64 | | Private Key 65 | |-------------------------------------------------------------------------- 66 | | 67 | | A path or resource to your private key. 68 | | 69 | | E.g. 'file://path/to/private/key' 70 | | 71 | */ 72 | 73 | 'private' => env('JWT_PRIVATE_KEY'), 74 | 75 | /* 76 | |-------------------------------------------------------------------------- 77 | | Passphrase 78 | |-------------------------------------------------------------------------- 79 | | 80 | | The passphrase for your private key. Can be null if none set. 81 | | 82 | */ 83 | 84 | 'passphrase' => env('JWT_PASSPHRASE'), 85 | 86 | ], 87 | 88 | /* 89 | |-------------------------------------------------------------------------- 90 | | JWT time to live 91 | |-------------------------------------------------------------------------- 92 | | 93 | | Specify the length of time (in minutes) that the token will be valid for. 94 | | Defaults to 1 hour. 95 | | 96 | | You can also set this to null, to yield a never expiring token. 97 | | Some people may want this behaviour for e.g. a mobile app. 98 | | This is not particularly recommended, so make sure you have appropriate 99 | | systems in place to revoke the token if necessary. 100 | | Notice: If you set this to null you should remove 'exp' element from 'required_claims' list. 101 | | 102 | */ 103 | 104 | 'ttl' => env('JWT_TTL', 60), 105 | 106 | /* 107 | |-------------------------------------------------------------------------- 108 | | Refresh time to live 109 | |-------------------------------------------------------------------------- 110 | | 111 | | Specify the length of time (in minutes) that the token can be refreshed 112 | | within. I.E. The user can refresh their token within a 2 week window of 113 | | the original token being created until they must re-authenticate. 114 | | Defaults to 2 weeks. 115 | | 116 | | You can also set this to null, to yield an infinite refresh time. 117 | | Some may want this instead of never expiring tokens for e.g. a mobile app. 118 | | This is not particularly recommended, so make sure you have appropriate 119 | | systems in place to revoke the token if necessary. 120 | | 121 | */ 122 | 123 | 'refresh_ttl' => env('JWT_REFRESH_TTL', 20160), 124 | 125 | /* 126 | |-------------------------------------------------------------------------- 127 | | JWT hashing algorithm 128 | |-------------------------------------------------------------------------- 129 | | 130 | | Specify the hashing algorithm that will be used to sign the token. 131 | | 132 | */ 133 | 134 | 'algo' => env('JWT_ALGO', Tymon\JWTAuth\Providers\JWT\Provider::ALGO_HS256), 135 | 136 | /* 137 | |-------------------------------------------------------------------------- 138 | | Required Claims 139 | |-------------------------------------------------------------------------- 140 | | 141 | | Specify the required claims that must exist in any token. 142 | | A TokenInvalidException will be thrown if any of these claims are not 143 | | present in the payload. 144 | | 145 | */ 146 | 147 | 'required_claims' => [ 148 | 'iss', 149 | 'iat', 150 | 'exp', 151 | 'nbf', 152 | 'sub', 153 | 'jti', 154 | ], 155 | 156 | /* 157 | |-------------------------------------------------------------------------- 158 | | Persistent Claims 159 | |-------------------------------------------------------------------------- 160 | | 161 | | Specify the claim keys to be persisted when refreshing a token. 162 | | `sub` and `iat` will automatically be persisted, in 163 | | addition to the these claims. 164 | | 165 | | Note: If a claim does not exist then it will be ignored. 166 | | 167 | */ 168 | 169 | 'persistent_claims' => [ 170 | // 'foo', 171 | // 'bar', 172 | ], 173 | 174 | /* 175 | |-------------------------------------------------------------------------- 176 | | Lock Subject 177 | |-------------------------------------------------------------------------- 178 | | 179 | | This will determine whether a `prv` claim is automatically added to 180 | | the token. The purpose of this is to ensure that if you have multiple 181 | | authentication models e.g. `App\User` & `App\OtherPerson`, then we 182 | | should prevent one authentication request from impersonating another, 183 | | if 2 tokens happen to have the same id across the 2 different models. 184 | | 185 | | Under specific circumstances, you may want to disable this behaviour 186 | | e.g. if you only have one authentication model, then you would save 187 | | a little on token size. 188 | | 189 | */ 190 | 191 | 'lock_subject' => true, 192 | 193 | /* 194 | |-------------------------------------------------------------------------- 195 | | Leeway 196 | |-------------------------------------------------------------------------- 197 | | 198 | | This property gives the jwt timestamp claims some "leeway". 199 | | Meaning that if you have any unavoidable slight clock skew on 200 | | any of your servers then this will afford you some level of cushioning. 201 | | 202 | | This applies to the claims `iat`, `nbf` and `exp`. 203 | | 204 | | Specify in seconds - only if you know you need it. 205 | | 206 | */ 207 | 208 | 'leeway' => env('JWT_LEEWAY', 0), 209 | 210 | /* 211 | |-------------------------------------------------------------------------- 212 | | Blacklist Enabled 213 | |-------------------------------------------------------------------------- 214 | | 215 | | In order to invalidate tokens, you must have the blacklist enabled. 216 | | If you do not want or need this functionality, then set this to false. 217 | | 218 | */ 219 | 220 | 'blacklist_enabled' => env('JWT_BLACKLIST_ENABLED', true), 221 | 222 | /* 223 | | ------------------------------------------------------------------------- 224 | | Blacklist Grace Period 225 | | ------------------------------------------------------------------------- 226 | | 227 | | When multiple concurrent requests are made with the same JWT, 228 | | it is possible that some of them fail, due to token regeneration 229 | | on every request. 230 | | 231 | | Set grace period in seconds to prevent parallel request failure. 232 | | 233 | */ 234 | 235 | 'blacklist_grace_period' => env('JWT_BLACKLIST_GRACE_PERIOD', 0), 236 | 237 | /* 238 | |-------------------------------------------------------------------------- 239 | | Cookies encryption 240 | |-------------------------------------------------------------------------- 241 | | 242 | | By default Laravel encrypt cookies for security reason. 243 | | If you decide to not decrypt cookies, you will have to configure Laravel 244 | | to not encrypt your cookie token by adding its name into the $except 245 | | array available in the middleware "EncryptCookies" provided by Laravel. 246 | | see https://laravel.com/docs/master/responses#cookies-and-encryption 247 | | for details. 248 | | 249 | | Set it to true if you want to decrypt cookies. 250 | | 251 | */ 252 | 253 | 'decrypt_cookies' => false, 254 | 255 | /* 256 | |-------------------------------------------------------------------------- 257 | | Providers 258 | |-------------------------------------------------------------------------- 259 | | 260 | | Specify the various providers used throughout the package. 261 | | 262 | */ 263 | 264 | 'providers' => [ 265 | 266 | /* 267 | |-------------------------------------------------------------------------- 268 | | JWT Provider 269 | |-------------------------------------------------------------------------- 270 | | 271 | | Specify the provider that is used to create and decode the tokens. 272 | | 273 | */ 274 | 275 | 'jwt' => Tymon\JWTAuth\Providers\JWT\Lcobucci::class, 276 | 277 | /* 278 | |-------------------------------------------------------------------------- 279 | | Authentication Provider 280 | |-------------------------------------------------------------------------- 281 | | 282 | | Specify the provider that is used to authenticate users. 283 | | 284 | */ 285 | 286 | 'auth' => Tymon\JWTAuth\Providers\Auth\Illuminate::class, 287 | 288 | /* 289 | |-------------------------------------------------------------------------- 290 | | Storage Provider 291 | |-------------------------------------------------------------------------- 292 | | 293 | | Specify the provider that is used to store tokens in the blacklist. 294 | | 295 | */ 296 | 297 | 'storage' => Tymon\JWTAuth\Providers\Storage\Illuminate::class, 298 | 299 | ], 300 | 301 | ]; 302 | -------------------------------------------------------------------------------- /src/Blacklist.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Tymon\JWTAuth; 13 | 14 | use Tymon\JWTAuth\Contracts\Providers\Storage; 15 | use Tymon\JWTAuth\Support\Utils; 16 | 17 | class Blacklist 18 | { 19 | /** 20 | * The storage. 21 | * 22 | * @var \Tymon\JWTAuth\Contracts\Providers\Storage 23 | */ 24 | protected $storage; 25 | 26 | /** 27 | * The grace period when a token is blacklisted. In seconds. 28 | * 29 | * @var int 30 | */ 31 | protected $gracePeriod = 0; 32 | 33 | /** 34 | * Number of minutes from issue date in which a JWT can be refreshed. 35 | * 36 | * @var int 37 | */ 38 | protected $refreshTTL = 20160; 39 | 40 | /** 41 | * The unique key held within the blacklist. 42 | * 43 | * @var string 44 | */ 45 | protected $key = 'jti'; 46 | 47 | /** 48 | * Constructor. 49 | * 50 | * @param \Tymon\JWTAuth\Contracts\Providers\Storage $storage 51 | * @return void 52 | */ 53 | public function __construct(Storage $storage) 54 | { 55 | $this->storage = $storage; 56 | } 57 | 58 | /** 59 | * Add the token (jti claim) to the blacklist. 60 | * 61 | * @param \Tymon\JWTAuth\Payload $payload 62 | * @return bool 63 | */ 64 | public function add(Payload $payload) 65 | { 66 | // if there is no exp claim then add the jwt to 67 | // the blacklist indefinitely 68 | if (! $payload->hasKey('exp')) { 69 | return $this->addForever($payload); 70 | } 71 | 72 | // if we have already added this token to the blacklist 73 | if (! empty($this->storage->get($this->getKey($payload)))) { 74 | return true; 75 | } 76 | 77 | $this->storage->add( 78 | $this->getKey($payload), 79 | ['valid_until' => $this->getGraceTimestamp()], 80 | $this->getMinutesUntilExpired($payload) 81 | ); 82 | 83 | return true; 84 | } 85 | 86 | /** 87 | * Get the number of minutes until the token expiry. 88 | * 89 | * @param \Tymon\JWTAuth\Payload $payload 90 | * @return int 91 | */ 92 | protected function getMinutesUntilExpired(Payload $payload) 93 | { 94 | $exp = Utils::timestamp($payload['exp']); 95 | $iat = Utils::timestamp($payload['iat']); 96 | 97 | // get the latter of the two expiration dates and find 98 | // the number of minutes until the expiration date, 99 | // plus 1 minute to avoid overlap 100 | $expiration = $exp->max($iat->addMinutes($this->refreshTTL))->addMinute(); 101 | 102 | $minutes = method_exists($expiration, 'diffInRealMinutes') 103 | ? $expiration->diffInRealMinutes() 104 | : $expiration->diffInUTCMinutes(); 105 | 106 | return (int) ceil(abs($minutes)); 107 | } 108 | 109 | /** 110 | * Add the token (jti claim) to the blacklist indefinitely. 111 | * 112 | * @param \Tymon\JWTAuth\Payload $payload 113 | * @return bool 114 | */ 115 | public function addForever(Payload $payload) 116 | { 117 | $this->storage->forever($this->getKey($payload), 'forever'); 118 | 119 | return true; 120 | } 121 | 122 | /** 123 | * Determine whether the token has been blacklisted. 124 | * 125 | * @param \Tymon\JWTAuth\Payload $payload 126 | * @return bool 127 | */ 128 | public function has(Payload $payload) 129 | { 130 | $val = $this->storage->get($this->getKey($payload)); 131 | 132 | // exit early if the token was blacklisted forever, 133 | if ($val === 'forever') { 134 | return true; 135 | } 136 | 137 | // check whether the expiry + grace has past 138 | return ! empty($val) && ! Utils::isFuture($val['valid_until']); 139 | } 140 | 141 | /** 142 | * Remove the token (jti claim) from the blacklist. 143 | * 144 | * @param \Tymon\JWTAuth\Payload $payload 145 | * @return bool 146 | */ 147 | public function remove(Payload $payload) 148 | { 149 | return $this->storage->destroy($this->getKey($payload)); 150 | } 151 | 152 | /** 153 | * Remove all tokens from the blacklist. 154 | * 155 | * @return bool 156 | */ 157 | public function clear() 158 | { 159 | $this->storage->flush(); 160 | 161 | return true; 162 | } 163 | 164 | /** 165 | * Get the timestamp when the blacklist comes into effect 166 | * This defaults to immediate (0 seconds). 167 | * 168 | * @return int 169 | */ 170 | protected function getGraceTimestamp() 171 | { 172 | return Utils::now()->addSeconds($this->gracePeriod)->getTimestamp(); 173 | } 174 | 175 | /** 176 | * Set the grace period. 177 | * 178 | * @param int $gracePeriod 179 | * @return $this 180 | */ 181 | public function setGracePeriod($gracePeriod) 182 | { 183 | $this->gracePeriod = (int) $gracePeriod; 184 | 185 | return $this; 186 | } 187 | 188 | /** 189 | * Get the grace period. 190 | * 191 | * @return int 192 | */ 193 | public function getGracePeriod() 194 | { 195 | return $this->gracePeriod; 196 | } 197 | 198 | /** 199 | * Get the unique key held within the blacklist. 200 | * 201 | * @param \Tymon\JWTAuth\Payload $payload 202 | * @return mixed 203 | */ 204 | public function getKey(Payload $payload) 205 | { 206 | return $payload($this->key); 207 | } 208 | 209 | /** 210 | * Set the unique key held within the blacklist. 211 | * 212 | * @param string $key 213 | * @return $this 214 | */ 215 | public function setKey($key) 216 | { 217 | $this->key = value($key); 218 | 219 | return $this; 220 | } 221 | 222 | /** 223 | * Set the refresh time limit. 224 | * 225 | * @param int $ttl 226 | * @return $this 227 | */ 228 | public function setRefreshTTL($ttl) 229 | { 230 | $this->refreshTTL = (int) $ttl; 231 | 232 | return $this; 233 | } 234 | 235 | /** 236 | * Get the refresh time limit. 237 | * 238 | * @return int 239 | */ 240 | public function getRefreshTTL() 241 | { 242 | return $this->refreshTTL; 243 | } 244 | } 245 | -------------------------------------------------------------------------------- /src/Claims/Audience.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Tymon\JWTAuth\Claims; 13 | 14 | class Audience extends Claim 15 | { 16 | /** 17 | * {@inheritdoc} 18 | */ 19 | protected $name = 'aud'; 20 | } 21 | -------------------------------------------------------------------------------- /src/Claims/Claim.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Tymon\JWTAuth\Claims; 13 | 14 | use Illuminate\Contracts\Support\Arrayable; 15 | use Illuminate\Contracts\Support\Jsonable; 16 | use JsonSerializable; 17 | use Tymon\JWTAuth\Contracts\Claim as ClaimContract; 18 | 19 | abstract class Claim implements Arrayable, ClaimContract, Jsonable, JsonSerializable 20 | { 21 | /** 22 | * The claim name. 23 | * 24 | * @var string 25 | */ 26 | protected $name; 27 | 28 | /** 29 | * The claim value. 30 | * 31 | * @var mixed 32 | */ 33 | private $value; 34 | 35 | /** 36 | * @param mixed $value 37 | * @return void 38 | */ 39 | public function __construct($value) 40 | { 41 | $this->setValue($value); 42 | } 43 | 44 | /** 45 | * Set the claim value, and call a validate method. 46 | * 47 | * @param mixed $value 48 | * @return $this 49 | * 50 | * @throws \Tymon\JWTAuth\Exceptions\InvalidClaimException 51 | */ 52 | public function setValue($value) 53 | { 54 | $this->value = $this->validateCreate($value); 55 | 56 | return $this; 57 | } 58 | 59 | /** 60 | * Get the claim value. 61 | * 62 | * @return mixed 63 | */ 64 | public function getValue() 65 | { 66 | return $this->value; 67 | } 68 | 69 | /** 70 | * Set the claim name. 71 | * 72 | * @param string $name 73 | * @return $this 74 | */ 75 | public function setName($name) 76 | { 77 | $this->name = $name; 78 | 79 | return $this; 80 | } 81 | 82 | /** 83 | * Get the claim name. 84 | * 85 | * @return string 86 | */ 87 | public function getName() 88 | { 89 | return $this->name; 90 | } 91 | 92 | /** 93 | * Validate the claim in a standalone Claim context. 94 | * 95 | * @param mixed $value 96 | * @return bool 97 | */ 98 | public function validateCreate($value) 99 | { 100 | return $value; 101 | } 102 | 103 | /** 104 | * Validate the Claim within a Payload context. 105 | * 106 | * @return bool 107 | */ 108 | public function validatePayload() 109 | { 110 | return $this->getValue(); 111 | } 112 | 113 | /** 114 | * Validate the Claim within a refresh context. 115 | * 116 | * @param int $refreshTTL 117 | * @return bool 118 | */ 119 | public function validateRefresh($refreshTTL) 120 | { 121 | return $this->getValue(); 122 | } 123 | 124 | /** 125 | * Checks if the value matches the claim. 126 | * 127 | * @param mixed $value 128 | * @param bool $strict 129 | * @return bool 130 | */ 131 | public function matches($value, $strict = true) 132 | { 133 | return $strict ? $this->value === $value : $this->value == $value; 134 | } 135 | 136 | /** 137 | * Convert the object into something JSON serializable. 138 | * 139 | * @return array 140 | */ 141 | #[\ReturnTypeWillChange] 142 | public function jsonSerialize(): mixed 143 | { 144 | return $this->toArray(); 145 | } 146 | 147 | /** 148 | * Build a key value array comprising of the claim name and value. 149 | * 150 | * @return array 151 | */ 152 | public function toArray() 153 | { 154 | return [$this->getName() => $this->getValue()]; 155 | } 156 | 157 | /** 158 | * Get the claim as JSON. 159 | * 160 | * @param int $options 161 | * @return string 162 | */ 163 | public function toJson($options = JSON_UNESCAPED_SLASHES) 164 | { 165 | return json_encode($this->toArray(), $options); 166 | } 167 | 168 | /** 169 | * Get the payload as a string. 170 | * 171 | * @return string 172 | */ 173 | public function __toString() 174 | { 175 | return $this->toJson(); 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /src/Claims/Collection.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Tymon\JWTAuth\Claims; 13 | 14 | use Illuminate\Support\Collection as IlluminateCollection; 15 | use Illuminate\Support\Str; 16 | 17 | class Collection extends IlluminateCollection 18 | { 19 | /** 20 | * Create a new collection. 21 | * 22 | * @param mixed $items 23 | * @return void 24 | */ 25 | public function __construct($items = []) 26 | { 27 | parent::__construct($this->getArrayableItems($items)); 28 | } 29 | 30 | /** 31 | * Get a Claim instance by it's unique name. 32 | * 33 | * @param string $name 34 | * @param callable|null $callback 35 | * @param mixed $default 36 | * @return \Tymon\JWTAuth\Claims\Claim 37 | */ 38 | public function getByClaimName($name, ?callable $callback = null, $default = null) 39 | { 40 | return $this->filter(function (Claim $claim) use ($name) { 41 | return $claim->getName() === $name; 42 | })->first($callback, $default); 43 | } 44 | 45 | /** 46 | * Validate each claim under a given context. 47 | * 48 | * @param string $context 49 | * @return $this 50 | */ 51 | public function validate($context = 'payload') 52 | { 53 | $args = func_get_args(); 54 | array_shift($args); 55 | 56 | $this->each(function ($claim) use ($context, $args) { 57 | call_user_func_array( 58 | [$claim, 'validate'.Str::ucfirst($context)], 59 | $args 60 | ); 61 | }); 62 | 63 | return $this; 64 | } 65 | 66 | /** 67 | * Determine if the Collection contains all of the given keys. 68 | * 69 | * @param mixed $claims 70 | * @return bool 71 | */ 72 | public function hasAllClaims($claims) 73 | { 74 | return count($claims) && (new static($claims))->diff($this->keys())->isEmpty(); 75 | } 76 | 77 | /** 78 | * Get the claims as key/val array. 79 | * 80 | * @return array 81 | */ 82 | public function toPlainArray() 83 | { 84 | return $this->map(function (Claim $claim) { 85 | return $claim->getValue(); 86 | })->toArray(); 87 | } 88 | 89 | /** 90 | * {@inheritdoc} 91 | */ 92 | protected function getArrayableItems($items) 93 | { 94 | return $this->sanitizeClaims($items); 95 | } 96 | 97 | /** 98 | * Ensure that the given claims array is keyed by the claim name. 99 | * 100 | * @param mixed $items 101 | * @return array 102 | */ 103 | private function sanitizeClaims($items) 104 | { 105 | $claims = []; 106 | foreach ($items as $key => $value) { 107 | if (! is_string($key) && $value instanceof Claim) { 108 | $key = $value->getName(); 109 | } 110 | 111 | $claims[$key] = $value; 112 | } 113 | 114 | return $claims; 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/Claims/Custom.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Tymon\JWTAuth\Claims; 13 | 14 | class Custom extends Claim 15 | { 16 | /** 17 | * @param string $name 18 | * @param mixed $value 19 | * @return void 20 | */ 21 | public function __construct($name, $value) 22 | { 23 | parent::__construct($value); 24 | $this->setName($name); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Claims/DatetimeTrait.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Tymon\JWTAuth\Claims; 13 | 14 | use DateInterval; 15 | use DateTimeInterface; 16 | use Tymon\JWTAuth\Exceptions\InvalidClaimException; 17 | use Tymon\JWTAuth\Support\Utils; 18 | 19 | trait DatetimeTrait 20 | { 21 | /** 22 | * Time leeway in seconds. 23 | * 24 | * @var int 25 | */ 26 | protected $leeway = 0; 27 | 28 | /** 29 | * Set the claim value, and call a validate method. 30 | * 31 | * @param mixed $value 32 | * @return $this 33 | * 34 | * @throws \Tymon\JWTAuth\Exceptions\InvalidClaimException 35 | */ 36 | public function setValue($value) 37 | { 38 | if ($value instanceof DateInterval) { 39 | $value = Utils::now()->add($value); 40 | } 41 | 42 | if ($value instanceof DateTimeInterface) { 43 | $value = $value->getTimestamp(); 44 | } 45 | 46 | return parent::setValue($value); 47 | } 48 | 49 | /** 50 | * {@inheritdoc} 51 | */ 52 | public function validateCreate($value) 53 | { 54 | if (! is_numeric($value)) { 55 | throw new InvalidClaimException($this); 56 | } 57 | 58 | return $value; 59 | } 60 | 61 | /** 62 | * Determine whether the value is in the future. 63 | * 64 | * @param mixed $value 65 | * @return bool 66 | */ 67 | protected function isFuture($value) 68 | { 69 | return Utils::isFuture($value, $this->leeway); 70 | } 71 | 72 | /** 73 | * Determine whether the value is in the past. 74 | * 75 | * @param mixed $value 76 | * @return bool 77 | */ 78 | protected function isPast($value) 79 | { 80 | return Utils::isPast($value, $this->leeway); 81 | } 82 | 83 | /** 84 | * Set the leeway in seconds. 85 | * 86 | * @param int $leeway 87 | * @return $this 88 | */ 89 | public function setLeeway($leeway) 90 | { 91 | $this->leeway = $leeway; 92 | 93 | return $this; 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/Claims/Expiration.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Tymon\JWTAuth\Claims; 13 | 14 | use Tymon\JWTAuth\Exceptions\TokenExpiredException; 15 | 16 | class Expiration extends Claim 17 | { 18 | use DatetimeTrait; 19 | 20 | /** 21 | * {@inheritdoc} 22 | */ 23 | protected $name = 'exp'; 24 | 25 | /** 26 | * {@inheritdoc} 27 | */ 28 | public function validatePayload() 29 | { 30 | if ($this->isPast($this->getValue())) { 31 | throw new TokenExpiredException('Token has expired'); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Claims/Factory.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Tymon\JWTAuth\Claims; 13 | 14 | use Illuminate\Http\Request; 15 | use Illuminate\Support\Str; 16 | use Tymon\JWTAuth\Support\Utils; 17 | 18 | class Factory 19 | { 20 | /** 21 | * The request. 22 | * 23 | * @var \Illuminate\Http\Request 24 | */ 25 | protected $request; 26 | 27 | /** 28 | * The TTL. 29 | * 30 | * @var int 31 | */ 32 | protected $ttl = 60; 33 | 34 | /** 35 | * Time leeway in seconds. 36 | * 37 | * @var int 38 | */ 39 | protected $leeway = 0; 40 | 41 | /** 42 | * The classes map. 43 | * 44 | * @var array 45 | */ 46 | private $classMap = [ 47 | 'aud' => Audience::class, 48 | 'exp' => Expiration::class, 49 | 'iat' => IssuedAt::class, 50 | 'iss' => Issuer::class, 51 | 'jti' => JwtId::class, 52 | 'nbf' => NotBefore::class, 53 | 'sub' => Subject::class, 54 | ]; 55 | 56 | /** 57 | * Constructor. 58 | * 59 | * @param \Illuminate\Http\Request $request 60 | * @return void 61 | */ 62 | public function __construct(Request $request) 63 | { 64 | $this->request = $request; 65 | } 66 | 67 | /** 68 | * Get the instance of the claim when passing the name and value. 69 | * 70 | * @param string $name 71 | * @param mixed $value 72 | * @return \Tymon\JWTAuth\Claims\Claim 73 | */ 74 | public function get($name, $value) 75 | { 76 | if ($this->has($name)) { 77 | $claim = new $this->classMap[$name]($value); 78 | 79 | return method_exists($claim, 'setLeeway') ? 80 | $claim->setLeeway($this->leeway) : 81 | $claim; 82 | } 83 | 84 | return new Custom($name, $value); 85 | } 86 | 87 | /** 88 | * Check whether the claim exists. 89 | * 90 | * @param string $name 91 | * @return bool 92 | */ 93 | public function has($name) 94 | { 95 | return array_key_exists($name, $this->classMap); 96 | } 97 | 98 | /** 99 | * Generate the initial value and return the Claim instance. 100 | * 101 | * @param string $name 102 | * @return \Tymon\JWTAuth\Claims\Claim 103 | */ 104 | public function make($name) 105 | { 106 | return $this->get($name, $this->$name()); 107 | } 108 | 109 | /** 110 | * Get the Issuer (iss) claim. 111 | * 112 | * @return string 113 | */ 114 | public function iss() 115 | { 116 | return $this->request->url(); 117 | } 118 | 119 | /** 120 | * Get the Issued At (iat) claim. 121 | * 122 | * @return int 123 | */ 124 | public function iat() 125 | { 126 | return Utils::now()->getTimestamp(); 127 | } 128 | 129 | /** 130 | * Get the Expiration (exp) claim. 131 | * 132 | * @return int 133 | */ 134 | public function exp() 135 | { 136 | return Utils::now()->addMinutes($this->ttl)->getTimestamp(); 137 | } 138 | 139 | /** 140 | * Get the Not Before (nbf) claim. 141 | * 142 | * @return int 143 | */ 144 | public function nbf() 145 | { 146 | return Utils::now()->getTimestamp(); 147 | } 148 | 149 | /** 150 | * Get the JWT Id (jti) claim. 151 | * 152 | * @return string 153 | */ 154 | public function jti() 155 | { 156 | return Str::random(); 157 | } 158 | 159 | /** 160 | * Add a new claim mapping. 161 | * 162 | * @param string $name 163 | * @param string $classPath 164 | * @return $this 165 | */ 166 | public function extend($name, $classPath) 167 | { 168 | $this->classMap[$name] = $classPath; 169 | 170 | return $this; 171 | } 172 | 173 | /** 174 | * Set the request instance. 175 | * 176 | * @param \Illuminate\Http\Request $request 177 | * @return $this 178 | */ 179 | public function setRequest(Request $request) 180 | { 181 | $this->request = $request; 182 | 183 | return $this; 184 | } 185 | 186 | /** 187 | * Set the token ttl (in minutes). 188 | * 189 | * @param int $ttl 190 | * @return $this 191 | */ 192 | public function setTTL($ttl) 193 | { 194 | $this->ttl = $ttl; 195 | 196 | return $this; 197 | } 198 | 199 | /** 200 | * Get the token ttl. 201 | * 202 | * @return int 203 | */ 204 | public function getTTL() 205 | { 206 | return $this->ttl; 207 | } 208 | 209 | /** 210 | * Set the leeway in seconds. 211 | * 212 | * @param int $leeway 213 | * @return $this 214 | */ 215 | public function setLeeway($leeway) 216 | { 217 | $this->leeway = $leeway; 218 | 219 | return $this; 220 | } 221 | } 222 | -------------------------------------------------------------------------------- /src/Claims/IssuedAt.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Tymon\JWTAuth\Claims; 13 | 14 | use Tymon\JWTAuth\Exceptions\InvalidClaimException; 15 | use Tymon\JWTAuth\Exceptions\TokenExpiredException; 16 | use Tymon\JWTAuth\Exceptions\TokenInvalidException; 17 | 18 | class IssuedAt extends Claim 19 | { 20 | use DatetimeTrait { 21 | validateCreate as commonValidateCreate; 22 | } 23 | 24 | /** 25 | * {@inheritdoc} 26 | */ 27 | protected $name = 'iat'; 28 | 29 | /** 30 | * {@inheritdoc} 31 | */ 32 | public function validateCreate($value) 33 | { 34 | $this->commonValidateCreate($value); 35 | 36 | if ($this->isFuture($value)) { 37 | throw new InvalidClaimException($this); 38 | } 39 | 40 | return $value; 41 | } 42 | 43 | /** 44 | * {@inheritdoc} 45 | */ 46 | public function validatePayload() 47 | { 48 | if ($this->isFuture($this->getValue())) { 49 | throw new TokenInvalidException('Issued At (iat) timestamp cannot be in the future'); 50 | } 51 | } 52 | 53 | /** 54 | * {@inheritdoc} 55 | */ 56 | public function validateRefresh($refreshTTL) 57 | { 58 | if ($this->isPast($this->getValue() + $refreshTTL * 60)) { 59 | throw new TokenExpiredException('Token has expired and can no longer be refreshed'); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/Claims/Issuer.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Tymon\JWTAuth\Claims; 13 | 14 | class Issuer extends Claim 15 | { 16 | /** 17 | * {@inheritdoc} 18 | */ 19 | protected $name = 'iss'; 20 | } 21 | -------------------------------------------------------------------------------- /src/Claims/JwtId.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Tymon\JWTAuth\Claims; 13 | 14 | class JwtId extends Claim 15 | { 16 | /** 17 | * {@inheritdoc} 18 | */ 19 | protected $name = 'jti'; 20 | } 21 | -------------------------------------------------------------------------------- /src/Claims/NotBefore.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Tymon\JWTAuth\Claims; 13 | 14 | use Tymon\JWTAuth\Exceptions\TokenInvalidException; 15 | 16 | class NotBefore extends Claim 17 | { 18 | use DatetimeTrait; 19 | 20 | /** 21 | * {@inheritdoc} 22 | */ 23 | protected $name = 'nbf'; 24 | 25 | /** 26 | * {@inheritdoc} 27 | */ 28 | public function validatePayload() 29 | { 30 | if ($this->isFuture($this->getValue())) { 31 | throw new TokenInvalidException('Not Before (nbf) timestamp cannot be in the future'); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Claims/Subject.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Tymon\JWTAuth\Claims; 13 | 14 | class Subject extends Claim 15 | { 16 | /** 17 | * {@inheritdoc} 18 | */ 19 | protected $name = 'sub'; 20 | } 21 | -------------------------------------------------------------------------------- /src/Console/JWTGenerateSecretCommand.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Tymon\JWTAuth\Console; 13 | 14 | use Illuminate\Console\Command; 15 | use Illuminate\Support\Str; 16 | 17 | class JWTGenerateSecretCommand extends Command 18 | { 19 | /** 20 | * The console command signature. 21 | * 22 | * @var string 23 | */ 24 | protected $signature = 'jwt:secret 25 | {--s|show : Display the key instead of modifying files.} 26 | {--always-no : Skip generating key if it already exists.} 27 | {--f|force : Skip confirmation when overwriting an existing key.}'; 28 | 29 | /** 30 | * The console command description. 31 | * 32 | * @var string 33 | */ 34 | protected $description = 'Set the JWTAuth secret key used to sign the tokens'; 35 | 36 | /** 37 | * Execute the console command. 38 | * 39 | * @return void 40 | */ 41 | public function handle() 42 | { 43 | $key = Str::random(64); 44 | 45 | if ($this->option('show')) { 46 | $this->comment($key); 47 | 48 | return; 49 | } 50 | 51 | if (file_exists($path = $this->envPath()) === false) { 52 | return $this->displayKey($key); 53 | } 54 | 55 | if (Str::contains(file_get_contents($path), 'JWT_SECRET') === false) { 56 | // create new entry 57 | file_put_contents($path, PHP_EOL."JWT_SECRET=$key".PHP_EOL, FILE_APPEND); 58 | } else { 59 | if ($this->option('always-no')) { 60 | $this->comment('Secret key already exists. Skipping...'); 61 | 62 | return; 63 | } 64 | 65 | if ($this->isConfirmed() === false) { 66 | $this->comment('Phew... No changes were made to your secret key.'); 67 | 68 | return; 69 | } 70 | 71 | // update existing entry 72 | file_put_contents($path, str_replace( 73 | 'JWT_SECRET='.$this->laravel['config']['jwt.secret'], 74 | 'JWT_SECRET='.$key, file_get_contents($path) 75 | )); 76 | } 77 | 78 | $this->displayKey($key); 79 | } 80 | 81 | /** 82 | * Display the key. 83 | * 84 | * @param string $key 85 | * @return void 86 | */ 87 | protected function displayKey($key) 88 | { 89 | $this->laravel['config']['jwt.secret'] = $key; 90 | 91 | $this->info("jwt-auth secret [$key] set successfully."); 92 | } 93 | 94 | /** 95 | * Check if the modification is confirmed. 96 | * 97 | * @return bool 98 | */ 99 | protected function isConfirmed() 100 | { 101 | return $this->option('force') ? true : $this->confirm( 102 | 'This will invalidate all existing tokens. Are you sure you want to override the secret key?' 103 | ); 104 | } 105 | 106 | /** 107 | * Get the .env file path. 108 | * 109 | * @return string 110 | */ 111 | protected function envPath() 112 | { 113 | if (method_exists($this->laravel, 'environmentFilePath')) { 114 | return $this->laravel->environmentFilePath(); 115 | } 116 | 117 | // check if laravel version Less than 5.4.17 118 | if (version_compare($this->laravel->version(), '5.4.17', '<')) { 119 | return $this->laravel->basePath().DIRECTORY_SEPARATOR.'.env'; 120 | } 121 | 122 | return $this->laravel->basePath('.env'); 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /src/Contracts/Claim.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Tymon\JWTAuth\Contracts; 13 | 14 | interface Claim 15 | { 16 | /** 17 | * Set the claim value, and call a validate method. 18 | * 19 | * @param mixed $value 20 | * @return $this 21 | * 22 | * @throws \Tymon\JWTAuth\Exceptions\InvalidClaimException 23 | */ 24 | public function setValue($value); 25 | 26 | /** 27 | * Get the claim value. 28 | * 29 | * @return mixed 30 | */ 31 | public function getValue(); 32 | 33 | /** 34 | * Set the claim name. 35 | * 36 | * @param string $name 37 | * @return $this 38 | */ 39 | public function setName($name); 40 | 41 | /** 42 | * Get the claim name. 43 | * 44 | * @return string 45 | */ 46 | public function getName(); 47 | 48 | /** 49 | * Validate the Claim value. 50 | * 51 | * @param mixed $value 52 | * @return bool 53 | */ 54 | public function validateCreate($value); 55 | } 56 | -------------------------------------------------------------------------------- /src/Contracts/Http/Parser.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Tymon\JWTAuth\Contracts\Http; 13 | 14 | use Illuminate\Http\Request; 15 | 16 | interface Parser 17 | { 18 | /** 19 | * Parse the request. 20 | * 21 | * @param \Illuminate\Http\Request $request 22 | * @return null|string 23 | */ 24 | public function parse(Request $request); 25 | } 26 | -------------------------------------------------------------------------------- /src/Contracts/JWTSubject.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Tymon\JWTAuth\Contracts; 13 | 14 | interface JWTSubject 15 | { 16 | /** 17 | * Get the identifier that will be stored in the subject claim of the JWT. 18 | * 19 | * @return mixed 20 | */ 21 | public function getJWTIdentifier(); 22 | 23 | /** 24 | * Return a key value array, containing any custom claims to be added to the JWT. 25 | * 26 | * @return array 27 | */ 28 | public function getJWTCustomClaims(); 29 | } 30 | -------------------------------------------------------------------------------- /src/Contracts/Providers/Auth.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Tymon\JWTAuth\Contracts\Providers; 13 | 14 | interface Auth 15 | { 16 | /** 17 | * Check a user's credentials. 18 | * 19 | * @param array $credentials 20 | * @return mixed 21 | */ 22 | public function byCredentials(array $credentials); 23 | 24 | /** 25 | * Authenticate a user via the id. 26 | * 27 | * @param mixed $id 28 | * @return mixed 29 | */ 30 | public function byId($id); 31 | 32 | /** 33 | * Get the currently authenticated user. 34 | * 35 | * @return mixed 36 | */ 37 | public function user(); 38 | } 39 | -------------------------------------------------------------------------------- /src/Contracts/Providers/JWT.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Tymon\JWTAuth\Contracts\Providers; 13 | 14 | interface JWT 15 | { 16 | /** 17 | * @param array $payload 18 | * @return string 19 | */ 20 | public function encode(array $payload); 21 | 22 | /** 23 | * @param string $token 24 | * @return array 25 | */ 26 | public function decode($token); 27 | } 28 | -------------------------------------------------------------------------------- /src/Contracts/Providers/Storage.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Tymon\JWTAuth\Contracts\Providers; 13 | 14 | interface Storage 15 | { 16 | /** 17 | * @param string $key 18 | * @param mixed $value 19 | * @param int $minutes 20 | * @return void 21 | */ 22 | public function add($key, $value, $minutes); 23 | 24 | /** 25 | * @param string $key 26 | * @param mixed $value 27 | * @return void 28 | */ 29 | public function forever($key, $value); 30 | 31 | /** 32 | * @param string $key 33 | * @return mixed 34 | */ 35 | public function get($key); 36 | 37 | /** 38 | * @param string $key 39 | * @return bool 40 | */ 41 | public function destroy($key); 42 | 43 | /** 44 | * @return void 45 | */ 46 | public function flush(); 47 | } 48 | -------------------------------------------------------------------------------- /src/Contracts/Validator.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Tymon\JWTAuth\Contracts; 13 | 14 | interface Validator 15 | { 16 | /** 17 | * Perform some checks on the value. 18 | * 19 | * @param mixed $value 20 | * @return void 21 | */ 22 | public function check($value); 23 | 24 | /** 25 | * Helper function to return a boolean. 26 | * 27 | * @param array $value 28 | * @return bool 29 | */ 30 | public function isValid($value); 31 | } 32 | -------------------------------------------------------------------------------- /src/Exceptions/InvalidClaimException.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Tymon\JWTAuth\Exceptions; 13 | 14 | use Exception; 15 | use Tymon\JWTAuth\Claims\Claim; 16 | 17 | class InvalidClaimException extends JWTException 18 | { 19 | /** 20 | * Constructor. 21 | * 22 | * @param \Tymon\JWTAuth\Claims\Claim $claim 23 | * @param int $code 24 | * @param \Exception|null $previous 25 | * @return void 26 | */ 27 | public function __construct(Claim $claim, $code = 0, ?Exception $previous = null) 28 | { 29 | parent::__construct('Invalid value provided for claim ['.$claim->getName().']', $code, $previous); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Exceptions/JWTException.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Tymon\JWTAuth\Exceptions; 13 | 14 | use Exception; 15 | 16 | class JWTException extends Exception 17 | { 18 | /** 19 | * {@inheritdoc} 20 | */ 21 | protected $message = 'An error occurred'; 22 | } 23 | -------------------------------------------------------------------------------- /src/Exceptions/PayloadException.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Tymon\JWTAuth\Exceptions; 13 | 14 | class PayloadException extends JWTException 15 | { 16 | // 17 | } 18 | -------------------------------------------------------------------------------- /src/Exceptions/TokenBlacklistedException.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Tymon\JWTAuth\Exceptions; 13 | 14 | class TokenBlacklistedException extends TokenInvalidException 15 | { 16 | // 17 | } 18 | -------------------------------------------------------------------------------- /src/Exceptions/TokenExpiredException.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Tymon\JWTAuth\Exceptions; 13 | 14 | class TokenExpiredException extends JWTException 15 | { 16 | // 17 | } 18 | -------------------------------------------------------------------------------- /src/Exceptions/TokenInvalidException.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Tymon\JWTAuth\Exceptions; 13 | 14 | class TokenInvalidException extends JWTException 15 | { 16 | // 17 | } 18 | -------------------------------------------------------------------------------- /src/Exceptions/UserNotDefinedException.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Tymon\JWTAuth\Exceptions; 13 | 14 | class UserNotDefinedException extends JWTException 15 | { 16 | // 17 | } 18 | -------------------------------------------------------------------------------- /src/Facades/JWTAuth.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Tymon\JWTAuth\Facades; 13 | 14 | use Illuminate\Support\Facades\Facade; 15 | 16 | class JWTAuth extends Facade 17 | { 18 | /** 19 | * Get the registered name of the component. 20 | * 21 | * @return string 22 | */ 23 | protected static function getFacadeAccessor() 24 | { 25 | return 'tymon.jwt.auth'; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Facades/JWTFactory.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Tymon\JWTAuth\Facades; 13 | 14 | use Illuminate\Support\Facades\Facade; 15 | 16 | class JWTFactory extends Facade 17 | { 18 | /** 19 | * Get the registered name of the component. 20 | * 21 | * @return string 22 | */ 23 | protected static function getFacadeAccessor() 24 | { 25 | return 'tymon.jwt.payload.factory'; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Facades/JWTProvider.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Tymon\JWTAuth\Facades; 13 | 14 | use Illuminate\Support\Facades\Facade; 15 | 16 | class JWTProvider extends Facade 17 | { 18 | /** 19 | * Get the registered name of the component. 20 | * 21 | * @return string 22 | */ 23 | protected static function getFacadeAccessor() 24 | { 25 | return 'tymon.jwt.provider.jwt'; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Factory.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Tymon\JWTAuth; 13 | 14 | use Tymon\JWTAuth\Claims\Claim; 15 | use Tymon\JWTAuth\Claims\Collection; 16 | use Tymon\JWTAuth\Claims\Factory as ClaimFactory; 17 | use Tymon\JWTAuth\Support\CustomClaims; 18 | use Tymon\JWTAuth\Support\RefreshFlow; 19 | use Tymon\JWTAuth\Validators\PayloadValidator; 20 | 21 | class Factory 22 | { 23 | use CustomClaims, RefreshFlow; 24 | 25 | /** 26 | * The claim factory. 27 | * 28 | * @var \Tymon\JWTAuth\Claims\Factory 29 | */ 30 | protected $claimFactory; 31 | 32 | /** 33 | * The validator. 34 | * 35 | * @var \Tymon\JWTAuth\Validators\PayloadValidator 36 | */ 37 | protected $validator; 38 | 39 | /** 40 | * The default claims. 41 | * 42 | * @var array 43 | */ 44 | protected $defaultClaims = [ 45 | 'iss', 46 | 'iat', 47 | 'exp', 48 | 'nbf', 49 | 'jti', 50 | ]; 51 | 52 | /** 53 | * The claims collection. 54 | * 55 | * @var \Tymon\JWTAuth\Claims\Collection 56 | */ 57 | protected $claims; 58 | 59 | /** 60 | * Constructor. 61 | * 62 | * @param \Tymon\JWTAuth\Claims\Factory $claimFactory 63 | * @param \Tymon\JWTAuth\Validators\PayloadValidator $validator 64 | * @return void 65 | */ 66 | public function __construct(ClaimFactory $claimFactory, PayloadValidator $validator) 67 | { 68 | $this->claimFactory = $claimFactory; 69 | $this->validator = $validator; 70 | $this->claims = new Collection; 71 | } 72 | 73 | /** 74 | * Create the Payload instance. 75 | * 76 | * @param bool $resetClaims 77 | * @return \Tymon\JWTAuth\Payload 78 | */ 79 | public function make($resetClaims = false) 80 | { 81 | if ($resetClaims) { 82 | $this->emptyClaims(); 83 | } 84 | 85 | return $this->withClaims($this->buildClaimsCollection()); 86 | } 87 | 88 | /** 89 | * Empty the claims collection. 90 | * 91 | * @return $this 92 | */ 93 | public function emptyClaims() 94 | { 95 | $this->claims = new Collection; 96 | 97 | return $this; 98 | } 99 | 100 | /** 101 | * Add an array of claims to the Payload. 102 | * 103 | * @param array $claims 104 | * @return $this 105 | */ 106 | protected function addClaims(array $claims) 107 | { 108 | foreach ($claims as $name => $value) { 109 | $this->addClaim($name, $value); 110 | } 111 | 112 | return $this; 113 | } 114 | 115 | /** 116 | * Add a claim to the Payload. 117 | * 118 | * @param string $name 119 | * @param mixed $value 120 | * @return $this 121 | */ 122 | protected function addClaim($name, $value) 123 | { 124 | $this->claims->put($name, $value); 125 | 126 | return $this; 127 | } 128 | 129 | /** 130 | * Build the default claims. 131 | * 132 | * @return $this 133 | */ 134 | protected function buildClaims() 135 | { 136 | // remove the exp claim if it exists and the ttl is null 137 | if ($this->claimFactory->getTTL() === null && $key = array_search('exp', $this->defaultClaims)) { 138 | unset($this->defaultClaims[$key]); 139 | } 140 | 141 | // add the default claims 142 | foreach ($this->defaultClaims as $claim) { 143 | $this->addClaim($claim, $this->claimFactory->make($claim)); 144 | } 145 | 146 | // add custom claims on top, allowing them to overwrite defaults 147 | return $this->addClaims($this->getCustomClaims()); 148 | } 149 | 150 | /** 151 | * Build out the Claim DTO's. 152 | * 153 | * @return \Tymon\JWTAuth\Claims\Collection 154 | */ 155 | protected function resolveClaims() 156 | { 157 | return $this->claims->map(function ($value, $name) { 158 | return $value instanceof Claim ? $value : $this->claimFactory->get($name, $value); 159 | }); 160 | } 161 | 162 | /** 163 | * Build and get the Claims Collection. 164 | * 165 | * @return \Tymon\JWTAuth\Claims\Collection 166 | */ 167 | public function buildClaimsCollection() 168 | { 169 | return $this->buildClaims()->resolveClaims(); 170 | } 171 | 172 | /** 173 | * Get a Payload instance with a claims collection. 174 | * 175 | * @param \Tymon\JWTAuth\Claims\Collection $claims 176 | * @return \Tymon\JWTAuth\Payload 177 | */ 178 | public function withClaims(Collection $claims) 179 | { 180 | return new Payload($claims, $this->validator, $this->refreshFlow); 181 | } 182 | 183 | /** 184 | * Set the default claims to be added to the Payload. 185 | * 186 | * @param array $claims 187 | * @return $this 188 | */ 189 | public function setDefaultClaims(array $claims) 190 | { 191 | $this->defaultClaims = $claims; 192 | 193 | return $this; 194 | } 195 | 196 | /** 197 | * Helper to set the ttl. 198 | * 199 | * @param int $ttl 200 | * @return $this 201 | */ 202 | public function setTTL($ttl) 203 | { 204 | $this->claimFactory->setTTL($ttl); 205 | 206 | return $this; 207 | } 208 | 209 | /** 210 | * Helper to get the ttl. 211 | * 212 | * @return int 213 | */ 214 | public function getTTL() 215 | { 216 | return $this->claimFactory->getTTL(); 217 | } 218 | 219 | /** 220 | * Get the default claims. 221 | * 222 | * @return array 223 | */ 224 | public function getDefaultClaims() 225 | { 226 | return $this->defaultClaims; 227 | } 228 | 229 | /** 230 | * Get the PayloadValidator instance. 231 | * 232 | * @return \Tymon\JWTAuth\Validators\PayloadValidator 233 | */ 234 | public function validator() 235 | { 236 | return $this->validator; 237 | } 238 | 239 | /** 240 | * Magically add a claim. 241 | * 242 | * @param string $method 243 | * @param array $parameters 244 | * @return $this 245 | */ 246 | public function __call($method, $parameters) 247 | { 248 | $this->addClaim($method, $parameters[0]); 249 | 250 | return $this; 251 | } 252 | } 253 | -------------------------------------------------------------------------------- /src/Http/Middleware/Authenticate.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Tymon\JWTAuth\Http\Middleware; 13 | 14 | use Closure; 15 | 16 | /** @deprecated */ 17 | class Authenticate extends BaseMiddleware 18 | { 19 | /** 20 | * Handle an incoming request. 21 | * 22 | * @param \Illuminate\Http\Request $request 23 | * @param \Closure $next 24 | * @return mixed 25 | * 26 | * @throws \Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException 27 | */ 28 | public function handle($request, Closure $next) 29 | { 30 | $this->authenticate($request); 31 | 32 | return $next($request); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Http/Middleware/AuthenticateAndRenew.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Tymon\JWTAuth\Http\Middleware; 13 | 14 | use Closure; 15 | 16 | /** @deprecated */ 17 | class AuthenticateAndRenew extends BaseMiddleware 18 | { 19 | /** 20 | * Handle an incoming request. 21 | * 22 | * @param \Illuminate\Http\Request $request 23 | * @param \Closure $next 24 | * @return mixed 25 | * 26 | * @throws \Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException 27 | */ 28 | public function handle($request, Closure $next) 29 | { 30 | $this->authenticate($request); 31 | 32 | $response = $next($request); 33 | 34 | // Send the refreshed token back to the client. 35 | return $this->setAuthenticationHeader($response); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Http/Middleware/BaseMiddleware.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Tymon\JWTAuth\Http\Middleware; 13 | 14 | use Illuminate\Http\Request; 15 | use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException; 16 | use Tymon\JWTAuth\Exceptions\JWTException; 17 | use Tymon\JWTAuth\JWTAuth; 18 | 19 | /** @deprecated */ 20 | abstract class BaseMiddleware 21 | { 22 | /** 23 | * The JWT Authenticator. 24 | * 25 | * @var \Tymon\JWTAuth\JWTAuth 26 | */ 27 | protected $auth; 28 | 29 | /** 30 | * Create a new BaseMiddleware instance. 31 | * 32 | * @param \Tymon\JWTAuth\JWTAuth $auth 33 | * @return void 34 | */ 35 | public function __construct(JWTAuth $auth) 36 | { 37 | $this->auth = $auth; 38 | } 39 | 40 | /** 41 | * Check the request for the presence of a token. 42 | * 43 | * @param \Illuminate\Http\Request $request 44 | * @return void 45 | * 46 | * @throws \Symfony\Component\HttpKernel\Exception\BadRequestHttpException 47 | */ 48 | public function checkForToken(Request $request) 49 | { 50 | if (! $this->auth->parser()->setRequest($request)->hasToken()) { 51 | throw new UnauthorizedHttpException('jwt-auth', 'Token not provided'); 52 | } 53 | } 54 | 55 | /** 56 | * Attempt to authenticate a user via the token in the request. 57 | * 58 | * @param \Illuminate\Http\Request $request 59 | * @return void 60 | * 61 | * @throws \Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException 62 | */ 63 | public function authenticate(Request $request) 64 | { 65 | $this->checkForToken($request); 66 | 67 | try { 68 | if (! $this->auth->parseToken()->authenticate()) { 69 | throw new UnauthorizedHttpException('jwt-auth', 'User not found'); 70 | } 71 | } catch (JWTException $e) { 72 | throw new UnauthorizedHttpException('jwt-auth', $e->getMessage(), $e, $e->getCode()); 73 | } 74 | } 75 | 76 | /** 77 | * Set the authentication header. 78 | * 79 | * @param \Illuminate\Http\Response|\Illuminate\Http\JsonResponse $response 80 | * @param string|null $token 81 | * @return \Illuminate\Http\Response|\Illuminate\Http\JsonResponse 82 | */ 83 | protected function setAuthenticationHeader($response, $token = null) 84 | { 85 | $token = $token ?: $this->auth->refresh(); 86 | $response->headers->set('Authorization', 'Bearer '.$token); 87 | 88 | return $response; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/Http/Middleware/Check.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Tymon\JWTAuth\Http\Middleware; 13 | 14 | use Closure; 15 | use Exception; 16 | 17 | /** @deprecated */ 18 | class Check extends BaseMiddleware 19 | { 20 | /** 21 | * Handle an incoming request. 22 | * 23 | * @param \Illuminate\Http\Request $request 24 | * @param \Closure $next 25 | * @return mixed 26 | */ 27 | public function handle($request, Closure $next) 28 | { 29 | if ($this->auth->parser()->setRequest($request)->hasToken()) { 30 | try { 31 | $this->auth->parseToken()->authenticate(); 32 | } catch (Exception $e) { 33 | // 34 | } 35 | } 36 | 37 | return $next($request); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Http/Middleware/RefreshToken.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Tymon\JWTAuth\Http\Middleware; 13 | 14 | use Closure; 15 | use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException; 16 | use Tymon\JWTAuth\Exceptions\JWTException; 17 | 18 | /** @deprecated */ 19 | class RefreshToken extends BaseMiddleware 20 | { 21 | /** 22 | * Handle an incoming request. 23 | * 24 | * @param \Illuminate\Http\Request $request 25 | * @param \Closure $next 26 | * @return mixed 27 | * 28 | * @throws \Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException 29 | */ 30 | public function handle($request, Closure $next) 31 | { 32 | $this->checkForToken($request); 33 | 34 | try { 35 | $token = $this->auth->parseToken()->refresh(); 36 | } catch (JWTException $e) { 37 | throw new UnauthorizedHttpException('jwt-auth', $e->getMessage(), $e, $e->getCode()); 38 | } 39 | 40 | $response = $next($request); 41 | 42 | // Send the refreshed token back to the client. 43 | return $this->setAuthenticationHeader($response, $token); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/Http/Parser/AuthHeaders.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Tymon\JWTAuth\Http\Parser; 13 | 14 | use Illuminate\Http\Request; 15 | use Tymon\JWTAuth\Contracts\Http\Parser as ParserContract; 16 | 17 | class AuthHeaders implements ParserContract 18 | { 19 | /** 20 | * The header name. 21 | * 22 | * @var string 23 | */ 24 | protected $header = 'authorization'; 25 | 26 | /** 27 | * The header prefix. 28 | * 29 | * @var string 30 | */ 31 | protected $prefix = 'bearer'; 32 | 33 | /** 34 | * Attempt to parse the token from some other possible headers. 35 | * 36 | * @param \Illuminate\Http\Request $request 37 | * @return null|string 38 | */ 39 | protected function fromAltHeaders(Request $request) 40 | { 41 | return $request->server->get('HTTP_AUTHORIZATION') ?: $request->server->get('REDIRECT_HTTP_AUTHORIZATION'); 42 | } 43 | 44 | /** 45 | * Try to parse the token from the request header. 46 | * 47 | * @param \Illuminate\Http\Request $request 48 | * @return null|string 49 | */ 50 | public function parse(Request $request) 51 | { 52 | $header = $request->headers->get($this->header) ?: $this->fromAltHeaders($request); 53 | 54 | if ($header !== null) { 55 | $position = strripos($header, $this->prefix); 56 | 57 | if ($position !== false) { 58 | $header = substr($header, $position + strlen($this->prefix)); 59 | 60 | return trim( 61 | strpos($header, ',') !== false ? strstr($header, ',', true) : $header 62 | ); 63 | } 64 | } 65 | 66 | return null; 67 | } 68 | 69 | /** 70 | * Set the header name. 71 | * 72 | * @param string $headerName 73 | * @return $this 74 | */ 75 | public function setHeaderName($headerName) 76 | { 77 | $this->header = $headerName; 78 | 79 | return $this; 80 | } 81 | 82 | /** 83 | * Set the header prefix. 84 | * 85 | * @param string $headerPrefix 86 | * @return $this 87 | */ 88 | public function setHeaderPrefix($headerPrefix) 89 | { 90 | $this->prefix = $headerPrefix; 91 | 92 | return $this; 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/Http/Parser/Cookies.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Tymon\JWTAuth\Http\Parser; 13 | 14 | use Illuminate\Http\Request; 15 | use Illuminate\Support\Facades\Crypt; 16 | use Tymon\JWTAuth\Contracts\Http\Parser as ParserContract; 17 | 18 | class Cookies implements ParserContract 19 | { 20 | use KeyTrait; 21 | 22 | /** 23 | * Decrypt or not the cookie while parsing. 24 | * 25 | * @var bool 26 | */ 27 | private $decrypt; 28 | 29 | public function __construct($decrypt = true) 30 | { 31 | $this->decrypt = $decrypt; 32 | } 33 | 34 | /** 35 | * Try to parse the token from the request cookies. 36 | * 37 | * @param \Illuminate\Http\Request $request 38 | * @return null|string 39 | */ 40 | public function parse(Request $request) 41 | { 42 | if ($this->decrypt && $request->hasCookie($this->key)) { 43 | return Crypt::decrypt($request->cookie($this->key)); 44 | } 45 | 46 | return $request->cookie($this->key); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/Http/Parser/InputSource.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Tymon\JWTAuth\Http\Parser; 13 | 14 | use Illuminate\Http\Request; 15 | use Tymon\JWTAuth\Contracts\Http\Parser as ParserContract; 16 | 17 | class InputSource implements ParserContract 18 | { 19 | use KeyTrait; 20 | 21 | /** 22 | * Try to parse the token from the request input source. 23 | * 24 | * @param \Illuminate\Http\Request $request 25 | * @return null|string 26 | */ 27 | public function parse(Request $request) 28 | { 29 | return $request->input($this->key); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Http/Parser/KeyTrait.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Tymon\JWTAuth\Http\Parser; 13 | 14 | trait KeyTrait 15 | { 16 | /** 17 | * The key. 18 | * 19 | * @var string 20 | */ 21 | protected $key = 'token'; 22 | 23 | /** 24 | * Set the key. 25 | * 26 | * @param string $key 27 | * @return $this 28 | */ 29 | public function setKey($key) 30 | { 31 | $this->key = $key; 32 | 33 | return $this; 34 | } 35 | 36 | /** 37 | * Get the key. 38 | * 39 | * @return string 40 | */ 41 | public function getKey() 42 | { 43 | return $this->key; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/Http/Parser/LumenRouteParams.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Tymon\JWTAuth\Http\Parser; 13 | 14 | use Illuminate\Http\Request; 15 | use Illuminate\Support\Arr; 16 | 17 | class LumenRouteParams extends RouteParams 18 | { 19 | /** 20 | * Try to get the token from the route parameters. 21 | * 22 | * @param \Illuminate\Http\Request $request 23 | * @return null|string 24 | */ 25 | public function parse(Request $request) 26 | { 27 | // WARNING: Only use this parser if you know what you're doing! 28 | // It will only work with poorly-specified aspects of certain Lumen releases. 29 | // Route is the expected kind of array, and has a parameter with the key we want. 30 | return Arr::get($request->route(), '2.'.$this->key); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Http/Parser/Parser.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Tymon\JWTAuth\Http\Parser; 13 | 14 | use Illuminate\Http\Request; 15 | 16 | class Parser 17 | { 18 | /** 19 | * The chain. 20 | * 21 | * @var array 22 | */ 23 | private $chain; 24 | 25 | /** 26 | * The request. 27 | * 28 | * @var \Illuminate\Http\Request 29 | */ 30 | protected $request; 31 | 32 | /** 33 | * Constructor. 34 | * 35 | * @param \Illuminate\Http\Request $request 36 | * @param array $chain 37 | * @return void 38 | */ 39 | public function __construct(Request $request, array $chain = []) 40 | { 41 | $this->request = $request; 42 | $this->chain = $chain; 43 | } 44 | 45 | /** 46 | * Get the parser chain. 47 | * 48 | * @return array 49 | */ 50 | public function getChain() 51 | { 52 | return $this->chain; 53 | } 54 | 55 | /** 56 | * Add a new parser to the chain. 57 | * 58 | * @param array|\Tymon\JWTAuth\Contracts\Http\Parser $parsers 59 | * @return $this 60 | */ 61 | public function addParser($parsers) 62 | { 63 | $this->chain = array_merge($this->chain, is_array($parsers) ? $parsers : [$parsers]); 64 | 65 | return $this; 66 | } 67 | 68 | /** 69 | * Set the order of the parser chain. 70 | * 71 | * @param array $chain 72 | * @return $this 73 | */ 74 | public function setChain(array $chain) 75 | { 76 | $this->chain = $chain; 77 | 78 | return $this; 79 | } 80 | 81 | /** 82 | * Alias for setting the order of the chain. 83 | * 84 | * @param array $chain 85 | * @return $this 86 | */ 87 | public function setChainOrder(array $chain) 88 | { 89 | return $this->setChain($chain); 90 | } 91 | 92 | /** 93 | * Iterate through the parsers and attempt to retrieve 94 | * a value, otherwise return null. 95 | * 96 | * @return string|null 97 | */ 98 | public function parseToken() 99 | { 100 | foreach ($this->chain as $parser) { 101 | if ($response = $parser->parse($this->request)) { 102 | return $response; 103 | } 104 | } 105 | } 106 | 107 | /** 108 | * Check whether a token exists in the chain. 109 | * 110 | * @return bool 111 | */ 112 | public function hasToken() 113 | { 114 | return $this->parseToken() !== null; 115 | } 116 | 117 | /** 118 | * Set the request instance. 119 | * 120 | * @param \Illuminate\Http\Request $request 121 | * @return $this 122 | */ 123 | public function setRequest(Request $request) 124 | { 125 | $this->request = $request; 126 | 127 | return $this; 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /src/Http/Parser/QueryString.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Tymon\JWTAuth\Http\Parser; 13 | 14 | use Illuminate\Http\Request; 15 | use Tymon\JWTAuth\Contracts\Http\Parser as ParserContract; 16 | 17 | class QueryString implements ParserContract 18 | { 19 | use KeyTrait; 20 | 21 | /** 22 | * Try to parse the token from the request query string. 23 | * 24 | * @param \Illuminate\Http\Request $request 25 | * @return null|string 26 | */ 27 | public function parse(Request $request) 28 | { 29 | return $request->query($this->key); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Http/Parser/RouteParams.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Tymon\JWTAuth\Http\Parser; 13 | 14 | use Illuminate\Http\Request; 15 | use Tymon\JWTAuth\Contracts\Http\Parser as ParserContract; 16 | 17 | class RouteParams implements ParserContract 18 | { 19 | use KeyTrait; 20 | 21 | /** 22 | * Try to get the token from the route parameters. 23 | * 24 | * @param \Illuminate\Http\Request $request 25 | * @return null|string 26 | */ 27 | public function parse(Request $request) 28 | { 29 | $route = $request->route(); 30 | 31 | // Route may not be an instance of Illuminate\Routing\Route 32 | // (it's an array in Lumen <5.2) or not exist at all 33 | // (if the request was never dispatched) 34 | if (is_callable([$route, 'parameter'])) { 35 | return $route->parameter($this->key); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/JWT.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Tymon\JWTAuth; 13 | 14 | use BadMethodCallException; 15 | use Illuminate\Http\Request; 16 | use Tymon\JWTAuth\Contracts\JWTSubject; 17 | use Tymon\JWTAuth\Exceptions\JWTException; 18 | use Tymon\JWTAuth\Http\Parser\Parser; 19 | use Tymon\JWTAuth\Support\CustomClaims; 20 | 21 | class JWT 22 | { 23 | use CustomClaims; 24 | 25 | /** 26 | * The authentication manager. 27 | * 28 | * @var \Tymon\JWTAuth\Manager 29 | */ 30 | protected $manager; 31 | 32 | /** 33 | * The HTTP parser. 34 | * 35 | * @var \Tymon\JWTAuth\Http\Parser\Parser 36 | */ 37 | protected $parser; 38 | 39 | /** 40 | * The token. 41 | * 42 | * @var \Tymon\JWTAuth\Token|null 43 | */ 44 | protected $token; 45 | 46 | /** 47 | * Lock the subject. 48 | * 49 | * @var bool 50 | */ 51 | protected $lockSubject = true; 52 | 53 | /** 54 | * JWT constructor. 55 | * 56 | * @param \Tymon\JWTAuth\Manager $manager 57 | * @param \Tymon\JWTAuth\Http\Parser\Parser $parser 58 | * @return void 59 | */ 60 | public function __construct(Manager $manager, Parser $parser) 61 | { 62 | $this->manager = $manager; 63 | $this->parser = $parser; 64 | } 65 | 66 | /** 67 | * Generate a token for a given subject. 68 | * 69 | * @param \Tymon\JWTAuth\Contracts\JWTSubject $subject 70 | * @return string 71 | */ 72 | public function fromSubject(JWTSubject $subject) 73 | { 74 | $payload = $this->makePayload($subject); 75 | 76 | return $this->manager->encode($payload)->get(); 77 | } 78 | 79 | /** 80 | * Alias to generate a token for a given user. 81 | * 82 | * @param \Tymon\JWTAuth\Contracts\JWTSubject $user 83 | * @return string 84 | */ 85 | public function fromUser(JWTSubject $user) 86 | { 87 | return $this->fromSubject($user); 88 | } 89 | 90 | /** 91 | * Refresh an expired token. 92 | * 93 | * @param bool $forceForever 94 | * @param bool $resetClaims 95 | * @return string 96 | */ 97 | public function refresh($forceForever = false, $resetClaims = false) 98 | { 99 | $this->requireToken(); 100 | 101 | return $this->manager->customClaims($this->getCustomClaims()) 102 | ->refresh($this->token, $forceForever, $resetClaims) 103 | ->get(); 104 | } 105 | 106 | /** 107 | * Invalidate a token (add it to the blacklist). 108 | * 109 | * @param bool $forceForever 110 | * @return $this 111 | */ 112 | public function invalidate($forceForever = false) 113 | { 114 | $this->requireToken(); 115 | 116 | $this->manager->invalidate($this->token, $forceForever); 117 | 118 | return $this; 119 | } 120 | 121 | /** 122 | * Alias to get the payload, and as a result checks that 123 | * the token is valid i.e. not expired or blacklisted. 124 | * 125 | * @return \Tymon\JWTAuth\Payload 126 | * 127 | * @throws \Tymon\JWTAuth\Exceptions\JWTException 128 | */ 129 | public function checkOrFail() 130 | { 131 | return $this->getPayload(); 132 | } 133 | 134 | /** 135 | * Check that the token is valid. 136 | * 137 | * @param bool $getPayload 138 | * @return \Tymon\JWTAuth\Payload|bool 139 | */ 140 | public function check($getPayload = false) 141 | { 142 | try { 143 | $payload = $this->checkOrFail(); 144 | } catch (JWTException $e) { 145 | return false; 146 | } 147 | 148 | return $getPayload ? $payload : true; 149 | } 150 | 151 | /** 152 | * Get the token. 153 | * 154 | * @return \Tymon\JWTAuth\Token|null 155 | */ 156 | public function getToken() 157 | { 158 | if ($this->token === null) { 159 | try { 160 | $this->parseToken(); 161 | } catch (JWTException $e) { 162 | $this->token = null; 163 | } 164 | } 165 | 166 | return $this->token; 167 | } 168 | 169 | /** 170 | * Parse the token from the request. 171 | * 172 | * @return $this 173 | * 174 | * @throws \Tymon\JWTAuth\Exceptions\JWTException 175 | */ 176 | public function parseToken() 177 | { 178 | if (! $token = $this->parser->parseToken()) { 179 | throw new JWTException('The token could not be parsed from the request'); 180 | } 181 | 182 | return $this->setToken($token); 183 | } 184 | 185 | /** 186 | * Get the raw Payload instance. 187 | * 188 | * @return \Tymon\JWTAuth\Payload 189 | */ 190 | public function getPayload() 191 | { 192 | $this->requireToken(); 193 | 194 | return $this->manager->decode($this->token); 195 | } 196 | 197 | /** 198 | * Alias for getPayload(). 199 | * 200 | * @return \Tymon\JWTAuth\Payload 201 | */ 202 | public function payload() 203 | { 204 | return $this->getPayload(); 205 | } 206 | 207 | /** 208 | * Convenience method to get a claim value. 209 | * 210 | * @param string $claim 211 | * @return mixed 212 | */ 213 | public function getClaim($claim) 214 | { 215 | return $this->payload()->get($claim); 216 | } 217 | 218 | /** 219 | * Create a Payload instance. 220 | * 221 | * @param \Tymon\JWTAuth\Contracts\JWTSubject $subject 222 | * @return \Tymon\JWTAuth\Payload 223 | */ 224 | public function makePayload(JWTSubject $subject) 225 | { 226 | return $this->factory()->customClaims($this->getClaimsArray($subject))->make(); 227 | } 228 | 229 | /** 230 | * Build the claims array and return it. 231 | * 232 | * @param \Tymon\JWTAuth\Contracts\JWTSubject $subject 233 | * @return array 234 | */ 235 | protected function getClaimsArray(JWTSubject $subject) 236 | { 237 | return array_merge( 238 | $this->getClaimsForSubject($subject), 239 | $subject->getJWTCustomClaims(), // custom claims from JWTSubject method 240 | $this->customClaims // custom claims from inline setter 241 | ); 242 | } 243 | 244 | /** 245 | * Get the claims associated with a given subject. 246 | * 247 | * @param \Tymon\JWTAuth\Contracts\JWTSubject $subject 248 | * @return array 249 | */ 250 | protected function getClaimsForSubject(JWTSubject $subject) 251 | { 252 | return array_merge([ 253 | 'sub' => $subject->getJWTIdentifier(), 254 | ], $this->lockSubject ? ['prv' => $this->hashSubjectModel($subject)] : []); 255 | } 256 | 257 | /** 258 | * Hash the subject model and return it. 259 | * 260 | * @param string|object $model 261 | * @return string 262 | */ 263 | protected function hashSubjectModel($model) 264 | { 265 | return sha1(is_object($model) ? get_class($model) : $model); 266 | } 267 | 268 | /** 269 | * Check if the subject model matches the one saved in the token. 270 | * 271 | * @param string|object $model 272 | * @return bool 273 | */ 274 | public function checkSubjectModel($model) 275 | { 276 | if (($prv = $this->payload()->get('prv')) === null) { 277 | return true; 278 | } 279 | 280 | return $this->hashSubjectModel($model) === $prv; 281 | } 282 | 283 | /** 284 | * Set the token. 285 | * 286 | * @param \Tymon\JWTAuth\Token|string $token 287 | * @return $this 288 | */ 289 | public function setToken($token) 290 | { 291 | $this->token = $token instanceof Token ? $token : new Token($token); 292 | 293 | return $this; 294 | } 295 | 296 | /** 297 | * Unset the current token. 298 | * 299 | * @return $this 300 | */ 301 | public function unsetToken() 302 | { 303 | $this->token = null; 304 | 305 | return $this; 306 | } 307 | 308 | /** 309 | * Ensure that a token is available. 310 | * 311 | * @return void 312 | * 313 | * @throws \Tymon\JWTAuth\Exceptions\JWTException 314 | */ 315 | protected function requireToken() 316 | { 317 | if (! $this->token) { 318 | throw new JWTException('A token is required'); 319 | } 320 | } 321 | 322 | /** 323 | * Set the request instance. 324 | * 325 | * @param \Illuminate\Http\Request $request 326 | * @return $this 327 | */ 328 | public function setRequest(Request $request) 329 | { 330 | $this->parser->setRequest($request); 331 | 332 | return $this; 333 | } 334 | 335 | /** 336 | * Set whether the subject should be "locked". 337 | * 338 | * @param bool $lock 339 | * @return $this 340 | */ 341 | public function lockSubject($lock) 342 | { 343 | $this->lockSubject = $lock; 344 | 345 | return $this; 346 | } 347 | 348 | /** 349 | * Get the Manager instance. 350 | * 351 | * @return \Tymon\JWTAuth\Manager 352 | */ 353 | public function manager() 354 | { 355 | return $this->manager; 356 | } 357 | 358 | /** 359 | * Get the Parser instance. 360 | * 361 | * @return \Tymon\JWTAuth\Http\Parser\Parser 362 | */ 363 | public function parser() 364 | { 365 | return $this->parser; 366 | } 367 | 368 | /** 369 | * Get the Payload Factory. 370 | * 371 | * @return \Tymon\JWTAuth\Factory 372 | */ 373 | public function factory() 374 | { 375 | return $this->manager->getPayloadFactory(); 376 | } 377 | 378 | /** 379 | * Get the Blacklist. 380 | * 381 | * @return \Tymon\JWTAuth\Blacklist 382 | */ 383 | public function blacklist() 384 | { 385 | return $this->manager->getBlacklist(); 386 | } 387 | 388 | /** 389 | * Magically call the JWT Manager. 390 | * 391 | * @param string $method 392 | * @param array $parameters 393 | * @return mixed 394 | * 395 | * @throws \BadMethodCallException 396 | */ 397 | public function __call($method, $parameters) 398 | { 399 | if (method_exists($this->manager, $method)) { 400 | return call_user_func_array([$this->manager, $method], $parameters); 401 | } 402 | 403 | throw new BadMethodCallException("Method [$method] does not exist."); 404 | } 405 | } 406 | -------------------------------------------------------------------------------- /src/JWTAuth.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Tymon\JWTAuth; 13 | 14 | use Tymon\JWTAuth\Contracts\Providers\Auth; 15 | use Tymon\JWTAuth\Http\Parser\Parser; 16 | 17 | /** @deprecated */ 18 | class JWTAuth extends JWT 19 | { 20 | /** 21 | * The authentication provider. 22 | * 23 | * @var \Tymon\JWTAuth\Contracts\Providers\Auth 24 | */ 25 | protected $auth; 26 | 27 | /** 28 | * Constructor. 29 | * 30 | * @param \Tymon\JWTAuth\Manager $manager 31 | * @param \Tymon\JWTAuth\Contracts\Providers\Auth $auth 32 | * @param \Tymon\JWTAuth\Http\Parser\Parser $parser 33 | * @return void 34 | */ 35 | public function __construct(Manager $manager, Auth $auth, Parser $parser) 36 | { 37 | parent::__construct($manager, $parser); 38 | $this->auth = $auth; 39 | } 40 | 41 | /** 42 | * Attempt to authenticate the user and return the token. 43 | * 44 | * @param array $credentials 45 | * @return false|string 46 | */ 47 | public function attempt(array $credentials) 48 | { 49 | if (! $this->auth->byCredentials($credentials)) { 50 | return false; 51 | } 52 | 53 | return $this->fromUser($this->user()); 54 | } 55 | 56 | /** 57 | * Authenticate a user via a token. 58 | * 59 | * @return \Tymon\JWTAuth\Contracts\JWTSubject|false 60 | */ 61 | public function authenticate() 62 | { 63 | $id = $this->getPayload()->get('sub'); 64 | 65 | if (! $this->auth->byId($id)) { 66 | return false; 67 | } 68 | 69 | return $this->user(); 70 | } 71 | 72 | /** 73 | * Alias for authenticate(). 74 | * 75 | * @return \Tymon\JWTAuth\Contracts\JWTSubject|false 76 | */ 77 | public function toUser() 78 | { 79 | return $this->authenticate(); 80 | } 81 | 82 | /** 83 | * Get the authenticated user. 84 | * 85 | * @return \Tymon\JWTAuth\Contracts\JWTSubject 86 | */ 87 | public function user() 88 | { 89 | return $this->auth->user(); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/JWTGuard.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Tymon\JWTAuth; 13 | 14 | use BadMethodCallException; 15 | use Illuminate\Auth\GuardHelpers; 16 | use Illuminate\Contracts\Auth\Guard; 17 | use Illuminate\Contracts\Auth\UserProvider; 18 | use Illuminate\Http\Request; 19 | use Illuminate\Support\Traits\Macroable; 20 | use Tymon\JWTAuth\Contracts\JWTSubject; 21 | use Tymon\JWTAuth\Exceptions\JWTException; 22 | use Tymon\JWTAuth\Exceptions\UserNotDefinedException; 23 | 24 | class JWTGuard implements Guard 25 | { 26 | use GuardHelpers, Macroable { 27 | __call as macroCall; 28 | } 29 | 30 | /** 31 | * The user we last attempted to retrieve. 32 | * 33 | * @var \Illuminate\Contracts\Auth\Authenticatable 34 | */ 35 | protected $lastAttempted; 36 | 37 | /** 38 | * The JWT instance. 39 | * 40 | * @var \Tymon\JWTAuth\JWT 41 | */ 42 | protected $jwt; 43 | 44 | /** 45 | * The request instance. 46 | * 47 | * @var \Illuminate\Http\Request 48 | */ 49 | protected $request; 50 | 51 | /** 52 | * Instantiate the class. 53 | * 54 | * @param \Tymon\JWTAuth\JWT $jwt 55 | * @param \Illuminate\Contracts\Auth\UserProvider $provider 56 | * @param \Illuminate\Http\Request $request 57 | * @return void 58 | */ 59 | public function __construct(JWT $jwt, UserProvider $provider, Request $request) 60 | { 61 | $this->jwt = $jwt; 62 | $this->provider = $provider; 63 | $this->request = $request; 64 | } 65 | 66 | /** 67 | * Get the currently authenticated user. 68 | * 69 | * @return \Illuminate\Contracts\Auth\Authenticatable|null 70 | */ 71 | public function user() 72 | { 73 | if ($this->user !== null) { 74 | return $this->user; 75 | } 76 | 77 | if ($this->jwt->setRequest($this->request)->getToken() && 78 | ($payload = $this->jwt->check(true)) && 79 | $this->validateSubject() 80 | ) { 81 | return $this->user = $this->provider->retrieveById($payload['sub']); 82 | } 83 | } 84 | 85 | /** 86 | * Get the currently authenticated user or throws an exception. 87 | * 88 | * @return \Illuminate\Contracts\Auth\Authenticatable 89 | * 90 | * @throws \Tymon\JWTAuth\Exceptions\UserNotDefinedException 91 | */ 92 | public function userOrFail() 93 | { 94 | if (! $user = $this->user()) { 95 | throw new UserNotDefinedException; 96 | } 97 | 98 | return $user; 99 | } 100 | 101 | /** 102 | * Validate a user's credentials. 103 | * 104 | * @param array $credentials 105 | * @return bool 106 | */ 107 | public function validate(array $credentials = []) 108 | { 109 | return (bool) $this->attempt($credentials, false); 110 | } 111 | 112 | /** 113 | * Attempt to authenticate the user using the given credentials and return the token. 114 | * 115 | * @param array $credentials 116 | * @param bool $login 117 | * @return bool|string 118 | */ 119 | public function attempt(array $credentials = [], $login = true) 120 | { 121 | $this->lastAttempted = $user = $this->provider->retrieveByCredentials($credentials); 122 | 123 | if ($this->hasValidCredentials($user, $credentials)) { 124 | return $login ? $this->login($user) : true; 125 | } 126 | 127 | return false; 128 | } 129 | 130 | /** 131 | * Create a token for a user. 132 | * 133 | * @param \Tymon\JWTAuth\Contracts\JWTSubject $user 134 | * @return string 135 | */ 136 | public function login(JWTSubject $user) 137 | { 138 | $token = $this->jwt->fromUser($user); 139 | $this->setToken($token)->setUser($user); 140 | 141 | return $token; 142 | } 143 | 144 | /** 145 | * Logout the user, thus invalidating the token. 146 | * 147 | * @param bool $forceForever 148 | * @return void 149 | */ 150 | public function logout($forceForever = false) 151 | { 152 | $this->requireToken()->invalidate($forceForever); 153 | 154 | $this->user = null; 155 | $this->jwt->unsetToken(); 156 | } 157 | 158 | /** 159 | * Refresh the token. 160 | * 161 | * @param bool $forceForever 162 | * @param bool $resetClaims 163 | * @return string 164 | */ 165 | public function refresh($forceForever = false, $resetClaims = false) 166 | { 167 | return $this->requireToken()->refresh($forceForever, $resetClaims); 168 | } 169 | 170 | /** 171 | * Invalidate the token. 172 | * 173 | * @param bool $forceForever 174 | * @return \Tymon\JWTAuth\JWT 175 | */ 176 | public function invalidate($forceForever = false) 177 | { 178 | return $this->requireToken()->invalidate($forceForever); 179 | } 180 | 181 | /** 182 | * Create a new token by User id. 183 | * 184 | * @param mixed $id 185 | * @return string|null 186 | */ 187 | public function tokenById($id) 188 | { 189 | if ($user = $this->provider->retrieveById($id)) { 190 | return $this->jwt->fromUser($user); 191 | } 192 | } 193 | 194 | /** 195 | * Log a user into the application using their credentials. 196 | * 197 | * @param array $credentials 198 | * @return bool 199 | */ 200 | public function once(array $credentials = []) 201 | { 202 | if ($this->validate($credentials)) { 203 | $this->setUser($this->lastAttempted); 204 | 205 | return true; 206 | } 207 | 208 | return false; 209 | } 210 | 211 | /** 212 | * Log the given User into the application. 213 | * 214 | * @param mixed $id 215 | * @return bool 216 | */ 217 | public function onceUsingId($id) 218 | { 219 | if ($user = $this->provider->retrieveById($id)) { 220 | $this->setUser($user); 221 | 222 | return true; 223 | } 224 | 225 | return false; 226 | } 227 | 228 | /** 229 | * Alias for onceUsingId. 230 | * 231 | * @param mixed $id 232 | * @return bool 233 | */ 234 | public function byId($id) 235 | { 236 | return $this->onceUsingId($id); 237 | } 238 | 239 | /** 240 | * Add any custom claims. 241 | * 242 | * @param array $claims 243 | * @return $this 244 | */ 245 | public function claims(array $claims) 246 | { 247 | $this->jwt->claims($claims); 248 | 249 | return $this; 250 | } 251 | 252 | /** 253 | * Get the raw Payload instance. 254 | * 255 | * @return \Tymon\JWTAuth\Payload 256 | */ 257 | public function getPayload() 258 | { 259 | return $this->requireToken()->getPayload(); 260 | } 261 | 262 | /** 263 | * Alias for getPayload(). 264 | * 265 | * @return \Tymon\JWTAuth\Payload 266 | */ 267 | public function payload() 268 | { 269 | return $this->getPayload(); 270 | } 271 | 272 | /** 273 | * Set the token. 274 | * 275 | * @param \Tymon\JWTAuth\Token|string $token 276 | * @return $this 277 | */ 278 | public function setToken($token) 279 | { 280 | $this->jwt->setToken($token); 281 | 282 | return $this; 283 | } 284 | 285 | /** 286 | * Set the token ttl. 287 | * 288 | * @param int $ttl 289 | * @return $this 290 | */ 291 | public function setTTL($ttl) 292 | { 293 | $this->jwt->factory()->setTTL($ttl); 294 | 295 | return $this; 296 | } 297 | 298 | /** 299 | * Get the user provider used by the guard. 300 | * 301 | * @return \Illuminate\Contracts\Auth\UserProvider 302 | */ 303 | public function getProvider() 304 | { 305 | return $this->provider; 306 | } 307 | 308 | /** 309 | * Set the user provider used by the guard. 310 | * 311 | * @param \Illuminate\Contracts\Auth\UserProvider $provider 312 | * @return $this 313 | */ 314 | public function setProvider(UserProvider $provider) 315 | { 316 | $this->provider = $provider; 317 | 318 | return $this; 319 | } 320 | 321 | /** 322 | * Return the currently cached user. 323 | * 324 | * @return \Illuminate\Contracts\Auth\Authenticatable|null 325 | */ 326 | public function getUser() 327 | { 328 | return $this->user; 329 | } 330 | 331 | /** 332 | * Get the current request instance. 333 | * 334 | * @return \Illuminate\Http\Request 335 | */ 336 | public function getRequest() 337 | { 338 | return $this->request ?: Request::createFromGlobals(); 339 | } 340 | 341 | /** 342 | * Set the current request instance. 343 | * 344 | * @param \Illuminate\Http\Request $request 345 | * @return $this 346 | */ 347 | public function setRequest(Request $request) 348 | { 349 | $this->request = $request; 350 | 351 | return $this; 352 | } 353 | 354 | /** 355 | * Get the token's auth factory. 356 | * 357 | * @return \Tymon\JWTAuth\Factory 358 | */ 359 | public function factory() 360 | { 361 | return $this->jwt->factory(); 362 | } 363 | 364 | /** 365 | * Get the last user we attempted to authenticate. 366 | * 367 | * @return \Illuminate\Contracts\Auth\Authenticatable 368 | */ 369 | public function getLastAttempted() 370 | { 371 | return $this->lastAttempted; 372 | } 373 | 374 | /** 375 | * Determine if the user matches the credentials. 376 | * 377 | * @param mixed $user 378 | * @param array $credentials 379 | * @return bool 380 | */ 381 | protected function hasValidCredentials($user, $credentials) 382 | { 383 | return $user !== null && $this->provider->validateCredentials($user, $credentials); 384 | } 385 | 386 | /** 387 | * Ensure the JWTSubject matches what is in the token. 388 | * 389 | * @return bool 390 | */ 391 | protected function validateSubject() 392 | { 393 | // If the provider doesn't have the necessary method 394 | // to get the underlying model name then allow. 395 | if (! method_exists($this->provider, 'getModel')) { 396 | return true; 397 | } 398 | 399 | return $this->jwt->checkSubjectModel($this->provider->getModel()); 400 | } 401 | 402 | /** 403 | * Ensure that a token is available in the request. 404 | * 405 | * @return \Tymon\JWTAuth\JWT 406 | * 407 | * @throws \Tymon\JWTAuth\Exceptions\JWTException 408 | */ 409 | protected function requireToken() 410 | { 411 | if (! $this->jwt->setRequest($this->getRequest())->getToken()) { 412 | throw new JWTException('Token could not be parsed from the request.'); 413 | } 414 | 415 | return $this->jwt; 416 | } 417 | 418 | /** 419 | * Magically call the JWT instance. 420 | * 421 | * @param string $method 422 | * @param array $parameters 423 | * @return mixed 424 | * 425 | * @throws \BadMethodCallException 426 | */ 427 | public function __call($method, $parameters) 428 | { 429 | if (method_exists($this->jwt, $method)) { 430 | return call_user_func_array([$this->jwt, $method], $parameters); 431 | } 432 | 433 | if (static::hasMacro($method)) { 434 | return $this->macroCall($method, $parameters); 435 | } 436 | 437 | throw new BadMethodCallException("Method [$method] does not exist."); 438 | } 439 | } 440 | -------------------------------------------------------------------------------- /src/Manager.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Tymon\JWTAuth; 13 | 14 | use Tymon\JWTAuth\Contracts\Providers\JWT as JWTContract; 15 | use Tymon\JWTAuth\Exceptions\JWTException; 16 | use Tymon\JWTAuth\Exceptions\TokenBlacklistedException; 17 | use Tymon\JWTAuth\Support\CustomClaims; 18 | use Tymon\JWTAuth\Support\RefreshFlow; 19 | 20 | class Manager 21 | { 22 | use CustomClaims, RefreshFlow; 23 | 24 | /** 25 | * The provider. 26 | * 27 | * @var \Tymon\JWTAuth\Contracts\Providers\JWT 28 | */ 29 | protected $provider; 30 | 31 | /** 32 | * The blacklist. 33 | * 34 | * @var \Tymon\JWTAuth\Blacklist 35 | */ 36 | protected $blacklist; 37 | 38 | /** 39 | * the payload factory. 40 | * 41 | * @var \Tymon\JWTAuth\Factory 42 | */ 43 | protected $payloadFactory; 44 | 45 | /** 46 | * The blacklist flag. 47 | * 48 | * @var bool 49 | */ 50 | protected $blacklistEnabled = true; 51 | 52 | /** 53 | * the persistent claims. 54 | * 55 | * @var array 56 | */ 57 | protected $persistentClaims = []; 58 | 59 | /** 60 | * Constructor. 61 | * 62 | * @param \Tymon\JWTAuth\Contracts\Providers\JWT $provider 63 | * @param \Tymon\JWTAuth\Blacklist $blacklist 64 | * @param \Tymon\JWTAuth\Factory $payloadFactory 65 | * @return void 66 | */ 67 | public function __construct(JWTContract $provider, Blacklist $blacklist, Factory $payloadFactory) 68 | { 69 | $this->provider = $provider; 70 | $this->blacklist = $blacklist; 71 | $this->payloadFactory = $payloadFactory; 72 | } 73 | 74 | /** 75 | * Encode a Payload and return the Token. 76 | * 77 | * @param \Tymon\JWTAuth\Payload $payload 78 | * @return \Tymon\JWTAuth\Token 79 | */ 80 | public function encode(Payload $payload) 81 | { 82 | $token = $this->provider->encode($payload->get()); 83 | 84 | return new Token($token); 85 | } 86 | 87 | /** 88 | * Decode a Token and return the Payload. 89 | * 90 | * @param \Tymon\JWTAuth\Token $token 91 | * @param bool $checkBlacklist 92 | * @return \Tymon\JWTAuth\Payload 93 | * 94 | * @throws \Tymon\JWTAuth\Exceptions\TokenBlacklistedException 95 | */ 96 | public function decode(Token $token, $checkBlacklist = true) 97 | { 98 | $payloadArray = $this->provider->decode($token->get()); 99 | 100 | $payload = $this->payloadFactory 101 | ->setRefreshFlow($this->refreshFlow) 102 | ->customClaims($payloadArray) 103 | ->make(); 104 | 105 | if ($checkBlacklist && $this->blacklistEnabled && $this->blacklist->has($payload)) { 106 | throw new TokenBlacklistedException('The token has been blacklisted'); 107 | } 108 | 109 | return $payload; 110 | } 111 | 112 | /** 113 | * Refresh a Token and return a new Token. 114 | * 115 | * @param \Tymon\JWTAuth\Token $token 116 | * @param bool $forceForever 117 | * @param bool $resetClaims 118 | * @return \Tymon\JWTAuth\Token 119 | */ 120 | public function refresh(Token $token, $forceForever = false, $resetClaims = false) 121 | { 122 | $this->setRefreshFlow(); 123 | 124 | $claims = $this->buildRefreshClaims($this->decode($token)); 125 | 126 | if ($this->blacklistEnabled) { 127 | // Invalidate old token 128 | $this->invalidate($token, $forceForever); 129 | } 130 | 131 | // Return the new token 132 | return $this->encode( 133 | $this->payloadFactory->customClaims($claims)->make($resetClaims) 134 | ); 135 | } 136 | 137 | /** 138 | * Invalidate a Token by adding it to the blacklist. 139 | * 140 | * @param \Tymon\JWTAuth\Token $token 141 | * @param bool $forceForever 142 | * @return bool 143 | * 144 | * @throws \Tymon\JWTAuth\Exceptions\JWTException 145 | */ 146 | public function invalidate(Token $token, $forceForever = false) 147 | { 148 | if (! $this->blacklistEnabled) { 149 | throw new JWTException('You must have the blacklist enabled to invalidate a token.'); 150 | } 151 | 152 | return call_user_func( 153 | [$this->blacklist, $forceForever ? 'addForever' : 'add'], 154 | $this->decode($token, false) 155 | ); 156 | } 157 | 158 | /** 159 | * Build the claims to go into the refreshed token. 160 | * 161 | * @param \Tymon\JWTAuth\Payload $payload 162 | * @return array 163 | */ 164 | protected function buildRefreshClaims(Payload $payload) 165 | { 166 | // Get the claims to be persisted from the payload 167 | $persistentClaims = collect($payload->toArray()) 168 | ->only($this->persistentClaims) 169 | ->toArray(); 170 | 171 | // persist the relevant claims 172 | return array_merge( 173 | $this->customClaims, 174 | $persistentClaims, 175 | [ 176 | 'sub' => $payload['sub'], 177 | 'iat' => $payload['iat'], 178 | ] 179 | ); 180 | } 181 | 182 | /** 183 | * Get the Payload Factory instance. 184 | * 185 | * @return \Tymon\JWTAuth\Factory 186 | */ 187 | public function getPayloadFactory() 188 | { 189 | return $this->payloadFactory; 190 | } 191 | 192 | /** 193 | * Get the JWTProvider instance. 194 | * 195 | * @return \Tymon\JWTAuth\Contracts\Providers\JWT 196 | */ 197 | public function getJWTProvider() 198 | { 199 | return $this->provider; 200 | } 201 | 202 | /** 203 | * Get the Blacklist instance. 204 | * 205 | * @return \Tymon\JWTAuth\Blacklist 206 | */ 207 | public function getBlacklist() 208 | { 209 | return $this->blacklist; 210 | } 211 | 212 | /** 213 | * Set whether the blacklist is enabled. 214 | * 215 | * @param bool $enabled 216 | * @return $this 217 | */ 218 | public function setBlacklistEnabled($enabled) 219 | { 220 | $this->blacklistEnabled = $enabled; 221 | 222 | return $this; 223 | } 224 | 225 | /** 226 | * Set the claims to be persisted when refreshing a token. 227 | * 228 | * @param array $claims 229 | * @return $this 230 | */ 231 | public function setPersistentClaims(array $claims) 232 | { 233 | $this->persistentClaims = $claims; 234 | 235 | return $this; 236 | } 237 | } 238 | -------------------------------------------------------------------------------- /src/Payload.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Tymon\JWTAuth; 13 | 14 | use ArrayAccess; 15 | use BadMethodCallException; 16 | use Countable; 17 | use Illuminate\Contracts\Support\Arrayable; 18 | use Illuminate\Contracts\Support\Jsonable; 19 | use Illuminate\Support\Arr; 20 | use Illuminate\Support\Str; 21 | use JsonSerializable; 22 | use Tymon\JWTAuth\Claims\Claim; 23 | use Tymon\JWTAuth\Claims\Collection; 24 | use Tymon\JWTAuth\Exceptions\PayloadException; 25 | use Tymon\JWTAuth\Validators\PayloadValidator; 26 | 27 | class Payload implements ArrayAccess, Arrayable, Countable, Jsonable, JsonSerializable 28 | { 29 | /** 30 | * The collection of claims. 31 | * 32 | * @var \Tymon\JWTAuth\Claims\Collection 33 | */ 34 | private $claims; 35 | 36 | /** 37 | * Build the Payload. 38 | * 39 | * @param \Tymon\JWTAuth\Claims\Collection $claims 40 | * @param \Tymon\JWTAuth\Validators\PayloadValidator $validator 41 | * @param bool $refreshFlow 42 | * @return void 43 | */ 44 | public function __construct(Collection $claims, PayloadValidator $validator, $refreshFlow = false) 45 | { 46 | $this->claims = $validator->setRefreshFlow($refreshFlow)->check($claims); 47 | } 48 | 49 | /** 50 | * Get the array of claim instances. 51 | * 52 | * @return \Tymon\JWTAuth\Claims\Collection 53 | */ 54 | public function getClaims() 55 | { 56 | return $this->claims; 57 | } 58 | 59 | /** 60 | * Checks if a payload matches some expected values. 61 | * 62 | * @param array $values 63 | * @param bool $strict 64 | * @return bool 65 | */ 66 | public function matches(array $values, $strict = false) 67 | { 68 | if (empty($values)) { 69 | return false; 70 | } 71 | 72 | $claims = $this->getClaims(); 73 | 74 | foreach ($values as $key => $value) { 75 | if (! $claims->has($key) || ! $claims->get($key)->matches($value, $strict)) { 76 | return false; 77 | } 78 | } 79 | 80 | return true; 81 | } 82 | 83 | /** 84 | * Checks if a payload strictly matches some expected values. 85 | * 86 | * @param array $values 87 | * @return bool 88 | */ 89 | public function matchesStrict(array $values) 90 | { 91 | return $this->matches($values, true); 92 | } 93 | 94 | /** 95 | * Get the payload. 96 | * 97 | * @param mixed $claim 98 | * @return mixed 99 | */ 100 | public function get($claim = null) 101 | { 102 | $claim = value($claim); 103 | 104 | if ($claim !== null) { 105 | if (is_array($claim)) { 106 | return array_map([$this, 'get'], $claim); 107 | } 108 | 109 | return Arr::get($this->toArray(), $claim); 110 | } 111 | 112 | return $this->toArray(); 113 | } 114 | 115 | /** 116 | * Get the underlying Claim instance. 117 | * 118 | * @param string $claim 119 | * @return \Tymon\JWTAuth\Claims\Claim 120 | */ 121 | public function getInternal($claim) 122 | { 123 | return $this->claims->getByClaimName($claim); 124 | } 125 | 126 | /** 127 | * Determine whether the payload has the claim (by instance). 128 | * 129 | * @param \Tymon\JWTAuth\Claims\Claim $claim 130 | * @return bool 131 | */ 132 | public function has(Claim $claim) 133 | { 134 | return $this->claims->has($claim->getName()); 135 | } 136 | 137 | /** 138 | * Determine whether the payload has the claim (by key). 139 | * 140 | * @param string $claim 141 | * @return bool 142 | */ 143 | public function hasKey($claim) 144 | { 145 | return $this->offsetExists($claim); 146 | } 147 | 148 | /** 149 | * Get the array of claims. 150 | * 151 | * @return array 152 | */ 153 | public function toArray() 154 | { 155 | return $this->claims->toPlainArray(); 156 | } 157 | 158 | /** 159 | * Convert the object into something JSON serializable. 160 | * 161 | * @return array 162 | */ 163 | #[\ReturnTypeWillChange] 164 | public function jsonSerialize() 165 | { 166 | return $this->toArray(); 167 | } 168 | 169 | /** 170 | * Get the payload as JSON. 171 | * 172 | * @param int $options 173 | * @return string 174 | */ 175 | public function toJson($options = JSON_UNESCAPED_SLASHES) 176 | { 177 | return json_encode($this->toArray(), $options); 178 | } 179 | 180 | /** 181 | * Get the payload as a string. 182 | * 183 | * @return string 184 | */ 185 | public function __toString() 186 | { 187 | return $this->toJson(); 188 | } 189 | 190 | /** 191 | * Determine if an item exists at an offset. 192 | * 193 | * @param mixed $key 194 | * @return bool 195 | */ 196 | #[\ReturnTypeWillChange] 197 | public function offsetExists($key) 198 | { 199 | return Arr::has($this->toArray(), $key); 200 | } 201 | 202 | /** 203 | * Get an item at a given offset. 204 | * 205 | * @param mixed $key 206 | * @return mixed 207 | */ 208 | #[\ReturnTypeWillChange] 209 | public function offsetGet($key) 210 | { 211 | return Arr::get($this->toArray(), $key); 212 | } 213 | 214 | /** 215 | * Don't allow changing the payload as it should be immutable. 216 | * 217 | * @param mixed $key 218 | * @param mixed $value 219 | * 220 | * @throws \Tymon\JWTAuth\Exceptions\PayloadException 221 | */ 222 | #[\ReturnTypeWillChange] 223 | public function offsetSet($key, $value) 224 | { 225 | throw new PayloadException('The payload is immutable'); 226 | } 227 | 228 | /** 229 | * Don't allow changing the payload as it should be immutable. 230 | * 231 | * @param string $key 232 | * @return void 233 | * 234 | * @throws \Tymon\JWTAuth\Exceptions\PayloadException 235 | */ 236 | #[\ReturnTypeWillChange] 237 | public function offsetUnset($key) 238 | { 239 | throw new PayloadException('The payload is immutable'); 240 | } 241 | 242 | /** 243 | * Count the number of claims. 244 | * 245 | * @return int 246 | */ 247 | #[\ReturnTypeWillChange] 248 | public function count() 249 | { 250 | return count($this->toArray()); 251 | } 252 | 253 | /** 254 | * Invoke the Payload as a callable function. 255 | * 256 | * @param mixed $claim 257 | * @return mixed 258 | */ 259 | public function __invoke($claim = null) 260 | { 261 | return $this->get($claim); 262 | } 263 | 264 | /** 265 | * Magically get a claim value. 266 | * 267 | * @param string $method 268 | * @param array $parameters 269 | * @return mixed 270 | * 271 | * @throws \BadMethodCallException 272 | */ 273 | public function __call($method, $parameters) 274 | { 275 | if (preg_match('/get(.+)\b/i', $method, $matches)) { 276 | foreach ($this->claims as $claim) { 277 | if (get_class($claim) === 'Tymon\\JWTAuth\\Claims\\'.$matches[1]) { 278 | return $claim->getValue(); 279 | } 280 | } 281 | } 282 | 283 | throw new BadMethodCallException(sprintf('The claim [%s] does not exist on the payload.', Str::after($method, 'get'))); 284 | } 285 | } 286 | -------------------------------------------------------------------------------- /src/Providers/AbstractServiceProvider.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Tymon\JWTAuth\Providers; 13 | 14 | use Illuminate\Support\ServiceProvider; 15 | use Namshi\JOSE\JWS; 16 | use Tymon\JWTAuth\Blacklist; 17 | use Tymon\JWTAuth\Claims\Factory as ClaimFactory; 18 | use Tymon\JWTAuth\Console\JWTGenerateSecretCommand; 19 | use Tymon\JWTAuth\Contracts\Providers\Auth; 20 | use Tymon\JWTAuth\Contracts\Providers\JWT as JWTContract; 21 | use Tymon\JWTAuth\Contracts\Providers\Storage; 22 | use Tymon\JWTAuth\Factory; 23 | use Tymon\JWTAuth\Http\Middleware\Authenticate; 24 | use Tymon\JWTAuth\Http\Middleware\AuthenticateAndRenew; 25 | use Tymon\JWTAuth\Http\Middleware\Check; 26 | use Tymon\JWTAuth\Http\Middleware\RefreshToken; 27 | use Tymon\JWTAuth\Http\Parser\AuthHeaders; 28 | use Tymon\JWTAuth\Http\Parser\InputSource; 29 | use Tymon\JWTAuth\Http\Parser\Parser; 30 | use Tymon\JWTAuth\Http\Parser\QueryString; 31 | use Tymon\JWTAuth\JWT; 32 | use Tymon\JWTAuth\JWTAuth; 33 | use Tymon\JWTAuth\JWTGuard; 34 | use Tymon\JWTAuth\Manager; 35 | use Tymon\JWTAuth\Providers\JWT\Lcobucci; 36 | use Tymon\JWTAuth\Providers\JWT\Namshi; 37 | use Tymon\JWTAuth\Validators\PayloadValidator; 38 | 39 | abstract class AbstractServiceProvider extends ServiceProvider 40 | { 41 | /** 42 | * The middleware aliases. 43 | * 44 | * @var array 45 | */ 46 | protected $middlewareAliases = [ 47 | 'jwt.auth' => Authenticate::class, 48 | 'jwt.check' => Check::class, 49 | 'jwt.refresh' => RefreshToken::class, 50 | 'jwt.renew' => AuthenticateAndRenew::class, 51 | ]; 52 | 53 | /** 54 | * Boot the service provider. 55 | * 56 | * @return void 57 | */ 58 | abstract public function boot(); 59 | 60 | /** 61 | * Register the service provider. 62 | * 63 | * @return void 64 | */ 65 | public function register() 66 | { 67 | $this->registerAliases(); 68 | 69 | $this->registerJWTProvider(); 70 | $this->registerAuthProvider(); 71 | $this->registerStorageProvider(); 72 | $this->registerJWTBlacklist(); 73 | 74 | $this->registerManager(); 75 | $this->registerTokenParser(); 76 | 77 | $this->registerJWT(); 78 | $this->registerJWTAuth(); 79 | $this->registerPayloadValidator(); 80 | $this->registerClaimFactory(); 81 | $this->registerPayloadFactory(); 82 | $this->registerJWTCommand(); 83 | 84 | $this->commands('tymon.jwt.secret'); 85 | } 86 | 87 | /** 88 | * Extend Laravel's Auth. 89 | * 90 | * @return void 91 | */ 92 | protected function extendAuthGuard() 93 | { 94 | $this->app['auth']->extend('jwt', function ($app, $name, array $config) { 95 | $guard = new JWTGuard( 96 | $app['tymon.jwt'], 97 | $app['auth']->createUserProvider($config['provider']), 98 | $app['request'] 99 | ); 100 | 101 | $app->refresh('request', $guard, 'setRequest'); 102 | 103 | return $guard; 104 | }); 105 | } 106 | 107 | /** 108 | * Bind some aliases. 109 | * 110 | * @return void 111 | */ 112 | protected function registerAliases() 113 | { 114 | $this->app->alias('tymon.jwt', JWT::class); 115 | $this->app->alias('tymon.jwt.auth', JWTAuth::class); 116 | $this->app->alias('tymon.jwt.provider.jwt', JWTContract::class); 117 | $this->app->alias('tymon.jwt.provider.jwt.namshi', Namshi::class); 118 | $this->app->alias('tymon.jwt.provider.jwt.lcobucci', Lcobucci::class); 119 | $this->app->alias('tymon.jwt.provider.auth', Auth::class); 120 | $this->app->alias('tymon.jwt.provider.storage', Storage::class); 121 | $this->app->alias('tymon.jwt.manager', Manager::class); 122 | $this->app->alias('tymon.jwt.blacklist', Blacklist::class); 123 | $this->app->alias('tymon.jwt.payload.factory', Factory::class); 124 | $this->app->alias('tymon.jwt.validators.payload', PayloadValidator::class); 125 | } 126 | 127 | /** 128 | * Register the bindings for the JSON Web Token provider. 129 | * 130 | * @return void 131 | */ 132 | protected function registerJWTProvider() 133 | { 134 | $this->registerNamshiProvider(); 135 | $this->registerLcobucciProvider(); 136 | 137 | $this->app->singleton('tymon.jwt.provider.jwt', function ($app) { 138 | return $this->getConfigInstance('providers.jwt'); 139 | }); 140 | } 141 | 142 | /** 143 | * Register the bindings for the Lcobucci JWT provider. 144 | * 145 | * @return void 146 | */ 147 | protected function registerNamshiProvider() 148 | { 149 | $this->app->singleton('tymon.jwt.provider.jwt.namshi', function ($app) { 150 | return new Namshi( 151 | new JWS(['typ' => 'JWT', 'alg' => $this->config('algo')]), 152 | $this->config('secret'), 153 | $this->config('algo'), 154 | $this->config('keys') 155 | ); 156 | }); 157 | } 158 | 159 | /** 160 | * Register the bindings for the Lcobucci JWT provider. 161 | * 162 | * @return void 163 | */ 164 | protected function registerLcobucciProvider() 165 | { 166 | $this->app->singleton('tymon.jwt.provider.jwt.lcobucci', function ($app) { 167 | return new Lcobucci( 168 | $this->config('secret'), 169 | $this->config('algo'), 170 | $this->config('keys') 171 | ); 172 | }); 173 | } 174 | 175 | /** 176 | * Register the bindings for the Auth provider. 177 | * 178 | * @return void 179 | */ 180 | protected function registerAuthProvider() 181 | { 182 | $this->app->singleton('tymon.jwt.provider.auth', function () { 183 | return $this->getConfigInstance('providers.auth'); 184 | }); 185 | } 186 | 187 | /** 188 | * Register the bindings for the Storage provider. 189 | * 190 | * @return void 191 | */ 192 | protected function registerStorageProvider() 193 | { 194 | $this->app->singleton('tymon.jwt.provider.storage', function () { 195 | return $this->getConfigInstance('providers.storage'); 196 | }); 197 | } 198 | 199 | /** 200 | * Register the bindings for the JWT Manager. 201 | * 202 | * @return void 203 | */ 204 | protected function registerManager() 205 | { 206 | $this->app->singleton('tymon.jwt.manager', function ($app) { 207 | $instance = new Manager( 208 | $app['tymon.jwt.provider.jwt'], 209 | $app['tymon.jwt.blacklist'], 210 | $app['tymon.jwt.payload.factory'] 211 | ); 212 | 213 | return $instance->setBlacklistEnabled((bool) $this->config('blacklist_enabled')) 214 | ->setPersistentClaims($this->config('persistent_claims')); 215 | }); 216 | } 217 | 218 | /** 219 | * Register the bindings for the Token Parser. 220 | * 221 | * @return void 222 | */ 223 | protected function registerTokenParser() 224 | { 225 | $this->app->singleton('tymon.jwt.parser', function ($app) { 226 | $parser = new Parser( 227 | $app['request'], 228 | [ 229 | new AuthHeaders, 230 | new QueryString, 231 | new InputSource, 232 | ] 233 | ); 234 | 235 | $app->refresh('request', $parser, 'setRequest'); 236 | 237 | return $parser; 238 | }); 239 | } 240 | 241 | /** 242 | * Register the bindings for the main JWT class. 243 | * 244 | * @return void 245 | */ 246 | protected function registerJWT() 247 | { 248 | $this->app->singleton('tymon.jwt', function ($app) { 249 | return (new JWT( 250 | $app['tymon.jwt.manager'], 251 | $app['tymon.jwt.parser'] 252 | ))->lockSubject($this->config('lock_subject')); 253 | }); 254 | } 255 | 256 | /** 257 | * Register the bindings for the main JWTAuth class. 258 | * 259 | * @return void 260 | */ 261 | protected function registerJWTAuth() 262 | { 263 | $this->app->singleton('tymon.jwt.auth', function ($app) { 264 | return (new JWTAuth( 265 | $app['tymon.jwt.manager'], 266 | $app['tymon.jwt.provider.auth'], 267 | $app['tymon.jwt.parser'] 268 | ))->lockSubject($this->config('lock_subject')); 269 | }); 270 | } 271 | 272 | /** 273 | * Register the bindings for the Blacklist. 274 | * 275 | * @return void 276 | */ 277 | protected function registerJWTBlacklist() 278 | { 279 | $this->app->singleton('tymon.jwt.blacklist', function ($app) { 280 | $instance = new Blacklist($app['tymon.jwt.provider.storage']); 281 | 282 | return $instance->setGracePeriod($this->config('blacklist_grace_period')) 283 | ->setRefreshTTL($this->config('refresh_ttl')); 284 | }); 285 | } 286 | 287 | /** 288 | * Register the bindings for the payload validator. 289 | * 290 | * @return void 291 | */ 292 | protected function registerPayloadValidator() 293 | { 294 | $this->app->singleton('tymon.jwt.validators.payload', function () { 295 | return (new PayloadValidator) 296 | ->setRefreshTTL($this->config('refresh_ttl')) 297 | ->setRequiredClaims($this->config('required_claims')); 298 | }); 299 | } 300 | 301 | /** 302 | * Register the bindings for the Claim Factory. 303 | * 304 | * @return void 305 | */ 306 | protected function registerClaimFactory() 307 | { 308 | $this->app->singleton('tymon.jwt.claim.factory', function ($app) { 309 | $factory = new ClaimFactory($app['request']); 310 | $app->refresh('request', $factory, 'setRequest'); 311 | 312 | return $factory->setTTL($this->config('ttl')) 313 | ->setLeeway($this->config('leeway')); 314 | }); 315 | } 316 | 317 | /** 318 | * Register the bindings for the Payload Factory. 319 | * 320 | * @return void 321 | */ 322 | protected function registerPayloadFactory() 323 | { 324 | $this->app->singleton('tymon.jwt.payload.factory', function ($app) { 325 | return new Factory( 326 | $app['tymon.jwt.claim.factory'], 327 | $app['tymon.jwt.validators.payload'] 328 | ); 329 | }); 330 | } 331 | 332 | /** 333 | * Register the Artisan command. 334 | * 335 | * @return void 336 | */ 337 | protected function registerJWTCommand() 338 | { 339 | $this->app->singleton('tymon.jwt.secret', function () { 340 | return new JWTGenerateSecretCommand; 341 | }); 342 | } 343 | 344 | /** 345 | * Helper to get the config values. 346 | * 347 | * @param string $key 348 | * @param string $default 349 | * @return mixed 350 | */ 351 | protected function config($key, $default = null) 352 | { 353 | return config("jwt.$key", $default); 354 | } 355 | 356 | /** 357 | * Get an instantiable configuration instance. 358 | * 359 | * @param string $key 360 | * @return mixed 361 | */ 362 | protected function getConfigInstance($key) 363 | { 364 | $instance = $this->config($key); 365 | 366 | if (is_string($instance)) { 367 | return $this->app->make($instance); 368 | } 369 | 370 | return $instance; 371 | } 372 | } 373 | -------------------------------------------------------------------------------- /src/Providers/Auth/Illuminate.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Tymon\JWTAuth\Providers\Auth; 13 | 14 | use Illuminate\Contracts\Auth\Guard as GuardContract; 15 | use Tymon\JWTAuth\Contracts\Providers\Auth; 16 | 17 | class Illuminate implements Auth 18 | { 19 | /** 20 | * The authentication guard. 21 | * 22 | * @var \Illuminate\Contracts\Auth\Guard 23 | */ 24 | protected $auth; 25 | 26 | /** 27 | * Constructor. 28 | * 29 | * @param \Illuminate\Contracts\Auth\Guard $auth 30 | * @return void 31 | */ 32 | public function __construct(GuardContract $auth) 33 | { 34 | $this->auth = $auth; 35 | } 36 | 37 | /** 38 | * Check a user's credentials. 39 | * 40 | * @param array $credentials 41 | * @return bool 42 | */ 43 | public function byCredentials(array $credentials) 44 | { 45 | return $this->auth->once($credentials); 46 | } 47 | 48 | /** 49 | * Authenticate a user via the id. 50 | * 51 | * @param mixed $id 52 | * @return bool 53 | */ 54 | public function byId($id) 55 | { 56 | return $this->auth->onceUsingId($id); 57 | } 58 | 59 | /** 60 | * Get the currently authenticated user. 61 | * 62 | * @return mixed 63 | */ 64 | public function user() 65 | { 66 | return $this->auth->user(); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/Providers/JWT/Lcobucci.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Tymon\JWTAuth\Providers\JWT; 13 | 14 | use DateTimeImmutable; 15 | use DateTimeInterface; 16 | use Exception; 17 | use Illuminate\Support\Collection; 18 | use Lcobucci\JWT\Configuration; 19 | use Lcobucci\JWT\Signer; 20 | use Lcobucci\JWT\Signer\Ecdsa; 21 | use Lcobucci\JWT\Signer\Key; 22 | use Lcobucci\JWT\Signer\Key\InMemory; 23 | use Lcobucci\JWT\Signer\Rsa; 24 | use Lcobucci\JWT\Token\Builder; 25 | use Lcobucci\JWT\Token\RegisteredClaims; 26 | use Lcobucci\JWT\Validation\Constraint\SignedWith; 27 | use Tymon\JWTAuth\Contracts\Providers\JWT; 28 | use Tymon\JWTAuth\Exceptions\JWTException; 29 | use Tymon\JWTAuth\Exceptions\TokenInvalidException; 30 | 31 | class Lcobucci extends Provider implements JWT 32 | { 33 | /** 34 | * \Lcobucci\JWT\Signer. 35 | */ 36 | protected $signer; 37 | 38 | /** 39 | * \Lcobucci\JWT\Configuration. 40 | */ 41 | protected $config; 42 | 43 | /** 44 | * Create the Lcobucci provider. 45 | * 46 | * @param string $secret 47 | * @param string $algo 48 | * @param array $keys 49 | * @param \Lcobucci\JWT\Configuration|null $config 50 | * @return void 51 | */ 52 | public function __construct($secret, $algo, array $keys, $config = null) 53 | { 54 | parent::__construct($secret, $algo, $keys); 55 | 56 | $this->signer = $this->getSigner(); 57 | $this->config = $config ?: $this->buildConfig(); 58 | } 59 | 60 | /** 61 | * Signers that this provider supports. 62 | * 63 | * @var array 64 | */ 65 | protected $signers = [ 66 | self::ALGO_HS256 => Signer\Hmac\Sha256::class, 67 | self::ALGO_HS384 => Signer\Hmac\Sha384::class, 68 | self::ALGO_HS512 => Signer\Hmac\Sha512::class, 69 | self::ALGO_RS256 => Signer\Rsa\Sha256::class, 70 | self::ALGO_RS384 => Signer\Rsa\Sha384::class, 71 | self::ALGO_RS512 => Signer\Rsa\Sha512::class, 72 | self::ALGO_ES256 => Signer\Ecdsa\Sha256::class, 73 | self::ALGO_ES384 => Signer\Ecdsa\Sha384::class, 74 | self::ALGO_ES512 => Signer\Ecdsa\Sha512::class, 75 | ]; 76 | 77 | /** 78 | * Create a JSON Web Token. 79 | * 80 | * @param array $payload 81 | * @return string 82 | * 83 | * @throws \Tymon\JWTAuth\Exceptions\JWTException 84 | */ 85 | public function encode(array $payload) 86 | { 87 | $builder = $this->getBuilderFromClaims($payload); 88 | 89 | try { 90 | return $builder 91 | ->getToken($this->config->signer(), $this->config->signingKey()) 92 | ->toString(); 93 | } catch (Exception $e) { 94 | throw new JWTException('Could not create token: '.$e->getMessage(), $e->getCode(), $e); 95 | } 96 | } 97 | 98 | /** 99 | * Decode a JSON Web Token. 100 | * 101 | * @param string $token 102 | * @return array 103 | * 104 | * @throws \Tymon\JWTAuth\Exceptions\JWTException 105 | */ 106 | public function decode($token) 107 | { 108 | try { 109 | /** @var \Lcobucci\JWT\Token\Plain */ 110 | $token = $this->config->parser()->parse($token); 111 | } catch (Exception $e) { 112 | throw new TokenInvalidException('Could not decode token: '.$e->getMessage(), $e->getCode(), $e); 113 | } 114 | 115 | if (! $this->config->validator()->validate($token, ...$this->config->validationConstraints())) { 116 | throw new TokenInvalidException('Token Signature could not be verified.'); 117 | } 118 | 119 | return Collection::wrap($token->claims()->all()) 120 | ->map(function ($claim) { 121 | if ($claim instanceof DateTimeInterface) { 122 | return $claim->getTimestamp(); 123 | } 124 | 125 | return is_object($claim) && method_exists($claim, 'getValue') 126 | ? $claim->getValue() 127 | : $claim; 128 | }) 129 | ->toArray(); 130 | } 131 | 132 | /** 133 | * Create an instance of the builder with all of the claims applied. 134 | * 135 | * @param array $payload 136 | * @return \Lcobucci\JWT\Token\Builder 137 | */ 138 | protected function getBuilderFromClaims(array $payload): Builder 139 | { 140 | $builder = $this->config->builder(); 141 | 142 | foreach ($payload as $key => $value) { 143 | switch ($key) { 144 | case RegisteredClaims::ID: 145 | $builder->identifiedBy($value); 146 | break; 147 | case RegisteredClaims::EXPIRATION_TIME: 148 | $builder->expiresAt(DateTimeImmutable::createFromFormat('U', $value)); 149 | break; 150 | case RegisteredClaims::NOT_BEFORE: 151 | $builder->canOnlyBeUsedAfter(DateTimeImmutable::createFromFormat('U', $value)); 152 | break; 153 | case RegisteredClaims::ISSUED_AT: 154 | $builder->issuedAt(DateTimeImmutable::createFromFormat('U', $value)); 155 | break; 156 | case RegisteredClaims::ISSUER: 157 | $builder->issuedBy($value); 158 | break; 159 | case RegisteredClaims::AUDIENCE: 160 | $builder->permittedFor($value); 161 | break; 162 | case RegisteredClaims::SUBJECT: 163 | $builder->relatedTo($value); 164 | break; 165 | default: 166 | $builder->withClaim($key, $value); 167 | } 168 | } 169 | 170 | return $builder; 171 | } 172 | 173 | /** 174 | * Build the configuration. 175 | * 176 | * @return \Lcobucci\JWT\Configuration 177 | */ 178 | protected function buildConfig(): Configuration 179 | { 180 | $config = $this->isAsymmetric() 181 | ? Configuration::forAsymmetricSigner( 182 | $this->signer, 183 | $this->getSigningKey(), 184 | $this->getVerificationKey() 185 | ) 186 | : Configuration::forSymmetricSigner($this->signer, $this->getSigningKey()); 187 | 188 | $config->setValidationConstraints( 189 | new SignedWith($this->signer, $this->getVerificationKey()) 190 | ); 191 | 192 | return $config; 193 | } 194 | 195 | /** 196 | * Get the signer instance. 197 | * 198 | * @return \Lcobucci\JWT\Signer 199 | * 200 | * @throws \Tymon\JWTAuth\Exceptions\JWTException 201 | */ 202 | protected function getSigner() 203 | { 204 | if (! array_key_exists($this->algo, $this->signers)) { 205 | throw new JWTException('The given algorithm could not be found'); 206 | } 207 | 208 | $signer = $this->signers[$this->algo]; 209 | 210 | if (is_subclass_of($signer, Ecdsa::class)) { 211 | return $signer::create(); 212 | } 213 | 214 | return new $signer(); 215 | } 216 | 217 | /** 218 | * {@inheritdoc} 219 | */ 220 | protected function isAsymmetric() 221 | { 222 | return is_subclass_of($this->signer, Rsa::class) 223 | || is_subclass_of($this->signer, Ecdsa::class); 224 | } 225 | 226 | /** 227 | * {@inheritdoc} 228 | * 229 | * @return \Lcobucci\JWT\Signer\Key 230 | * 231 | * @throws \Tymon\JWTAuth\Exceptions\JWTException 232 | */ 233 | protected function getSigningKey() 234 | { 235 | if ($this->isAsymmetric()) { 236 | if (! $privateKey = $this->getPrivateKey()) { 237 | throw new JWTException('Private key is not set.'); 238 | } 239 | 240 | return $this->getKey($privateKey, $this->getPassphrase() ?? ''); 241 | } 242 | 243 | if (! $secret = $this->getSecret()) { 244 | throw new JWTException('Secret is not set.'); 245 | } 246 | 247 | return $this->getKey($secret); 248 | } 249 | 250 | /** 251 | * {@inheritdoc} 252 | * 253 | * @return \Lcobucci\JWT\Signer\Key 254 | * 255 | * @throws \Tymon\JWTAuth\Exceptions\JWTException 256 | */ 257 | protected function getVerificationKey() 258 | { 259 | if ($this->isAsymmetric()) { 260 | if (! $public = $this->getPublicKey()) { 261 | throw new JWTException('Public key is not set.'); 262 | } 263 | 264 | return $this->getKey($public); 265 | } 266 | 267 | if (! $secret = $this->getSecret()) { 268 | throw new JWTException('Secret is not set.'); 269 | } 270 | 271 | return $this->getKey($secret); 272 | } 273 | 274 | /** 275 | * Get the signing key instance. 276 | */ 277 | protected function getKey(string $contents, string $passphrase = ''): Key 278 | { 279 | return InMemory::plainText($contents, $passphrase); 280 | } 281 | } 282 | -------------------------------------------------------------------------------- /src/Providers/JWT/Provider.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Tymon\JWTAuth\Providers\JWT; 13 | 14 | use Illuminate\Support\Arr; 15 | 16 | abstract class Provider 17 | { 18 | const ALGO_HS256 = 'HS256'; 19 | const ALGO_HS384 = 'HS384'; 20 | const ALGO_HS512 = 'HS512'; 21 | const ALGO_RS256 = 'RS256'; 22 | const ALGO_RS384 = 'RS384'; 23 | const ALGO_RS512 = 'RS512'; 24 | const ALGO_ES256 = 'ES256'; 25 | const ALGO_ES384 = 'ES384'; 26 | const ALGO_ES512 = 'ES512'; 27 | 28 | /** 29 | * The secret. 30 | * 31 | * @var string 32 | */ 33 | protected $secret; 34 | 35 | /** 36 | * The array of keys. 37 | * 38 | * @var array 39 | */ 40 | protected $keys; 41 | 42 | /** 43 | * The used algorithm. 44 | * 45 | * @var string 46 | */ 47 | protected $algo; 48 | 49 | /** 50 | * Constructor. 51 | * 52 | * @param string $secret 53 | * @param string $algo 54 | * @param array $keys 55 | * @return void 56 | */ 57 | public function __construct($secret, $algo, array $keys) 58 | { 59 | $this->secret = $secret; 60 | $this->algo = $algo; 61 | $this->keys = $keys; 62 | } 63 | 64 | /** 65 | * Set the algorithm used to sign the token. 66 | * 67 | * @param string $algo 68 | * @return $this 69 | */ 70 | public function setAlgo($algo) 71 | { 72 | $this->algo = $algo; 73 | 74 | return $this; 75 | } 76 | 77 | /** 78 | * Get the algorithm used to sign the token. 79 | * 80 | * @return string 81 | */ 82 | public function getAlgo() 83 | { 84 | return $this->algo; 85 | } 86 | 87 | /** 88 | * Set the secret used to sign the token. 89 | * 90 | * @param string $secret 91 | * @return $this 92 | */ 93 | public function setSecret($secret) 94 | { 95 | $this->secret = $secret; 96 | 97 | return $this; 98 | } 99 | 100 | /** 101 | * Get the secret used to sign the token. 102 | * 103 | * @return string 104 | */ 105 | public function getSecret() 106 | { 107 | return $this->secret; 108 | } 109 | 110 | /** 111 | * Set the keys used to sign the token. 112 | * 113 | * @param array $keys 114 | * @return $this 115 | */ 116 | public function setKeys(array $keys) 117 | { 118 | $this->keys = $keys; 119 | 120 | return $this; 121 | } 122 | 123 | /** 124 | * Get the array of keys used to sign tokens with an asymmetric algorithm. 125 | * 126 | * @return array 127 | */ 128 | public function getKeys() 129 | { 130 | return $this->keys; 131 | } 132 | 133 | /** 134 | * Get the public key used to sign tokens with an asymmetric algorithm. 135 | * 136 | * @return string|null 137 | */ 138 | public function getPublicKey() 139 | { 140 | return Arr::get($this->keys, 'public'); 141 | } 142 | 143 | /** 144 | * Get the private key used to sign tokens with an asymmetric algorithm. 145 | * 146 | * @return string|null 147 | */ 148 | public function getPrivateKey() 149 | { 150 | return Arr::get($this->keys, 'private'); 151 | } 152 | 153 | /** 154 | * Get the passphrase used to sign tokens 155 | * with an asymmetric algorithm. 156 | * 157 | * @return string|null 158 | */ 159 | public function getPassphrase() 160 | { 161 | return Arr::get($this->keys, 'passphrase'); 162 | } 163 | 164 | /** 165 | * Get the key used to sign the tokens. 166 | * 167 | * @return string|null 168 | */ 169 | protected function getSigningKey() 170 | { 171 | return $this->isAsymmetric() ? $this->getPrivateKey() : $this->getSecret(); 172 | } 173 | 174 | /** 175 | * Get the key used to verify the tokens. 176 | * 177 | * @return string|null 178 | */ 179 | protected function getVerificationKey() 180 | { 181 | return $this->isAsymmetric() ? $this->getPublicKey() : $this->getSecret(); 182 | } 183 | 184 | /** 185 | * Determine if the algorithm is asymmetric, and thus requires a public/private key combo. 186 | * 187 | * @return bool 188 | */ 189 | abstract protected function isAsymmetric(); 190 | } 191 | -------------------------------------------------------------------------------- /src/Providers/LaravelServiceProvider.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Tymon\JWTAuth\Providers; 13 | 14 | use Tymon\JWTAuth\Http\Parser\Cookies; 15 | use Tymon\JWTAuth\Http\Parser\RouteParams; 16 | 17 | class LaravelServiceProvider extends AbstractServiceProvider 18 | { 19 | /** 20 | * {@inheritdoc} 21 | */ 22 | public function boot() 23 | { 24 | $path = realpath(__DIR__.'/../../config/config.php'); 25 | 26 | $this->publishes([$path => config_path('jwt.php')], 'config'); 27 | $this->mergeConfigFrom($path, 'jwt'); 28 | 29 | $this->aliasMiddleware(); 30 | 31 | $this->extendAuthGuard(); 32 | 33 | $this->app['tymon.jwt.parser']->addParser([ 34 | new RouteParams, 35 | new Cookies($this->config('decrypt_cookies')), 36 | ]); 37 | } 38 | 39 | /** 40 | * {@inheritdoc} 41 | */ 42 | protected function registerStorageProvider() 43 | { 44 | $this->app->singleton('tymon.jwt.provider.storage', function () { 45 | $instance = $this->getConfigInstance('providers.storage'); 46 | 47 | if (method_exists($instance, 'setLaravelVersion')) { 48 | $instance->setLaravelVersion($this->app->version()); 49 | } 50 | 51 | return $instance; 52 | }); 53 | } 54 | 55 | /** 56 | * Alias the middleware. 57 | * 58 | * @return void 59 | */ 60 | protected function aliasMiddleware() 61 | { 62 | $router = $this->app['router']; 63 | 64 | $method = method_exists($router, 'aliasMiddleware') ? 'aliasMiddleware' : 'middleware'; 65 | 66 | foreach ($this->middlewareAliases as $alias => $middleware) { 67 | $router->$method($alias, $middleware); 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/Providers/LumenServiceProvider.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Tymon\JWTAuth\Providers; 13 | 14 | use Tymon\JWTAuth\Http\Parser\LumenRouteParams; 15 | 16 | class LumenServiceProvider extends AbstractServiceProvider 17 | { 18 | /** 19 | * {@inheritdoc} 20 | */ 21 | public function boot() 22 | { 23 | $this->app->configure('jwt'); 24 | 25 | $path = realpath(__DIR__.'/../../config/config.php'); 26 | $this->mergeConfigFrom($path, 'jwt'); 27 | 28 | $this->app->routeMiddleware($this->middlewareAliases); 29 | 30 | $this->extendAuthGuard(); 31 | 32 | $this->app['tymon.jwt.parser']->addParser(new LumenRouteParams); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Providers/Storage/Illuminate.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Tymon\JWTAuth\Providers\Storage; 13 | 14 | use BadMethodCallException; 15 | use Illuminate\Contracts\Cache\Repository as CacheContract; 16 | use Psr\SimpleCache\CacheInterface as PsrCacheInterface; 17 | use Tymon\JWTAuth\Contracts\Providers\Storage; 18 | 19 | class Illuminate implements Storage 20 | { 21 | /** 22 | * The cache repository contract. 23 | * 24 | * @var \Illuminate\Contracts\Cache\Repository 25 | */ 26 | protected $cache; 27 | 28 | /** 29 | * The used cache tag. 30 | * 31 | * @var string 32 | */ 33 | protected $tag = 'tymon.jwt'; 34 | 35 | /** 36 | * @var bool 37 | */ 38 | protected $supportsTags; 39 | 40 | /** 41 | * @var string|null 42 | */ 43 | protected $laravelVersion; 44 | 45 | /** 46 | * Constructor. 47 | * 48 | * @param \Illuminate\Contracts\Cache\Repository $cache 49 | * @return void 50 | */ 51 | public function __construct(CacheContract $cache) 52 | { 53 | $this->cache = $cache; 54 | } 55 | 56 | /** 57 | * Add a new item into storage. 58 | * 59 | * @param string $key 60 | * @param mixed $value 61 | * @param int $minutes 62 | * @return void 63 | */ 64 | public function add($key, $value, $minutes) 65 | { 66 | // If the laravel version is 5.8 or higher then convert minutes to seconds. 67 | if ($this->laravelVersion !== null 68 | && is_int($minutes) 69 | && version_compare($this->laravelVersion, '5.8', '>=') 70 | ) { 71 | $minutes = $minutes * 60; 72 | } 73 | 74 | $this->cache()->put($key, $value, $minutes); 75 | } 76 | 77 | /** 78 | * Add a new item into storage forever. 79 | * 80 | * @param string $key 81 | * @param mixed $value 82 | * @return void 83 | */ 84 | public function forever($key, $value) 85 | { 86 | $this->cache()->forever($key, $value); 87 | } 88 | 89 | /** 90 | * Get an item from storage. 91 | * 92 | * @param string $key 93 | * @return mixed 94 | */ 95 | public function get($key) 96 | { 97 | return $this->cache()->get($key); 98 | } 99 | 100 | /** 101 | * Remove an item from storage. 102 | * 103 | * @param string $key 104 | * @return bool 105 | */ 106 | public function destroy($key) 107 | { 108 | return $this->cache()->forget($key); 109 | } 110 | 111 | /** 112 | * Remove all items associated with the tag. 113 | * 114 | * @return void 115 | */ 116 | public function flush() 117 | { 118 | $this->cache()->flush(); 119 | } 120 | 121 | /** 122 | * Return the cache instance with tags attached. 123 | * 124 | * @return \Illuminate\Contracts\Cache\Repository 125 | */ 126 | protected function cache() 127 | { 128 | if ($this->supportsTags === null) { 129 | $this->determineTagSupport(); 130 | } 131 | 132 | if ($this->supportsTags) { 133 | return $this->cache->tags($this->tag); 134 | } 135 | 136 | return $this->cache; 137 | } 138 | 139 | /** 140 | * Set the laravel version. 141 | */ 142 | public function setLaravelVersion($version) 143 | { 144 | $this->laravelVersion = $version; 145 | 146 | return $this; 147 | } 148 | 149 | /** 150 | * Detect as best we can whether tags are supported with this repository & store, 151 | * and save our result on the $supportsTags flag. 152 | * 153 | * @return void 154 | */ 155 | protected function determineTagSupport() 156 | { 157 | // Laravel >= 5.1.28 158 | if (method_exists($this->cache, 'tags') || $this->cache instanceof PsrCacheInterface) { 159 | try { 160 | // Attempt the repository tags command, which throws exceptions when unsupported 161 | $this->cache->tags($this->tag); 162 | $this->supportsTags = true; 163 | } catch (BadMethodCallException $ex) { 164 | $this->supportsTags = false; 165 | } 166 | } else { 167 | // Laravel <= 5.1.27 168 | if (method_exists($this->cache, 'getStore')) { 169 | // Check for the tags function directly on the store 170 | $this->supportsTags = method_exists($this->cache->getStore(), 'tags'); 171 | } else { 172 | // Must be using custom cache repository without getStore(), and all bets are off, 173 | // or we are mocking the cache contract (in testing), which will not create a getStore method 174 | $this->supportsTags = false; 175 | } 176 | } 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /src/Support/CustomClaims.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Tymon\JWTAuth\Support; 13 | 14 | trait CustomClaims 15 | { 16 | /** 17 | * Custom claims. 18 | * 19 | * @var array 20 | */ 21 | protected $customClaims = []; 22 | 23 | /** 24 | * Set the custom claims. 25 | * 26 | * @param array $customClaims 27 | * @return $this 28 | */ 29 | public function customClaims(array $customClaims) 30 | { 31 | $this->customClaims = $customClaims; 32 | 33 | return $this; 34 | } 35 | 36 | /** 37 | * Alias to set the custom claims. 38 | * 39 | * @param array $customClaims 40 | * @return $this 41 | */ 42 | public function claims(array $customClaims) 43 | { 44 | return $this->customClaims($customClaims); 45 | } 46 | 47 | /** 48 | * Get the custom claims. 49 | * 50 | * @return array 51 | */ 52 | public function getCustomClaims() 53 | { 54 | return $this->customClaims; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/Support/RefreshFlow.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Tymon\JWTAuth\Support; 13 | 14 | trait RefreshFlow 15 | { 16 | /** 17 | * The refresh flow flag. 18 | * 19 | * @var bool 20 | */ 21 | protected $refreshFlow = false; 22 | 23 | /** 24 | * Set the refresh flow flag. 25 | * 26 | * @param bool $refreshFlow 27 | * @return $this 28 | */ 29 | public function setRefreshFlow($refreshFlow = true) 30 | { 31 | $this->refreshFlow = $refreshFlow; 32 | 33 | return $this; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Support/Utils.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Tymon\JWTAuth\Support; 13 | 14 | use Carbon\Carbon; 15 | 16 | class Utils 17 | { 18 | /** 19 | * Get the Carbon instance for the current time. 20 | * 21 | * @return \Carbon\Carbon 22 | */ 23 | public static function now() 24 | { 25 | return Carbon::now('UTC'); 26 | } 27 | 28 | /** 29 | * Get the Carbon instance for the timestamp. 30 | * 31 | * @param int $timestamp 32 | * @return \Carbon\Carbon 33 | */ 34 | public static function timestamp($timestamp) 35 | { 36 | return Carbon::createFromTimestampUTC($timestamp)->timezone('UTC'); 37 | } 38 | 39 | /** 40 | * Checks if a timestamp is in the past. 41 | * 42 | * @param int $timestamp 43 | * @param int $leeway 44 | * @return bool 45 | */ 46 | public static function isPast($timestamp, $leeway = 0) 47 | { 48 | $timestamp = static::timestamp($timestamp); 49 | 50 | return $leeway > 0 51 | ? $timestamp->addSeconds($leeway)->isPast() 52 | : $timestamp->isPast(); 53 | } 54 | 55 | /** 56 | * Checks if a timestamp is in the future. 57 | * 58 | * @param int $timestamp 59 | * @param int $leeway 60 | * @return bool 61 | */ 62 | public static function isFuture($timestamp, $leeway = 0) 63 | { 64 | $timestamp = static::timestamp($timestamp); 65 | 66 | return $leeway > 0 67 | ? $timestamp->subSeconds($leeway)->isFuture() 68 | : $timestamp->isFuture(); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/Token.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Tymon\JWTAuth; 13 | 14 | use Tymon\JWTAuth\Validators\TokenValidator; 15 | 16 | class Token 17 | { 18 | /** 19 | * @var string 20 | */ 21 | private $value; 22 | 23 | /** 24 | * Create a new JSON Web Token. 25 | * 26 | * @param string $value 27 | * @return void 28 | */ 29 | public function __construct($value) 30 | { 31 | $this->value = (string) (new TokenValidator)->check($value); 32 | } 33 | 34 | /** 35 | * Get the token. 36 | * 37 | * @return string 38 | */ 39 | public function get() 40 | { 41 | return $this->value; 42 | } 43 | 44 | /** 45 | * Get the token when casting to string. 46 | * 47 | * @return string 48 | */ 49 | public function __toString() 50 | { 51 | return $this->get(); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/Validators/PayloadValidator.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Tymon\JWTAuth\Validators; 13 | 14 | use Tymon\JWTAuth\Claims\Collection; 15 | use Tymon\JWTAuth\Exceptions\TokenInvalidException; 16 | 17 | class PayloadValidator extends Validator 18 | { 19 | /** 20 | * The required claims. 21 | * 22 | * @var array 23 | */ 24 | protected $requiredClaims = [ 25 | 'iss', 26 | 'iat', 27 | 'exp', 28 | 'nbf', 29 | 'sub', 30 | 'jti', 31 | ]; 32 | 33 | /** 34 | * The refresh TTL. 35 | * 36 | * @var int 37 | */ 38 | protected $refreshTTL = 20160; 39 | 40 | /** 41 | * Run the validations on the payload array. 42 | * 43 | * @param \Tymon\JWTAuth\Claims\Collection $value 44 | * @return \Tymon\JWTAuth\Claims\Collection 45 | */ 46 | public function check($value) 47 | { 48 | $this->validateStructure($value); 49 | 50 | return $this->refreshFlow ? $this->validateRefresh($value) : $this->validatePayload($value); 51 | } 52 | 53 | /** 54 | * Ensure the payload contains the required claims and 55 | * the claims have the relevant type. 56 | * 57 | * @param \Tymon\JWTAuth\Claims\Collection $claims 58 | * @return void 59 | * 60 | * @throws \Tymon\JWTAuth\Exceptions\TokenInvalidException 61 | */ 62 | protected function validateStructure(Collection $claims) 63 | { 64 | if ($this->requiredClaims && ! $claims->hasAllClaims($this->requiredClaims)) { 65 | throw new TokenInvalidException('JWT payload does not contain the required claims'); 66 | } 67 | } 68 | 69 | /** 70 | * Validate the payload timestamps. 71 | * 72 | * @param \Tymon\JWTAuth\Claims\Collection $claims 73 | * @return \Tymon\JWTAuth\Claims\Collection 74 | * 75 | * @throws \Tymon\JWTAuth\Exceptions\TokenExpiredException 76 | * @throws \Tymon\JWTAuth\Exceptions\TokenInvalidException 77 | */ 78 | protected function validatePayload(Collection $claims) 79 | { 80 | return $claims->validate('payload'); 81 | } 82 | 83 | /** 84 | * Check the token in the refresh flow context. 85 | * 86 | * @param \Tymon\JWTAuth\Claims\Collection $claims 87 | * @return \Tymon\JWTAuth\Claims\Collection 88 | * 89 | * @throws \Tymon\JWTAuth\Exceptions\TokenExpiredException 90 | */ 91 | protected function validateRefresh(Collection $claims) 92 | { 93 | return $this->refreshTTL === null ? $claims : $claims->validate('refresh', $this->refreshTTL); 94 | } 95 | 96 | /** 97 | * Set the required claims. 98 | * 99 | * @param array $claims 100 | * @return $this 101 | */ 102 | public function setRequiredClaims(array $claims) 103 | { 104 | $this->requiredClaims = $claims; 105 | 106 | return $this; 107 | } 108 | 109 | /** 110 | * Set the refresh ttl. 111 | * 112 | * @param int $ttl 113 | * @return $this 114 | */ 115 | public function setRefreshTTL($ttl) 116 | { 117 | $this->refreshTTL = $ttl; 118 | 119 | return $this; 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/Validators/TokenValidator.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Tymon\JWTAuth\Validators; 13 | 14 | use Tymon\JWTAuth\Exceptions\TokenInvalidException; 15 | 16 | class TokenValidator extends Validator 17 | { 18 | /** 19 | * Check the structure of the token. 20 | * 21 | * @param string $value 22 | * @return string 23 | */ 24 | public function check($value) 25 | { 26 | return $this->validateStructure($value); 27 | } 28 | 29 | /** 30 | * @param string $token 31 | * @return string 32 | * 33 | * @throws \Tymon\JWTAuth\Exceptions\TokenInvalidException 34 | */ 35 | protected function validateStructure($token) 36 | { 37 | $parts = explode('.', $token); 38 | 39 | if (count($parts) !== 3) { 40 | throw new TokenInvalidException('Wrong number of segments'); 41 | } 42 | 43 | $parts = array_filter(array_map('trim', $parts)); 44 | 45 | if (count($parts) !== 3 || implode('.', $parts) !== $token) { 46 | throw new TokenInvalidException('Malformed token'); 47 | } 48 | 49 | return $token; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/Validators/Validator.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Tymon\JWTAuth\Validators; 13 | 14 | use Tymon\JWTAuth\Contracts\Validator as ValidatorContract; 15 | use Tymon\JWTAuth\Exceptions\JWTException; 16 | use Tymon\JWTAuth\Support\RefreshFlow; 17 | 18 | abstract class Validator implements ValidatorContract 19 | { 20 | use RefreshFlow; 21 | 22 | /** 23 | * Helper function to return a boolean. 24 | * 25 | * @param array $value 26 | * @return bool 27 | */ 28 | public function isValid($value) 29 | { 30 | try { 31 | $this->check($value); 32 | } catch (JWTException $e) { 33 | return false; 34 | } 35 | 36 | return true; 37 | } 38 | 39 | /** 40 | * Run the validation. 41 | * 42 | * @param array $value 43 | * @return void 44 | */ 45 | abstract public function check($value); 46 | } 47 | --------------------------------------------------------------------------------