├── CHANGELOG.md ├── LICENSE ├── README.pr-ar.md ├── SECURITY.md ├── 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 ├── EnvHelperTrait.php ├── JWTGenerateCertCommand.php └── 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 ├── SecretMissingException.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 │ ├── Namshi.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 /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6 | 7 | You can find and compare releases at the [GitHub release page](https://github.com/PHP-Open-Source-Saver/jwt-auth/releases). 8 | 9 | ## [Unreleased] 10 | ### Fixed 11 | - Fixed the return type of getMinutesUntilExpired in BlackList, which returned a float instead of an int when using Carbon v2. 12 | - Fixed PHPStan issue in JWTGenerateSecretCommand by ensuring displayKey($key); is called before returning, avoiding returning a void method. 13 | - Fixed missing return true; statements in validatePayload() and validateRefresh() methods of Expiration.php, IssuedAt.php, and NotBefore.php to resolve PHPStan errors. 14 | - Fixed PHPStan error related to new static() by refactoring hasAllClaims method in Collection class. 15 | 16 | 17 | ## [2.8.0] 2025-02-11 18 | Please see (https://github.com/PHP-Open-Source-Saver/jwt-auth/releases/tag/2.8.0) 19 | 20 | ### Added 21 | - Adds support for Laravel 12 22 | - Adds CI testing for PHP 8.4 23 | - Don't show jwt secret if show option is false even if the key is updated 24 | - Casts config ints to int by default in new config file publishes 25 | - Override "id" method in JWTGuard 26 | 27 | ### Removed 28 | 29 | - Dropping support for PHP 8.1, if you are still on this version, please update your PHP version in order to use the latest version of this package. 30 | 31 | ## [2.7.2] 2024-09-28 32 | 33 | ### Added 34 | - Add `cookie_key_name` config to customize cookie name for authentication 35 | - Delegate `Auth::id()` calls to the newly added `getUserId` method 36 | 37 | ## [2.7.0] 2024-07-24 38 | 39 | ### Fixed 40 | - Support for Carbon 3 alongside Carbon 2 41 | 42 | ## [2.6.0] 2024-07-11 43 | 44 | ### Added 45 | - New `getUserId` method 46 | 47 | ## [2.5.0] 2024-07-03 48 | 49 | ### Added 50 | - Refresh iat claim when refreshing a token 51 | 52 | ## [2.4.0] 2024-05-27 53 | 54 | ### Added 55 | - Support for lcobucci/jwt^5.0 (and dropped support for ^4.0) 56 | - SetSecret regenerates config with new secret in the Lcobucci provider 57 | 58 | ## [2.3.0] 2024-05-09 59 | 60 | ### Added 61 | - Support for Carbon 3 (and drop Carbon 1, but it was unused anyway) 62 | 63 | ### Removed 64 | - Dropped support for Laravel < 10 and PHP < 8.1 65 | 66 | ### Fixed 67 | - Use `id` claim for identify user if `sub` doesn't exists. 68 | 69 | ## [2.2.0] 2024-03-12 70 | 71 | ### Added 72 | - Different TTL configurations for each guard 73 | - lcobucci/jwt: add array support for `aud` claim 74 | - Laravel 11 support 75 | 76 | ## [2.1.0] 2023-02-17 77 | 78 | ### Added 79 | - Laravel 10 support 80 | 81 | ## [2.0.0] 2022-09-08 82 | - No changes to 2.0.0-RC1 83 | 84 | ### Added 85 | - Arabic translation for docs by hawkiq 86 | 87 | ## [2.0.0-RC1] 2022-08-25 88 | 89 | ### Added 90 | - Adds Octane Compatibility 91 | - Added `ask-passphrase` parameter to generating certs command 92 | - Support autocomplete guard 93 | 94 | ### Fixed 95 | - Default config value for `show_black_list_exception` changed to true 96 | - Auth header not ignoring other auth schemes 97 | - Fixed replacing of values using regex 98 | 99 | ## [1.4.2] 2022-04-22 100 | 101 | ### Added 102 | - Added exception if secret key or private/public key are missing 103 | 104 | ### Fixed 105 | - Add command for generating certs 106 | 107 | ## [1.4.1] - 2022-01-24 108 | 109 | ### Fixed 110 | - Add more ReturnTypeWillChange for PHP 8.1 compatibility 111 | 112 | ## [1.4.0] - 2022-01-18 113 | 114 | ### Added 115 | 116 | ### Fixed 117 | - Fixes #101 - Secret is not nullable but should be according to the library config boilerplate 118 | - Fixes #99 - Steps for migrating from tymons package 119 | 120 | ## [1.3.0] - 2022-01-13 121 | 122 | ### Added 123 | - PHP 8.1 support (#58, #77, #87) 124 | - Typed variables (#52) 125 | 126 | ### Fixed 127 | - Compatability with Laravel 6 versions below 6.15 128 | 129 | ## [1.2.0] - 2021-11-16 130 | 131 | ### Added 132 | - Dispatch Auth Events by @okaufmann in #45 133 | 134 | ## [1.1.1] - 2021-10-21 135 | 136 | ### Changed 137 | - Blacklisted token exception no more thrown by default by @Messhias in #32 138 | 139 | ### Fixed 140 | - ECDSA signers by @josecl in #31 141 | 142 | ## [1.1.0] - 2021-11-11 143 | 144 | ### Added 145 | - PHP 8.0 and `lcobucci/jwt` version 4 compatability by @eschricker in #14 146 | - Option to hide Blacklisted Token exception by @Messhias in #7 147 | - Throw exception for invalid encrypted cookies by @eschricker in #22 148 | 149 | ### Fixed 150 | - Typo in tests by @eschricker in #23 151 | 152 | [Unreleased]: https://github.com/PHP-Open-Source-Saver/jwt-auth/compare/2.0.0...HEAD 153 | [2.0.0]: https://github.com/PHP-Open-Source-Saver/jwt-auth/compare/1.4.2...2.0.0 154 | [2.0.0-RC1]: https://github.com/PHP-Open-Source-Saver/jwt-auth/compare/1.4.2...2.0.0-RC1 155 | [1.4.2]: https://github.com/PHP-Open-Source-Saver/jwt-auth/compare/1.4.1...1.4.2 156 | [1.4.1]: https://github.com/PHP-Open-Source-Saver/jwt-auth/compare/1.4.0...1.4.1 157 | [1.4.0]: https://github.com/PHP-Open-Source-Saver/jwt-auth/compare/1.3.0...1.4.0 158 | [1.3.0]: https://github.com/PHP-Open-Source-Saver/jwt-auth/compare/1.2.0...1.3.0 159 | [1.2.0]: https://github.com/PHP-Open-Source-Saver/jwt-auth/compare/1.1.1...1.2.0 160 | [1.1.1]: https://github.com/PHP-Open-Source-Saver/jwt-auth/compare/1.1.0...1.1.1 161 | [1.1.0]: https://github.com/PHP-Open-Source-Saver/jwt-auth/compare/1.0.2...1.1.0 162 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014-2021 Sean Tymon 4 | Copyright (c) 2021 PHP Open Source Saver 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.pr-ar.md: -------------------------------------------------------------------------------- 1 |
2 | ## الحقوق 3 |

4 | ان هذا المشروع منسوخ من 5 | 6 | [tymonsdesigns/jwt-auth](https://github.com/tymondesigns/jwt-auth/wiki) 7 |
لقد قررنا نسخه بسبب ان صاحب المشروع الاصلي لم يحدثه منذ زمن طويل. 8 |

9 | 10 | 11 | ##
للتحديث الى المستودع الحالي من [`tymondesigns/jwt-auth`](https://github.com/tymondesigns/jwt-auth)
12 | 13 |
14 | 15 | ان هذا المشروع يستخدم نيم سبيس مختلف عن, `tymondesigns/jwt-auth`, ولكن يستخدم نفس الخصائص فلا داعي للقلق: 16 | 17 | 1) استخدم ايعاز `composer remove tymon/jwt-auth` 18 | > **ملاحظة** قد تظهر بعض الاخطاء يمكنك تجاهلها. 19 | 2) قم باستبدال `Tymon\JWTAuth` ب `PHPOpenSourceSaver\JWTAuth`. 20 | > **نصيحة**: يمكن استخدام الاختصارات بمحرر النصوص لديك مثلا. Ctrl + Shift + R 21 | 3) نفذ `composer require php-open-source-saver/jwt-auth` 22 | 23 | ### ملاحظات 24 | 25 | بسبب بعض الاضافات في مشروعنا قد تواجه مشاكل بالتوافقية. _ولكن هذا لن يؤثر على مشروعك بشكل عام_, الا اذا كنت [implicitly disabled autodiscovery](https://laravel.com/docs/8.x/packages#opting-out-of-package-discovery) للبكج الاصلي. 26 | 27 | العناصر الغير متوافقة حاليا: 28 | - [`JWTGuard`](src/JWTGuard.php) يحتوي على متغير جديد في دالة الانشاء [`$eventDispatcher`](src/Providers/AbstractServiceProvider.php#L97) 29 |
30 | 31 | ## الوثائق 32 | 33 | جميع الشروحات متوفرة على [laravel-jwt-auth.readthedocs.io](https://laravel-jwt-auth.readthedocs.io/) 34 | 35 | ----------------------------------- 36 | 37 | ## الأمان 38 | 39 | اذا وجدت اي ثغرة يمكنك اتباع [بوليصة الامان](https://github.com/PHP-Open-Source-Saver/jwt-auth/security/policy) 40 | 41 | ## الرخصة 42 | 43 | The MIT License (MIT) 44 | 45 | 46 |
-------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Reporting a Vulnerability 4 | 5 | If you discover any security related issues, please email messhias@gmail.com or 6 | eric.schricker@adiutabyte.de instead of using the issue tracker. 7 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "php-open-source-saver/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/PHP-Open-Source-Saver/jwt-auth", 12 | "support": { 13 | "issues": "https://github.com/PHP-Open-Source-Saver/jwt-auth/issues", 14 | "source": "https://github.com/PHP-Open-Source-Saver/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": "Forked package creator | Developer" 23 | }, 24 | { 25 | "name": "Eric Schricker", 26 | "email": "eric.schricker@adiutabyte.de", 27 | "role": "Developer" 28 | }, 29 | { 30 | "name": "Fabio William Conceição", 31 | "email": "messhias@gmail.com", 32 | "role": "Developer" 33 | }, 34 | { 35 | "name": "Max Snow", 36 | "email": "contact@maxsnow.me", 37 | "role": "Developer" 38 | } 39 | ], 40 | "require": { 41 | "php": "^8.2", 42 | "ext-json": "*", 43 | "illuminate/auth": "^10|^11|^12", 44 | "illuminate/contracts": "^10|^11|^12", 45 | "illuminate/http": "^10|^11|^12", 46 | "illuminate/support": "^10|^11|^12", 47 | "lcobucci/jwt": "^5.4", 48 | "namshi/jose": "^7.0", 49 | "nesbot/carbon": "^2.0|^3.0" 50 | }, 51 | "require-dev": { 52 | "friendsofphp/php-cs-fixer": "^3", 53 | "illuminate/console": "^10|^11|^12", 54 | "illuminate/routing": "^10|^11|^12", 55 | "orchestra/testbench": "^8|^9|^10", 56 | "mockery/mockery": "^1.6", 57 | "phpstan/phpstan": "^2", 58 | "phpunit/phpunit": "^10.5|^11" 59 | }, 60 | "autoload": { 61 | "psr-4": { 62 | "PHPOpenSourceSaver\\JWTAuth\\": "src/" 63 | } 64 | }, 65 | "autoload-dev": { 66 | "psr-4": { 67 | "PHPOpenSourceSaver\\JWTAuth\\Test\\": "tests/" 68 | } 69 | }, 70 | "extra": { 71 | "branch-alias": { 72 | "dev-develop": "2.0-dev" 73 | }, 74 | "laravel": { 75 | "aliases": { 76 | "JWTAuth": "PHPOpenSourceSaver\\JWTAuth\\Facades\\JWTAuth", 77 | "JWTFactory": "PHPOpenSourceSaver\\JWTAuth\\Facades\\JWTFactory" 78 | }, 79 | "providers": [ 80 | "PHPOpenSourceSaver\\JWTAuth\\Providers\\LaravelServiceProvider" 81 | ] 82 | } 83 | }, 84 | "funding": [], 85 | "config": { 86 | "sort-packages": true 87 | }, 88 | "prefer-stable": true, 89 | "minimum-stability": "dev", 90 | "scripts": { 91 | "php-cs-fixer": "php-cs-fixer fix --diff", 92 | "test": "phpunit --colors=always", 93 | "test:ci": "composer test -- --coverage-text --coverage-clover=coverage.xml", 94 | "phpstan": "phpstan analyse --memory-limit=256M", 95 | "phpstan-baseline": "phpstan analyse --generate-baseline --memory-limit=256M" 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /config/config.php: -------------------------------------------------------------------------------- 1 | env('JWT_SECRET'), 19 | 20 | /* 21 | |-------------------------------------------------------------------------- 22 | | JWT Authentication Keys 23 | |-------------------------------------------------------------------------- 24 | | 25 | | The algorithm you are using, will determine whether your tokens are 26 | | signed with a random string (defined in `JWT_SECRET`) or using the 27 | | following public & private keys. 28 | | 29 | | Symmetric Algorithms: 30 | | HS256, HS384 & HS512 will use `JWT_SECRET`. 31 | | 32 | | Asymmetric Algorithms: 33 | | RS256, RS384 & RS512 / ES256, ES384 & ES512 will use the keys below. 34 | | 35 | */ 36 | 37 | 'keys' => [ 38 | /* 39 | |-------------------------------------------------------------------------- 40 | | Public Key 41 | |-------------------------------------------------------------------------- 42 | | 43 | | A path or resource to your public key. 44 | | 45 | | E.g. 'file://path/to/public/key' 46 | | 47 | */ 48 | 49 | 'public' => env('JWT_PUBLIC_KEY'), 50 | 51 | /* 52 | |-------------------------------------------------------------------------- 53 | | Private Key 54 | |-------------------------------------------------------------------------- 55 | | 56 | | A path or resource to your private key. 57 | | 58 | | E.g. 'file://path/to/private/key' 59 | | 60 | */ 61 | 62 | 'private' => env('JWT_PRIVATE_KEY'), 63 | 64 | /* 65 | |-------------------------------------------------------------------------- 66 | | Passphrase 67 | |-------------------------------------------------------------------------- 68 | | 69 | | The passphrase for your private key. Can be null if none set. 70 | | 71 | */ 72 | 73 | 'passphrase' => env('JWT_PASSPHRASE'), 74 | ], 75 | 76 | /* 77 | |-------------------------------------------------------------------------- 78 | | JWT time to live 79 | |-------------------------------------------------------------------------- 80 | | 81 | | Specify the length of time (in minutes) that the token will be valid for. 82 | | Defaults to 1 hour. 83 | | 84 | | You can also set this to null, to yield a never expiring token. 85 | | Some people may want this behaviour for e.g. a mobile app. 86 | | This is not particularly recommended, so make sure you have appropriate 87 | | systems in place to revoke the token if necessary. 88 | | Notice: If you set this to null you should remove 'exp' element from 'required_claims' list. 89 | | 90 | */ 91 | 92 | 'ttl' => (int) env('JWT_TTL', 60), 93 | 94 | /* 95 | |-------------------------------------------------------------------------- 96 | | Refresh time to live 97 | |-------------------------------------------------------------------------- 98 | | 99 | | Specify the length of time (in minutes) that the token can be refreshed 100 | | within. I.E. The user can refresh their token within a 2 week window of 101 | | the original token being created until they must re-authenticate. 102 | | Defaults to 2 weeks. 103 | | 104 | | You can also set this to null, to yield an infinite refresh time. 105 | | Some may want this instead of never expiring tokens for e.g. a mobile app. 106 | | This is not particularly recommended, so make sure you have appropriate 107 | | systems in place to revoke the token if necessary. 108 | | 109 | */ 110 | 111 | 'refresh_ttl' => (int) env('JWT_REFRESH_TTL', 20160), 112 | 113 | /* 114 | |-------------------------------------------------------------------------- 115 | | JWT hashing algorithm 116 | |-------------------------------------------------------------------------- 117 | | 118 | | Specify the hashing algorithm that will be used to sign the token. 119 | | 120 | | See here: https://github.com/namshi/jose/tree/master/src/Namshi/JOSE/Signer/OpenSSL 121 | | for possible values. 122 | | 123 | */ 124 | 125 | 'algo' => env('JWT_ALGO', 'HS256'), 126 | 127 | /* 128 | |-------------------------------------------------------------------------- 129 | | Required Claims 130 | |-------------------------------------------------------------------------- 131 | | 132 | | Specify the required claims that must exist in any token. 133 | | A TokenInvalidException will be thrown if any of these claims are not 134 | | present in the payload. 135 | | 136 | */ 137 | 138 | 'required_claims' => [ 139 | 'iss', 140 | 'iat', 141 | 'exp', 142 | 'nbf', 143 | 'sub', 144 | 'jti', 145 | ], 146 | 147 | /* 148 | |-------------------------------------------------------------------------- 149 | | Persistent Claims 150 | |-------------------------------------------------------------------------- 151 | | 152 | | Specify the claim keys to be persisted when refreshing a token. 153 | | `sub` and `iat` will automatically be persisted, in 154 | | addition to the these claims. 155 | | 156 | | Note: If a claim does not exist then it will be ignored. 157 | | 158 | */ 159 | 160 | 'persistent_claims' => [ 161 | // 'foo', 162 | // 'bar', 163 | ], 164 | 165 | /* 166 | |-------------------------------------------------------------------------- 167 | | Lock Subject 168 | |-------------------------------------------------------------------------- 169 | | 170 | | This will determine whether a `prv` claim is automatically added to 171 | | the token. The purpose of this is to ensure that if you have multiple 172 | | authentication models e.g. `App\User` & `App\OtherPerson`, then we 173 | | should prevent one authentication request from impersonating another, 174 | | if 2 tokens happen to have the same id across the 2 different models. 175 | | 176 | | Under specific circumstances, you may want to disable this behaviour 177 | | e.g. if you only have one authentication model, then you would save 178 | | a little on token size. 179 | | 180 | */ 181 | 182 | 'lock_subject' => true, 183 | 184 | /* 185 | |-------------------------------------------------------------------------- 186 | | Leeway 187 | |-------------------------------------------------------------------------- 188 | | 189 | | This property gives the jwt timestamp claims some "leeway". 190 | | Meaning that if you have any unavoidable slight clock skew on 191 | | any of your servers then this will afford you some level of cushioning. 192 | | 193 | | This applies to the claims `iat`, `nbf` and `exp`. 194 | | 195 | | Specify in seconds - only if you know you need it. 196 | | 197 | */ 198 | 199 | 'leeway' => (int) env('JWT_LEEWAY', 0), 200 | 201 | /* 202 | |-------------------------------------------------------------------------- 203 | | Blacklist Enabled 204 | |-------------------------------------------------------------------------- 205 | | 206 | | In order to invalidate tokens, you must have the blacklist enabled. 207 | | If you do not want or need this functionality, then set this to false. 208 | | 209 | */ 210 | 211 | 'blacklist_enabled' => env('JWT_BLACKLIST_ENABLED', true), 212 | 213 | /* 214 | | ------------------------------------------------------------------------- 215 | | Blacklist Grace Period 216 | | ------------------------------------------------------------------------- 217 | | 218 | | When multiple concurrent requests are made with the same JWT, 219 | | it is possible that some of them fail, due to token regeneration 220 | | on every request. 221 | | 222 | | Set grace period in seconds to prevent parallel request failure. 223 | | 224 | */ 225 | 226 | 'blacklist_grace_period' => (int) env('JWT_BLACKLIST_GRACE_PERIOD', 0), 227 | 228 | /* 229 | |-------------------------------------------------------------------------- 230 | | Show blacklisted token option 231 | |-------------------------------------------------------------------------- 232 | | 233 | | Specify if you want to show black listed token exception on the laravel logs. 234 | | 235 | */ 236 | 237 | 'show_black_list_exception' => env('JWT_SHOW_BLACKLIST_EXCEPTION', true), 238 | 239 | /* 240 | |-------------------------------------------------------------------------- 241 | | Cookies encryption 242 | |-------------------------------------------------------------------------- 243 | | 244 | | By default Laravel encrypt cookies for security reason. 245 | | If you decide to not decrypt cookies, you will have to configure Laravel 246 | | to not encrypt your cookie token by adding its name into the $except 247 | | array available in the middleware "EncryptCookies" provided by Laravel. 248 | | see https://laravel.com/docs/master/responses#cookies-and-encryption 249 | | for details. 250 | | 251 | | Set it to true if you want to decrypt cookies. 252 | | 253 | */ 254 | 255 | 'decrypt_cookies' => false, 256 | 257 | /* 258 | |-------------------------------------------------------------------------- 259 | | Cookie key name 260 | |-------------------------------------------------------------------------- 261 | | 262 | | Specify the cookie key name that you would like to use for the cookie token. 263 | | 264 | */ 265 | 266 | 'cookie_key_name' => 'token', 267 | 268 | /* 269 | |-------------------------------------------------------------------------- 270 | | Providers 271 | |-------------------------------------------------------------------------- 272 | | 273 | | Specify the various providers used throughout the package. 274 | | 275 | */ 276 | 277 | 'providers' => [ 278 | /* 279 | |-------------------------------------------------------------------------- 280 | | JWT Provider 281 | |-------------------------------------------------------------------------- 282 | | 283 | | Specify the provider that is used to create and decode the tokens. 284 | | 285 | */ 286 | 287 | 'jwt' => PHPOpenSourceSaver\JWTAuth\Providers\JWT\Lcobucci::class, 288 | 289 | /* 290 | |-------------------------------------------------------------------------- 291 | | Authentication Provider 292 | |-------------------------------------------------------------------------- 293 | | 294 | | Specify the provider that is used to authenticate users. 295 | | 296 | */ 297 | 298 | 'auth' => PHPOpenSourceSaver\JWTAuth\Providers\Auth\Illuminate::class, 299 | 300 | /* 301 | |-------------------------------------------------------------------------- 302 | | Storage Provider 303 | |-------------------------------------------------------------------------- 304 | | 305 | | Specify the provider that is used to store tokens in the blacklist. 306 | | 307 | */ 308 | 309 | 'storage' => PHPOpenSourceSaver\JWTAuth\Providers\Storage\Illuminate::class, 310 | ], 311 | ]; 312 | -------------------------------------------------------------------------------- /src/Blacklist.php: -------------------------------------------------------------------------------- 1 | 7 | * (c) 2021 PHP Open Source Saver 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | namespace PHPOpenSourceSaver\JWTAuth; 14 | 15 | use PHPOpenSourceSaver\JWTAuth\Contracts\Providers\Storage; 16 | use PHPOpenSourceSaver\JWTAuth\Support\Utils; 17 | 18 | class Blacklist 19 | { 20 | /** 21 | * The storage. 22 | * 23 | * @var Storage 24 | */ 25 | protected $storage; 26 | 27 | /** 28 | * The grace period when a token is blacklisted. In seconds. 29 | * 30 | * @var int 31 | */ 32 | protected $gracePeriod = 0; 33 | 34 | /** 35 | * Number of minutes from issue date in which a JWT can be refreshed. 36 | * 37 | * @var int 38 | */ 39 | protected $refreshTTL = 20160; 40 | 41 | /** 42 | * The unique key held within the blacklist. 43 | * 44 | * @var string 45 | */ 46 | protected $key = 'jti'; 47 | 48 | /** 49 | * Constructor. 50 | * 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 | * @return bool 62 | */ 63 | public function add(Payload $payload) 64 | { 65 | // if there is no exp claim then add the jwt to 66 | // the blacklist indefinitely 67 | if (!$payload->hasKey('exp')) { 68 | return $this->addForever($payload); 69 | } 70 | 71 | // if we have already added this token to the blacklist 72 | if (!empty($this->storage->get($this->getKey($payload)))) { 73 | return true; 74 | } 75 | 76 | $this->storage->add( 77 | $this->getKey($payload), 78 | ['valid_until' => $this->getGraceTimestamp()], 79 | $this->getMinutesUntilExpired($payload) 80 | ); 81 | 82 | return true; 83 | } 84 | 85 | /** 86 | * Get the number of minutes until the token expiry. 87 | * 88 | * @return int 89 | */ 90 | protected function getMinutesUntilExpired(Payload $payload) 91 | { 92 | $exp = Utils::timestamp($payload['exp']); 93 | $iat = Utils::timestamp($payload['iat']); 94 | 95 | // get the latter of the two expiration dates and find 96 | // the number of minutes until the expiration date, 97 | // plus 1 minute to avoid overlap 98 | $intermediateResult = $exp->max($iat->addMinutes($this->refreshTTL))->addMinute(); 99 | 100 | // Handle Carbon 2 vs 3 deprecation of "Real" diff functions, see https://github.com/PHP-Open-Source-Saver/jwt-auth/issues/260 101 | if (method_exists($intermediateResult, 'diffInRealMinutes')) { 102 | return (int) round($intermediateResult->diffInRealMinutes(null, true)); 103 | } else { 104 | return (int) round($intermediateResult->diffInMinutes(null, true)); 105 | } 106 | } 107 | 108 | /** 109 | * Add the token (jti claim) to the blacklist indefinitely. 110 | * 111 | * @return bool 112 | */ 113 | public function addForever(Payload $payload) 114 | { 115 | $this->storage->forever($this->getKey($payload), 'forever'); 116 | 117 | return true; 118 | } 119 | 120 | /** 121 | * Determine whether the token has been blacklisted. 122 | * 123 | * @return bool 124 | */ 125 | public function has(Payload $payload) 126 | { 127 | $val = $this->storage->get($this->getKey($payload)); 128 | 129 | // exit early if the token was blacklisted forever, 130 | if ('forever' === $val) { 131 | return true; 132 | } 133 | 134 | // check whether the expiry + grace has past 135 | return !empty($val) && !Utils::isFuture($val['valid_until']); 136 | } 137 | 138 | /** 139 | * Remove the token (jti claim) from the blacklist. 140 | * 141 | * @return bool 142 | */ 143 | public function remove(Payload $payload) 144 | { 145 | return $this->storage->destroy($this->getKey($payload)); 146 | } 147 | 148 | /** 149 | * Remove all tokens from the blacklist. 150 | * 151 | * @return bool 152 | */ 153 | public function clear() 154 | { 155 | $this->storage->flush(); 156 | 157 | return true; 158 | } 159 | 160 | /** 161 | * Get the timestamp when the blacklist comes into effect 162 | * This defaults to immediate (0 seconds). 163 | * 164 | * @return int 165 | */ 166 | protected function getGraceTimestamp() 167 | { 168 | return Utils::now()->addSeconds($this->gracePeriod)->getTimestamp(); 169 | } 170 | 171 | /** 172 | * Set the grace period. 173 | * 174 | * @param int $gracePeriod 175 | * 176 | * @return $this 177 | */ 178 | public function setGracePeriod($gracePeriod) 179 | { 180 | $this->gracePeriod = (int) $gracePeriod; 181 | 182 | return $this; 183 | } 184 | 185 | /** 186 | * Get the grace period. 187 | * 188 | * @return int 189 | */ 190 | public function getGracePeriod() 191 | { 192 | return $this->gracePeriod; 193 | } 194 | 195 | /** 196 | * Get the unique key held within the blacklist. 197 | */ 198 | public function getKey(Payload $payload) 199 | { 200 | return $payload($this->key); 201 | } 202 | 203 | /** 204 | * Set the unique key held within the blacklist. 205 | * 206 | * @param string $key 207 | * 208 | * @return $this 209 | */ 210 | public function setKey($key) 211 | { 212 | $this->key = value($key); 213 | 214 | return $this; 215 | } 216 | 217 | /** 218 | * Set the refresh time limit. 219 | * 220 | * @param int $ttl 221 | * 222 | * @return $this 223 | */ 224 | public function setRefreshTTL($ttl) 225 | { 226 | $this->refreshTTL = (int) $ttl; 227 | 228 | return $this; 229 | } 230 | 231 | /** 232 | * Get the refresh time limit. 233 | * 234 | * @return int 235 | */ 236 | public function getRefreshTTL() 237 | { 238 | return $this->refreshTTL; 239 | } 240 | } 241 | -------------------------------------------------------------------------------- /src/Claims/Audience.php: -------------------------------------------------------------------------------- 1 | 7 | * (c) 2021 PHP Open Source Saver 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | namespace PHPOpenSourceSaver\JWTAuth\Claims; 14 | 15 | class Audience extends Claim 16 | { 17 | protected $name = 'aud'; 18 | } 19 | -------------------------------------------------------------------------------- /src/Claims/Claim.php: -------------------------------------------------------------------------------- 1 | 7 | * (c) 2021 PHP Open Source Saver 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | namespace PHPOpenSourceSaver\JWTAuth\Claims; 14 | 15 | use Illuminate\Contracts\Support\Arrayable; 16 | use Illuminate\Contracts\Support\Jsonable; 17 | use PHPOpenSourceSaver\JWTAuth\Contracts\Claim as ClaimContract; 18 | use PHPOpenSourceSaver\JWTAuth\Exceptions\InvalidClaimException; 19 | 20 | abstract class Claim implements Arrayable, ClaimContract, Jsonable, \JsonSerializable 21 | { 22 | /** 23 | * The claim name. 24 | * 25 | * @var string 26 | */ 27 | protected $name; 28 | 29 | /** 30 | * The claim value. 31 | */ 32 | private $value; 33 | 34 | /** 35 | * @return void 36 | * 37 | * @throws InvalidClaimException 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 | * @return $this 48 | * 49 | * @throws InvalidClaimException 50 | */ 51 | public function setValue($value) 52 | { 53 | $this->value = $this->validateCreate($value); 54 | 55 | return $this; 56 | } 57 | 58 | /** 59 | * Get the claim value. 60 | */ 61 | public function getValue() 62 | { 63 | return $this->value; 64 | } 65 | 66 | /** 67 | * Set the claim name. 68 | * 69 | * @param string $name 70 | * 71 | * @return $this 72 | */ 73 | public function setName($name) 74 | { 75 | $this->name = $name; 76 | 77 | return $this; 78 | } 79 | 80 | /** 81 | * Get the claim name. 82 | * 83 | * @return string 84 | */ 85 | public function getName() 86 | { 87 | return $this->name; 88 | } 89 | 90 | /** 91 | * Validate the claim in a standalone Claim context. 92 | * 93 | * @return bool 94 | */ 95 | public function validateCreate($value) 96 | { 97 | return $value; 98 | } 99 | 100 | /** 101 | * Validate the Claim within a Payload context. 102 | * 103 | * @return bool 104 | */ 105 | public function validatePayload() 106 | { 107 | return $this->getValue(); 108 | } 109 | 110 | /** 111 | * Validate the Claim within a refresh context. 112 | * 113 | * @param int $refreshTTL 114 | * 115 | * @return bool 116 | */ 117 | public function validateRefresh($refreshTTL) 118 | { 119 | return $this->getValue(); 120 | } 121 | 122 | /** 123 | * Checks if the value matches the claim. 124 | * 125 | * @param bool $strict 126 | * 127 | * @return bool 128 | */ 129 | public function matches($value, $strict = true) 130 | { 131 | return $strict ? $this->value === $value : $this->value == $value; 132 | } 133 | 134 | /** 135 | * Convert the object into something JSON serializable. 136 | * 137 | * @return array 138 | */ 139 | #[\ReturnTypeWillChange] 140 | public function jsonSerialize() 141 | { 142 | return $this->toArray(); 143 | } 144 | 145 | /** 146 | * Build a key value array comprising of the claim name and value. 147 | * 148 | * @return array 149 | */ 150 | public function toArray() 151 | { 152 | return [$this->getName() => $this->getValue()]; 153 | } 154 | 155 | /** 156 | * Get the claim as JSON. 157 | * 158 | * @param int $options 159 | * 160 | * @return string 161 | */ 162 | public function toJson($options = JSON_UNESCAPED_SLASHES) 163 | { 164 | return json_encode($this->toArray(), $options); 165 | } 166 | 167 | /** 168 | * Get the payload as a string. 169 | * 170 | * @return string 171 | */ 172 | public function __toString() 173 | { 174 | return $this->toJson(); 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /src/Claims/Collection.php: -------------------------------------------------------------------------------- 1 | 7 | * (c) 2021 PHP Open Source Saver 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | namespace PHPOpenSourceSaver\JWTAuth\Claims; 14 | 15 | use Illuminate\Support\Collection as IlluminateCollection; 16 | use Illuminate\Support\Str; 17 | 18 | class Collection extends IlluminateCollection 19 | { 20 | /** 21 | * Create a new collection. 22 | * 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 | * 35 | * @return Claim 36 | */ 37 | public function getByClaimName($name, ?callable $callback = null, $default = null) 38 | { 39 | return $this->filter(function (Claim $claim) use ($name) { 40 | return $claim->getName() === $name; 41 | })->first($callback, $default); 42 | } 43 | 44 | /** 45 | * Validate each claim under a given context. 46 | * 47 | * @param string $context 48 | * 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 | * @return bool 70 | */ 71 | public function hasAllClaims($claims) 72 | { 73 | if (!count($claims)) { 74 | return false; 75 | } 76 | 77 | foreach ($claims as $claim) { 78 | if (!$this->has($claim)) { 79 | return false; 80 | } 81 | } 82 | 83 | return true; 84 | } 85 | 86 | /** 87 | * Get the claims as key/val array. 88 | * 89 | * @return array 90 | */ 91 | public function toPlainArray() 92 | { 93 | return $this->map(function (Claim $claim) { 94 | return $claim->getValue(); 95 | })->toArray(); 96 | } 97 | 98 | protected function getArrayableItems($items) 99 | { 100 | return $this->sanitizeClaims($items); 101 | } 102 | 103 | /** 104 | * Ensure that the given claims array is keyed by the claim name. 105 | * 106 | * @return array 107 | */ 108 | private function sanitizeClaims($items) 109 | { 110 | $claims = []; 111 | foreach ($items as $key => $value) { 112 | if (!is_string($key) && $value instanceof Claim) { 113 | $key = $value->getName(); 114 | } 115 | 116 | $claims[$key] = $value; 117 | } 118 | 119 | return $claims; 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/Claims/Custom.php: -------------------------------------------------------------------------------- 1 | 7 | * (c) 2021 PHP Open Source Saver 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | namespace PHPOpenSourceSaver\JWTAuth\Claims; 14 | 15 | use PHPOpenSourceSaver\JWTAuth\Exceptions\InvalidClaimException; 16 | 17 | class Custom extends Claim 18 | { 19 | /** 20 | * @param string $name 21 | * 22 | * @return void 23 | * 24 | * @throws InvalidClaimException 25 | */ 26 | public function __construct($name, $value) 27 | { 28 | parent::__construct($value); 29 | $this->setName($name); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Claims/DatetimeTrait.php: -------------------------------------------------------------------------------- 1 | 7 | * (c) 2021 PHP Open Source Saver 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | namespace PHPOpenSourceSaver\JWTAuth\Claims; 14 | 15 | use PHPOpenSourceSaver\JWTAuth\Exceptions\InvalidClaimException; 16 | use PHPOpenSourceSaver\JWTAuth\Support\Utils; 17 | 18 | trait DatetimeTrait 19 | { 20 | /** 21 | * Time leeway in seconds. 22 | * 23 | * @var int 24 | */ 25 | protected $leeway = 0; 26 | 27 | /** 28 | * Set the claim value, and call a validate method. 29 | * 30 | * @return $this 31 | * 32 | * @throws InvalidClaimException 33 | */ 34 | public function setValue($value) 35 | { 36 | if ($value instanceof \DateInterval) { 37 | $value = Utils::now()->add($value); 38 | } 39 | 40 | if ($value instanceof \DateTimeInterface) { 41 | $value = $value->getTimestamp(); 42 | } 43 | 44 | return parent::setValue($value); 45 | } 46 | 47 | public function validateCreate($value) 48 | { 49 | if (!is_numeric($value)) { 50 | throw new InvalidClaimException($this); 51 | } 52 | 53 | return $value; 54 | } 55 | 56 | /** 57 | * Determine whether the value is in the future. 58 | * 59 | * @return bool 60 | */ 61 | protected function isFuture($value) 62 | { 63 | return Utils::isFuture($value, $this->leeway); 64 | } 65 | 66 | /** 67 | * Determine whether the value is in the past. 68 | * 69 | * @return bool 70 | */ 71 | protected function isPast($value) 72 | { 73 | return Utils::isPast($value, $this->leeway); 74 | } 75 | 76 | /** 77 | * Set the leeway in seconds. 78 | * 79 | * @param int $leeway 80 | * 81 | * @return $this 82 | */ 83 | public function setLeeway($leeway) 84 | { 85 | $this->leeway = $leeway; 86 | 87 | return $this; 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/Claims/Expiration.php: -------------------------------------------------------------------------------- 1 | 7 | * (c) 2021 PHP Open Source Saver 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | namespace PHPOpenSourceSaver\JWTAuth\Claims; 14 | 15 | use PHPOpenSourceSaver\JWTAuth\Exceptions\TokenExpiredException; 16 | 17 | class Expiration extends Claim 18 | { 19 | use DatetimeTrait; 20 | 21 | protected $name = 'exp'; 22 | 23 | public function validatePayload() 24 | { 25 | if ($this->isPast($this->getValue())) { 26 | throw new TokenExpiredException('Token has expired'); 27 | } 28 | 29 | return true; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Claims/Factory.php: -------------------------------------------------------------------------------- 1 | 7 | * (c) 2021 PHP Open Source Saver 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | namespace PHPOpenSourceSaver\JWTAuth\Claims; 14 | 15 | use Illuminate\Http\Request; 16 | use Illuminate\Support\Str; 17 | use PHPOpenSourceSaver\JWTAuth\Exceptions\InvalidClaimException; 18 | use PHPOpenSourceSaver\JWTAuth\Support\Utils; 19 | 20 | class Factory 21 | { 22 | /** 23 | * The request. 24 | * 25 | * @var Request 26 | */ 27 | protected $request; 28 | 29 | /** 30 | * The TTL. 31 | * 32 | * @var int|null 33 | */ 34 | protected $ttl = 60; 35 | 36 | /** 37 | * Time leeway in seconds. 38 | * 39 | * @var int 40 | */ 41 | protected $leeway = 0; 42 | 43 | /** 44 | * The classes map. 45 | * 46 | * @var array 47 | */ 48 | private $classMap = [ 49 | 'aud' => Audience::class, 50 | 'exp' => Expiration::class, 51 | 'iat' => IssuedAt::class, 52 | 'iss' => Issuer::class, 53 | 'jti' => JwtId::class, 54 | 'nbf' => NotBefore::class, 55 | 'sub' => Subject::class, 56 | ]; 57 | 58 | /** 59 | * Constructor. 60 | * 61 | * @return void 62 | */ 63 | public function __construct(Request $request) 64 | { 65 | $this->request = $request; 66 | } 67 | 68 | /** 69 | * Get the instance of the claim when passing the name and value. 70 | * 71 | * @param string $name 72 | * 73 | * @return Claim 74 | * 75 | * @throws InvalidClaimException 76 | */ 77 | public function get($name, $value) 78 | { 79 | if ($this->has($name)) { 80 | $claim = new $this->classMap[$name]($value); 81 | 82 | return method_exists($claim, 'setLeeway') ? 83 | $claim->setLeeway($this->leeway) : 84 | $claim; 85 | } 86 | 87 | return new Custom($name, $value); 88 | } 89 | 90 | /** 91 | * Check whether the claim exists. 92 | * 93 | * @param string $name 94 | * 95 | * @return bool 96 | */ 97 | public function has($name) 98 | { 99 | return array_key_exists($name, $this->classMap); 100 | } 101 | 102 | /** 103 | * Generate the initial value and return the Claim instance. 104 | * 105 | * @param string $name 106 | * 107 | * @return Claim 108 | * 109 | * @throws InvalidClaimException 110 | */ 111 | public function make($name) 112 | { 113 | return $this->get($name, $this->$name()); 114 | } 115 | 116 | /** 117 | * Get the Issuer (iss) claim. 118 | * 119 | * @return string 120 | */ 121 | public function iss() 122 | { 123 | return $this->request->url(); 124 | } 125 | 126 | /** 127 | * Get the Issued At (iat) claim. 128 | * 129 | * @return int 130 | */ 131 | public function iat() 132 | { 133 | return Utils::now()->getTimestamp(); 134 | } 135 | 136 | /** 137 | * Get the Expiration (exp) claim. 138 | * 139 | * @return int 140 | */ 141 | public function exp() 142 | { 143 | return Utils::now()->addMinutes($this->ttl)->getTimestamp(); 144 | } 145 | 146 | /** 147 | * Get the Not Before (nbf) claim. 148 | * 149 | * @return int 150 | */ 151 | public function nbf() 152 | { 153 | return Utils::now()->getTimestamp(); 154 | } 155 | 156 | /** 157 | * Get the JWT Id (jti) claim. 158 | * 159 | * @return string 160 | */ 161 | public function jti() 162 | { 163 | return Str::random(); 164 | } 165 | 166 | /** 167 | * Add a new claim mapping. 168 | * 169 | * @param string $name 170 | * @param string $classPath 171 | * 172 | * @return $this 173 | */ 174 | public function extend($name, $classPath) 175 | { 176 | $this->classMap[$name] = $classPath; 177 | 178 | return $this; 179 | } 180 | 181 | /** 182 | * Set the request instance. 183 | * 184 | * @return $this 185 | */ 186 | public function setRequest(Request $request) 187 | { 188 | $this->request = $request; 189 | 190 | return $this; 191 | } 192 | 193 | /** 194 | * Set the token ttl (in minutes). 195 | * 196 | * @param int|null $ttl 197 | * 198 | * @return $this 199 | */ 200 | public function setTTL($ttl) 201 | { 202 | $this->ttl = $ttl ? (int) $ttl : $ttl; 203 | 204 | return $this; 205 | } 206 | 207 | /** 208 | * Get the token ttl. 209 | * 210 | * @return int|null 211 | */ 212 | public function getTTL() 213 | { 214 | return $this->ttl; 215 | } 216 | 217 | /** 218 | * Set the leeway in seconds. 219 | * 220 | * @param int $leeway 221 | * 222 | * @return $this 223 | */ 224 | public function setLeeway($leeway) 225 | { 226 | $this->leeway = $leeway; 227 | 228 | return $this; 229 | } 230 | } 231 | -------------------------------------------------------------------------------- /src/Claims/IssuedAt.php: -------------------------------------------------------------------------------- 1 | 7 | * (c) 2021 PHP Open Source Saver 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | namespace PHPOpenSourceSaver\JWTAuth\Claims; 14 | 15 | use PHPOpenSourceSaver\JWTAuth\Exceptions\InvalidClaimException; 16 | use PHPOpenSourceSaver\JWTAuth\Exceptions\TokenExpiredException; 17 | use PHPOpenSourceSaver\JWTAuth\Exceptions\TokenInvalidException; 18 | 19 | class IssuedAt extends Claim 20 | { 21 | use DatetimeTrait { 22 | validateCreate as commonValidateCreate; 23 | } 24 | 25 | protected $name = 'iat'; 26 | 27 | public function validateCreate($value) 28 | { 29 | $this->commonValidateCreate($value); 30 | 31 | if ($this->isFuture($value)) { 32 | throw new InvalidClaimException($this); 33 | } 34 | 35 | return $value; 36 | } 37 | 38 | public function validatePayload() 39 | { 40 | if ($this->isFuture($this->getValue())) { 41 | throw new TokenInvalidException('Issued At (iat) timestamp cannot be in the future'); 42 | } 43 | 44 | return true; 45 | } 46 | 47 | public function validateRefresh($refreshTTL) 48 | { 49 | if ($this->isPast($this->getValue() + $refreshTTL * 60)) { 50 | throw new TokenExpiredException('Token has expired and can no longer be refreshed'); 51 | } 52 | 53 | return true; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/Claims/Issuer.php: -------------------------------------------------------------------------------- 1 | 7 | * (c) 2021 PHP Open Source Saver 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | namespace PHPOpenSourceSaver\JWTAuth\Claims; 14 | 15 | class Issuer extends Claim 16 | { 17 | protected $name = 'iss'; 18 | } 19 | -------------------------------------------------------------------------------- /src/Claims/JwtId.php: -------------------------------------------------------------------------------- 1 | 7 | * (c) 2021 PHP Open Source Saver 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | namespace PHPOpenSourceSaver\JWTAuth\Claims; 14 | 15 | class JwtId extends Claim 16 | { 17 | protected $name = 'jti'; 18 | } 19 | -------------------------------------------------------------------------------- /src/Claims/NotBefore.php: -------------------------------------------------------------------------------- 1 | 7 | * (c) 2021 PHP Open Source Saver 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | namespace PHPOpenSourceSaver\JWTAuth\Claims; 14 | 15 | use PHPOpenSourceSaver\JWTAuth\Exceptions\TokenInvalidException; 16 | 17 | class NotBefore extends Claim 18 | { 19 | use DatetimeTrait; 20 | 21 | protected $name = 'nbf'; 22 | 23 | /** 24 | * @throws TokenInvalidException 25 | */ 26 | public function validatePayload() 27 | { 28 | if ($this->isFuture($this->getValue())) { 29 | throw new TokenInvalidException('Not Before (nbf) timestamp cannot be in the future'); 30 | } 31 | 32 | return true; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Claims/Subject.php: -------------------------------------------------------------------------------- 1 | 7 | * (c) 2021 PHP Open Source Saver 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | namespace PHPOpenSourceSaver\JWTAuth\Claims; 14 | 15 | class Subject extends Claim 16 | { 17 | protected $name = 'sub'; 18 | } 19 | -------------------------------------------------------------------------------- /src/Console/EnvHelperTrait.php: -------------------------------------------------------------------------------- 1 | 7 | * (c) 2021 PHP Open Source Saver 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | namespace PHPOpenSourceSaver\JWTAuth\Console; 14 | 15 | use Illuminate\Support\Str; 16 | 17 | trait EnvHelperTrait 18 | { 19 | /** 20 | * Checks if the env file exists. 21 | */ 22 | protected function envFileExists(): bool 23 | { 24 | return file_exists($this->envPath()); 25 | } 26 | 27 | /** 28 | * Update an env-file entry. 29 | * 30 | * @param string|int $value 31 | */ 32 | public function updateEnvEntry(string $key, $value, ?\Closure $confirmOnExisting = null): bool 33 | { 34 | $filepath = $this->envPath(); 35 | 36 | $filecontents = $this->getFileContents($filepath); 37 | 38 | if (false === Str::contains($filecontents, $key)) { 39 | // create new entry 40 | $this->putFileContents( 41 | $filepath, 42 | $filecontents.PHP_EOL."{$key}={$value}".PHP_EOL 43 | ); 44 | 45 | return true; 46 | } else { 47 | if (is_null($confirmOnExisting) || $confirmOnExisting()) { 48 | // update existing entry 49 | $this->putFileContents( 50 | $filepath, 51 | preg_replace( 52 | "/{$key}=.*/", 53 | "{$key}={$value}", 54 | $filecontents 55 | ) 56 | ); 57 | 58 | return true; 59 | } 60 | } 61 | 62 | return false; 63 | } 64 | 65 | protected function getFileContents(string $filepath): string 66 | { 67 | return file_get_contents($filepath); 68 | } 69 | 70 | protected function putFileContents(string $filepath, string $data): void 71 | { 72 | file_put_contents($filepath, $data); 73 | } 74 | 75 | /** 76 | * Get the .env file path. 77 | */ 78 | protected function envPath(): string 79 | { 80 | if (method_exists($this->laravel, 'environmentFilePath')) { 81 | return $this->laravel->environmentFilePath(); 82 | } 83 | 84 | return $this->laravel->basePath('.env'); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/Console/JWTGenerateCertCommand.php: -------------------------------------------------------------------------------- 1 | 7 | * (c) 2021 PHP Open Source Saver 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | namespace PHPOpenSourceSaver\JWTAuth\Console; 14 | 15 | use Illuminate\Console\Command; 16 | 17 | class JWTGenerateCertCommand extends Command 18 | { 19 | use EnvHelperTrait; 20 | /** 21 | * The name and signature of the console command. 22 | * 23 | * @var string 24 | */ 25 | protected $signature = 'jwt:generate-certs 26 | {--force : Override certificates if existing} 27 | {--algo= : Algorithm (rsa/ec)} 28 | {--bits= : RSA-Key length (1024,2048,4096,8192} 29 | {--sha= : SHA-variant (1,224,256,384,512)} 30 | {--dir= : Directory where the certificates should be placed} 31 | {--curve= : EC-Curvename (e.g. secp384r1, prime256v1 )} 32 | {--passphrase= : Passphrase} 33 | {--ask-passphrase : Enter passphrase instead passing as argument}'; 34 | 35 | /** 36 | * The console command description. 37 | * 38 | * @var string 39 | */ 40 | protected $description = 'Generates a new cert pair'; 41 | 42 | /** 43 | * Execute the console command. 44 | * 45 | * @return int 46 | */ 47 | public function handle() 48 | { 49 | $force = $this->option('force'); 50 | $directory = $this->option('dir') ? $this->option('dir') : 'storage/certs'; 51 | $algo = $this->option('algo') ? $this->option('algo') : 'rsa'; 52 | $bits = $this->option('bits') ? intval($this->option('bits')) : 4096; 53 | $shaVariant = $this->option('sha') ? intval($this->option('sha')) : 512; 54 | $curve = $this->option('curve') ? $this->option('curve') : 'prime256v1'; 55 | 56 | if ($this->option('ask-passphrase')) { 57 | $passphrase = $this->secret('Passphrase'); 58 | } else { 59 | $passphrase = $this->option('passphrase') ? $this->option('passphrase') : null; 60 | } 61 | 62 | $filenamePublic = sprintf('%s/jwt-%s-%d-public.pem', $directory, $algo, $bits); 63 | $filenamePrivate = sprintf('%s/jwt-%s-%d-private.pem', $directory, $algo, $bits); 64 | 65 | if (true === file_exists($filenamePrivate)) { 66 | $this->warn('Private cert already exists'); 67 | 68 | if (!$force) { 69 | $this->warn('Aborting'); 70 | 71 | return; 72 | } 73 | } 74 | 75 | if (true === file_exists($filenamePublic)) { 76 | $this->warn('Public cert already exists'); 77 | 78 | if (!$force) { 79 | $this->warn('Aborting'); 80 | 81 | return; 82 | } 83 | } 84 | 85 | switch ($algo) { 86 | case 'rsa': 87 | $keyType = OPENSSL_KEYTYPE_RSA; 88 | $algoIdentifier = sprintf('RS%d', $shaVariant); 89 | break; 90 | 91 | case 'ec': 92 | $keyType = OPENSSL_KEYTYPE_EC; 93 | $algoIdentifier = sprintf('ES%d', $shaVariant); 94 | break; 95 | 96 | default: 97 | $this->error('Unknown algorithm'); 98 | 99 | return -1; 100 | } 101 | 102 | // Create the private and public key 103 | $res = openssl_pkey_new([ 104 | 'digest_alg' => sprintf('sha%d', $shaVariant), 105 | 'private_key_bits' => $bits, 106 | 'private_key_type' => $keyType, 107 | 'curve_name' => $curve, 108 | ]); 109 | 110 | // Extract the private key from $res to $privKey 111 | openssl_pkey_export($res, $privKey, $passphrase); 112 | 113 | // Extract the public key from $res to $pubKey 114 | $pubKey = openssl_pkey_get_details($res); 115 | $pubKey = $pubKey['key']; 116 | 117 | // save certificates to disk 118 | if (false === is_dir($directory)) { 119 | mkdir($directory, 0777, true); 120 | } 121 | 122 | file_put_contents($filenamePrivate, $privKey); 123 | file_put_contents($filenamePublic, $pubKey); 124 | 125 | // Updated .env-file 126 | if (!$this->envFileExists()) { 127 | $this->error('.env file missing'); 128 | 129 | return -1; 130 | } 131 | 132 | $this->updateEnvEntry('JWT_ALGO', $algoIdentifier); 133 | $this->updateEnvEntry('JWT_PRIVATE_KEY', sprintf('file://../%s', $filenamePrivate)); 134 | $this->updateEnvEntry('JWT_PUBLIC_KEY', sprintf('file://../%s', $filenamePublic)); 135 | $this->updateEnvEntry('JWT_PASSPHRASE', $passphrase ?? ''); 136 | 137 | return 0; 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /src/Console/JWTGenerateSecretCommand.php: -------------------------------------------------------------------------------- 1 | 7 | * (c) 2021 PHP Open Source Saver 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | namespace PHPOpenSourceSaver\JWTAuth\Console; 14 | 15 | use Illuminate\Console\Command; 16 | use Illuminate\Support\Str; 17 | 18 | class JWTGenerateSecretCommand extends Command 19 | { 20 | use EnvHelperTrait; 21 | 22 | /** 23 | * The console command signature. 24 | * 25 | * @var string 26 | */ 27 | protected $signature = 'jwt:secret 28 | {--s|show : Display the key instead of modifying files.} 29 | {--always-no : Skip generating key if it already exists.} 30 | {--f|force : Skip confirmation when overwriting an existing key.}'; 31 | 32 | /** 33 | * The console command description. 34 | * 35 | * @var string 36 | */ 37 | protected $description = 'Set the JWTAuth secret key used to sign the tokens'; 38 | 39 | /** 40 | * Execute the console command. 41 | * 42 | * @return void 43 | */ 44 | public function handle() 45 | { 46 | $key = Str::random(64); 47 | 48 | if ($this->option('show')) { 49 | $this->comment($key); 50 | 51 | return; 52 | } 53 | 54 | if (!$this->envFileExists()) { 55 | $this->displayKey($key); 56 | 57 | return; 58 | } 59 | 60 | $updated = $this->updateEnvEntry('JWT_SECRET', $key, function () { 61 | if ($this->option('always-no')) { 62 | $this->comment('Secret key already exists. Skipping...'); 63 | 64 | return false; 65 | } 66 | 67 | if (false === $this->isConfirmed()) { 68 | $this->comment('Phew... No changes were made to your secret key.'); 69 | 70 | return false; 71 | } 72 | 73 | return true; 74 | }); 75 | 76 | if ($updated) { 77 | $this->updateEnvEntry('JWT_ALGO', 'HS256'); 78 | $this->info('jwt-auth secret set successfully.'); 79 | } 80 | } 81 | 82 | /** 83 | * Display the key. 84 | * 85 | * @param string $key 86 | * 87 | * @return void 88 | */ 89 | protected function displayKey($key) 90 | { 91 | $this->laravel['config']['jwt.secret'] = $key; 92 | 93 | $this->info("jwt-auth secret [$key] set successfully."); 94 | } 95 | 96 | /** 97 | * Check if the modification is confirmed. 98 | * 99 | * @return bool 100 | */ 101 | protected function isConfirmed() 102 | { 103 | return $this->option('force') ? true : $this->confirm( 104 | 'This will invalidate all existing tokens. Are you sure you want to override the secret key?' 105 | ); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/Contracts/Claim.php: -------------------------------------------------------------------------------- 1 | 7 | * (c) 2021 PHP Open Source Saver 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | namespace PHPOpenSourceSaver\JWTAuth\Contracts; 14 | 15 | use PHPOpenSourceSaver\JWTAuth\Exceptions\InvalidClaimException; 16 | 17 | interface Claim 18 | { 19 | /** 20 | * Set the claim value, and call a validate method. 21 | * 22 | * @return $this 23 | * 24 | * @throws InvalidClaimException 25 | */ 26 | public function setValue($value); 27 | 28 | /** 29 | * Get the claim value. 30 | */ 31 | public function getValue(); 32 | 33 | /** 34 | * Set the claim name. 35 | * 36 | * @param string $name 37 | * 38 | * @return $this 39 | */ 40 | public function setName($name); 41 | 42 | /** 43 | * Get the claim name. 44 | * 45 | * @return string 46 | */ 47 | public function getName(); 48 | 49 | /** 50 | * Validate the Claim value. 51 | * 52 | * @return bool 53 | */ 54 | public function validateCreate($value); 55 | } 56 | -------------------------------------------------------------------------------- /src/Contracts/Http/Parser.php: -------------------------------------------------------------------------------- 1 | 7 | * (c) 2021 PHP Open Source Saver 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | namespace PHPOpenSourceSaver\JWTAuth\Contracts\Http; 14 | 15 | use Illuminate\Http\Request; 16 | 17 | interface Parser 18 | { 19 | /** 20 | * Parse the request. 21 | * 22 | * @return string|null 23 | */ 24 | public function parse(Request $request); 25 | } 26 | -------------------------------------------------------------------------------- /src/Contracts/JWTSubject.php: -------------------------------------------------------------------------------- 1 | 7 | * (c) 2021 PHP Open Source Saver 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | namespace PHPOpenSourceSaver\JWTAuth\Contracts; 14 | 15 | interface JWTSubject 16 | { 17 | /** 18 | * Get the identifier that will be stored in the subject claim of the JWT. 19 | */ 20 | public function getJWTIdentifier(); 21 | 22 | /** 23 | * Return a key value array, containing any custom claims to be added to the JWT. 24 | * 25 | * @return array 26 | */ 27 | public function getJWTCustomClaims(); 28 | } 29 | -------------------------------------------------------------------------------- /src/Contracts/Providers/Auth.php: -------------------------------------------------------------------------------- 1 | 7 | * (c) 2021 PHP Open Source Saver 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | namespace PHPOpenSourceSaver\JWTAuth\Contracts\Providers; 14 | 15 | interface Auth 16 | { 17 | /** 18 | * Check a user's credentials. 19 | */ 20 | public function byCredentials(array $credentials); 21 | 22 | /** 23 | * Authenticate a user via the id. 24 | */ 25 | public function byId($id); 26 | 27 | /** 28 | * Get the currently authenticated user. 29 | */ 30 | public function user(); 31 | } 32 | -------------------------------------------------------------------------------- /src/Contracts/Providers/JWT.php: -------------------------------------------------------------------------------- 1 | 7 | * (c) 2021 PHP Open Source Saver 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | namespace PHPOpenSourceSaver\JWTAuth\Contracts\Providers; 14 | 15 | interface JWT 16 | { 17 | /** 18 | * @return string 19 | */ 20 | public function encode(array $payload); 21 | 22 | /** 23 | * @param string $token 24 | * 25 | * @return array 26 | */ 27 | public function decode($token); 28 | } 29 | -------------------------------------------------------------------------------- /src/Contracts/Providers/Storage.php: -------------------------------------------------------------------------------- 1 | 7 | * (c) 2021 PHP Open Source Saver 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | namespace PHPOpenSourceSaver\JWTAuth\Contracts\Providers; 14 | 15 | interface Storage 16 | { 17 | /** 18 | * @param string $key 19 | * @param int $minutes 20 | * 21 | * @return void 22 | */ 23 | public function add($key, $value, $minutes); 24 | 25 | /** 26 | * @param string $key 27 | * 28 | * @return void 29 | */ 30 | public function forever($key, $value); 31 | 32 | /** 33 | * @param string $key 34 | */ 35 | public function get($key); 36 | 37 | /** 38 | * @param string $key 39 | * 40 | * @return bool 41 | */ 42 | public function destroy($key); 43 | 44 | /** 45 | * @return void 46 | */ 47 | public function flush(); 48 | } 49 | -------------------------------------------------------------------------------- /src/Contracts/Validator.php: -------------------------------------------------------------------------------- 1 | 7 | * (c) 2021 PHP Open Source Saver 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | namespace PHPOpenSourceSaver\JWTAuth\Contracts; 14 | 15 | interface Validator 16 | { 17 | /** 18 | * Perform some checks on the value. 19 | * 20 | * @return void 21 | */ 22 | public function check($value); 23 | 24 | /** 25 | * Helper function to return a boolean. 26 | * 27 | * @param array $value 28 | * 29 | * @return bool 30 | */ 31 | public function isValid($value); 32 | } 33 | -------------------------------------------------------------------------------- /src/Exceptions/InvalidClaimException.php: -------------------------------------------------------------------------------- 1 | 7 | * (c) 2021 PHP Open Source Saver 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | namespace PHPOpenSourceSaver\JWTAuth\Exceptions; 14 | 15 | use PHPOpenSourceSaver\JWTAuth\Claims\Claim; 16 | 17 | class InvalidClaimException extends JWTException 18 | { 19 | /** 20 | * Constructor. 21 | * 22 | * @param int $code 23 | * 24 | * @return void 25 | */ 26 | public function __construct(Claim $claim, $code = 0, ?\Exception $previous = null) 27 | { 28 | parent::__construct('Invalid value provided for claim ['.$claim->getName().']', $code, $previous); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Exceptions/JWTException.php: -------------------------------------------------------------------------------- 1 | 7 | * (c) 2021 PHP Open Source Saver 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | namespace PHPOpenSourceSaver\JWTAuth\Exceptions; 14 | 15 | class JWTException extends \Exception 16 | { 17 | protected $message = 'An error occurred'; 18 | } 19 | -------------------------------------------------------------------------------- /src/Exceptions/PayloadException.php: -------------------------------------------------------------------------------- 1 | 7 | * (c) 2021 PHP Open Source Saver 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | namespace PHPOpenSourceSaver\JWTAuth\Exceptions; 14 | 15 | class PayloadException extends JWTException 16 | { 17 | } 18 | -------------------------------------------------------------------------------- /src/Exceptions/SecretMissingException.php: -------------------------------------------------------------------------------- 1 | 7 | * (c) 2021 PHP Open Source Saver 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | namespace PHPOpenSourceSaver\JWTAuth\Exceptions; 14 | 15 | class SecretMissingException extends JWTException 16 | { 17 | } 18 | -------------------------------------------------------------------------------- /src/Exceptions/TokenBlacklistedException.php: -------------------------------------------------------------------------------- 1 | 7 | * (c) 2021 PHP Open Source Saver 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | namespace PHPOpenSourceSaver\JWTAuth\Exceptions; 14 | 15 | class TokenBlacklistedException extends TokenInvalidException 16 | { 17 | } 18 | -------------------------------------------------------------------------------- /src/Exceptions/TokenExpiredException.php: -------------------------------------------------------------------------------- 1 | 7 | * (c) 2021 PHP Open Source Saver 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | namespace PHPOpenSourceSaver\JWTAuth\Exceptions; 14 | 15 | class TokenExpiredException extends JWTException 16 | { 17 | } 18 | -------------------------------------------------------------------------------- /src/Exceptions/TokenInvalidException.php: -------------------------------------------------------------------------------- 1 | 7 | * (c) 2021 PHP Open Source Saver 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | namespace PHPOpenSourceSaver\JWTAuth\Exceptions; 14 | 15 | class TokenInvalidException extends JWTException 16 | { 17 | } 18 | -------------------------------------------------------------------------------- /src/Exceptions/UserNotDefinedException.php: -------------------------------------------------------------------------------- 1 | 7 | * (c) 2021 PHP Open Source Saver 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | namespace PHPOpenSourceSaver\JWTAuth\Exceptions; 14 | 15 | class UserNotDefinedException extends JWTException 16 | { 17 | } 18 | -------------------------------------------------------------------------------- /src/Facades/JWTAuth.php: -------------------------------------------------------------------------------- 1 | 7 | * (c) 2021 PHP Open Source Saver 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | namespace PHPOpenSourceSaver\JWTAuth\Facades; 14 | 15 | use Illuminate\Support\Facades\Facade; 16 | 17 | class JWTAuth extends Facade 18 | { 19 | /** 20 | * Get the registered name of the component. 21 | * 22 | * @return string 23 | */ 24 | protected static function getFacadeAccessor() 25 | { 26 | return 'tymon.jwt.auth'; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Facades/JWTFactory.php: -------------------------------------------------------------------------------- 1 | 7 | * (c) 2021 PHP Open Source Saver 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | namespace PHPOpenSourceSaver\JWTAuth\Facades; 14 | 15 | use Illuminate\Support\Facades\Facade; 16 | 17 | class JWTFactory extends Facade 18 | { 19 | /** 20 | * Get the registered name of the component. 21 | * 22 | * @return string 23 | */ 24 | protected static function getFacadeAccessor() 25 | { 26 | return 'tymon.jwt.payload.factory'; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Facades/JWTProvider.php: -------------------------------------------------------------------------------- 1 | 7 | * (c) 2021 PHP Open Source Saver 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | namespace PHPOpenSourceSaver\JWTAuth\Facades; 14 | 15 | use Illuminate\Support\Facades\Facade; 16 | 17 | class JWTProvider extends Facade 18 | { 19 | /** 20 | * Get the registered name of the component. 21 | * 22 | * @return string 23 | */ 24 | protected static function getFacadeAccessor() 25 | { 26 | return 'tymon.jwt.provider.jwt'; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Factory.php: -------------------------------------------------------------------------------- 1 | 7 | * (c) 2021 PHP Open Source Saver 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | namespace PHPOpenSourceSaver\JWTAuth; 14 | 15 | use PHPOpenSourceSaver\JWTAuth\Claims\Claim; 16 | use PHPOpenSourceSaver\JWTAuth\Claims\Collection; 17 | use PHPOpenSourceSaver\JWTAuth\Claims\Factory as ClaimFactory; 18 | use PHPOpenSourceSaver\JWTAuth\Support\CustomClaims; 19 | use PHPOpenSourceSaver\JWTAuth\Support\RefreshFlow; 20 | use PHPOpenSourceSaver\JWTAuth\Validators\PayloadValidator; 21 | 22 | class Factory 23 | { 24 | use CustomClaims; 25 | use RefreshFlow; 26 | 27 | /** 28 | * The claim factory. 29 | * 30 | * @var ClaimFactory 31 | */ 32 | protected $claimFactory; 33 | 34 | /** 35 | * The validator. 36 | * 37 | * @var PayloadValidator 38 | */ 39 | protected $validator; 40 | 41 | /** 42 | * The default claims. 43 | * 44 | * @var array 45 | */ 46 | protected $defaultClaims = [ 47 | 'iss', 48 | 'iat', 49 | 'exp', 50 | 'nbf', 51 | 'jti', 52 | ]; 53 | 54 | /** 55 | * The claims collection. 56 | * 57 | * @var Collection 58 | */ 59 | protected $claims; 60 | 61 | /** 62 | * Constructor. 63 | * 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 | * 78 | * @return Payload 79 | */ 80 | public function make($resetClaims = false) 81 | { 82 | if ($resetClaims) { 83 | $this->emptyClaims(); 84 | } 85 | 86 | return $this->withClaims($this->buildClaimsCollection()); 87 | } 88 | 89 | /** 90 | * Empty the claims collection. 91 | * 92 | * @return $this 93 | */ 94 | public function emptyClaims() 95 | { 96 | $this->claims = new Collection(); 97 | 98 | return $this; 99 | } 100 | 101 | /** 102 | * Add an array of claims to the Payload. 103 | * 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 | * 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 (null === $this->claimFactory->getTTL() && $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 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 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 | * @return Payload 176 | */ 177 | public function withClaims(Collection $claims) 178 | { 179 | return new Payload($claims, $this->validator, $this->refreshFlow); 180 | } 181 | 182 | /** 183 | * Set the default claims to be added to the Payload. 184 | * 185 | * @return $this 186 | */ 187 | public function setDefaultClaims(array $claims) 188 | { 189 | $this->defaultClaims = $claims; 190 | 191 | return $this; 192 | } 193 | 194 | /** 195 | * Helper to set the ttl. 196 | * 197 | * @param int|null $ttl 198 | * 199 | * @return $this 200 | */ 201 | public function setTTL($ttl) 202 | { 203 | $this->claimFactory->setTTL($ttl); 204 | 205 | return $this; 206 | } 207 | 208 | /** 209 | * Helper to get the ttl. 210 | * 211 | * @return int|null 212 | */ 213 | public function getTTL() 214 | { 215 | return $this->claimFactory->getTTL(); 216 | } 217 | 218 | /** 219 | * Get the default claims. 220 | * 221 | * @return array 222 | */ 223 | public function getDefaultClaims() 224 | { 225 | return $this->defaultClaims; 226 | } 227 | 228 | /** 229 | * Get the PayloadValidator instance. 230 | * 231 | * @return PayloadValidator 232 | */ 233 | public function validator() 234 | { 235 | return $this->validator; 236 | } 237 | 238 | /** 239 | * Magically add a claim. 240 | * 241 | * @param string $method 242 | * @param array $parameters 243 | * 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 | * (c) 2021 PHP Open Source Saver 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | namespace PHPOpenSourceSaver\JWTAuth\Http\Middleware; 14 | 15 | use Illuminate\Http\Request; 16 | use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException; 17 | 18 | class Authenticate extends BaseMiddleware 19 | { 20 | /** 21 | * Handle an incoming request. 22 | * 23 | * @param Request $request 24 | * 25 | * @throws UnauthorizedHttpException 26 | */ 27 | public function handle($request, \Closure $next) 28 | { 29 | $this->authenticate($request); 30 | 31 | return $next($request); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Http/Middleware/AuthenticateAndRenew.php: -------------------------------------------------------------------------------- 1 | 7 | * (c) 2021 PHP Open Source Saver 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | namespace PHPOpenSourceSaver\JWTAuth\Http\Middleware; 14 | 15 | use Illuminate\Http\Request; 16 | use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException; 17 | 18 | class AuthenticateAndRenew extends BaseMiddleware 19 | { 20 | /** 21 | * Handle an incoming request. 22 | * 23 | * @param Request $request 24 | * 25 | * @throws UnauthorizedHttpException 26 | */ 27 | public function handle($request, \Closure $next) 28 | { 29 | $this->authenticate($request); 30 | 31 | $response = $next($request); 32 | 33 | // Send the refreshed token back to the client. 34 | return $this->setAuthenticationHeader($response); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Http/Middleware/BaseMiddleware.php: -------------------------------------------------------------------------------- 1 | 7 | * (c) 2021 PHP Open Source Saver 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | namespace PHPOpenSourceSaver\JWTAuth\Http\Middleware; 14 | 15 | use Illuminate\Http\JsonResponse; 16 | use Illuminate\Http\Request; 17 | use Illuminate\Http\Response; 18 | use PHPOpenSourceSaver\JWTAuth\Exceptions\JWTException; 19 | use PHPOpenSourceSaver\JWTAuth\JWTAuth; 20 | use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; 21 | use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException; 22 | 23 | abstract class BaseMiddleware 24 | { 25 | /** 26 | * The JWT Authenticator. 27 | */ 28 | protected JWTAuth $auth; 29 | 30 | /** 31 | * Create a new BaseMiddleware instance. 32 | * 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 | * @return void 44 | * 45 | * @throws BadRequestHttpException 46 | */ 47 | public function checkForToken(Request $request) 48 | { 49 | if (!$this->auth->parser()->setRequest($request)->hasToken()) { 50 | throw new UnauthorizedHttpException('jwt-auth', 'Token not provided'); 51 | } 52 | } 53 | 54 | /** 55 | * Attempt to authenticate a user via the token in the request. 56 | * 57 | * @return void 58 | * 59 | * @throws UnauthorizedHttpException 60 | */ 61 | public function authenticate(Request $request) 62 | { 63 | $this->checkForToken($request); 64 | 65 | try { 66 | if (!$this->auth->parseToken()->authenticate()) { 67 | throw new UnauthorizedHttpException('jwt-auth', 'User not found'); 68 | } 69 | } catch (JWTException $e) { 70 | throw new UnauthorizedHttpException('jwt-auth', $e->getMessage(), $e, $e->getCode()); 71 | } 72 | } 73 | 74 | /** 75 | * Set the authentication header. 76 | * 77 | * @param Response|JsonResponse $response 78 | * @param string|null $token 79 | * 80 | * @return Response|JsonResponse 81 | */ 82 | protected function setAuthenticationHeader($response, $token = null) 83 | { 84 | $token = $token ?: $this->auth->refresh(); 85 | $response->headers->set('Authorization', 'Bearer '.$token); 86 | 87 | return $response; 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/Http/Middleware/Check.php: -------------------------------------------------------------------------------- 1 | 7 | * (c) 2021 PHP Open Source Saver 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | namespace PHPOpenSourceSaver\JWTAuth\Http\Middleware; 14 | 15 | use Illuminate\Http\Request; 16 | 17 | class Check extends BaseMiddleware 18 | { 19 | /** 20 | * Handle an incoming request. 21 | * 22 | * @param Request $request 23 | */ 24 | public function handle($request, \Closure $next) 25 | { 26 | if ($this->auth->parser()->setRequest($request)->hasToken()) { 27 | try { 28 | $this->auth->parseToken()->authenticate(); 29 | } catch (\Exception $e) { 30 | } 31 | } 32 | 33 | return $next($request); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Http/Middleware/RefreshToken.php: -------------------------------------------------------------------------------- 1 | 7 | * (c) 2021 PHP Open Source Saver 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | namespace PHPOpenSourceSaver\JWTAuth\Http\Middleware; 14 | 15 | use Illuminate\Http\Request; 16 | use PHPOpenSourceSaver\JWTAuth\Exceptions\JWTException; 17 | use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException; 18 | 19 | class RefreshToken extends BaseMiddleware 20 | { 21 | /** 22 | * Handle an incoming request. 23 | * 24 | * @param Request $request 25 | * 26 | * @throws UnauthorizedHttpException 27 | */ 28 | public function handle($request, \Closure $next) 29 | { 30 | $this->checkForToken($request); 31 | 32 | try { 33 | $token = $this->auth->parseToken()->refresh(); 34 | } catch (JWTException $e) { 35 | throw new UnauthorizedHttpException('jwt-auth', $e->getMessage(), $e, $e->getCode()); 36 | } 37 | 38 | $response = $next($request); 39 | 40 | // Send the refreshed token back to the client. 41 | return $this->setAuthenticationHeader($response, $token); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/Http/Parser/AuthHeaders.php: -------------------------------------------------------------------------------- 1 | 7 | * (c) 2021 PHP Open Source Saver 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | namespace PHPOpenSourceSaver\JWTAuth\Http\Parser; 14 | 15 | use Illuminate\Http\Request; 16 | use PHPOpenSourceSaver\JWTAuth\Contracts\Http\Parser as ParserContract; 17 | 18 | class AuthHeaders implements ParserContract 19 | { 20 | /** 21 | * The header name. 22 | * 23 | * @var string 24 | */ 25 | protected $header = 'authorization'; 26 | 27 | /** 28 | * The header prefix. 29 | * 30 | * @var string 31 | */ 32 | protected $prefix = 'bearer'; 33 | 34 | /** 35 | * Attempt to parse the token from some other possible headers. 36 | * 37 | * @return string|null 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 | * @return string|null 48 | */ 49 | public function parse(Request $request) 50 | { 51 | $header = $request->headers->get($this->header) ?: $this->fromAltHeaders($request); 52 | 53 | if (null !== $header) { 54 | $position = strripos($header, $this->prefix); 55 | 56 | if (false !== $position) { 57 | $header = substr($header, $position + strlen($this->prefix)); 58 | 59 | return trim( 60 | false !== strpos($header, ',') ? strstr($header, ',', true) : $header 61 | ); 62 | } 63 | } 64 | 65 | return null; 66 | } 67 | 68 | /** 69 | * Set the header name. 70 | * 71 | * @param string $headerName 72 | * 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 | * 87 | * @return $this 88 | */ 89 | public function setHeaderPrefix($headerPrefix) 90 | { 91 | $this->prefix = $headerPrefix; 92 | 93 | return $this; 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/Http/Parser/Cookies.php: -------------------------------------------------------------------------------- 1 | 7 | * (c) 2021 PHP Open Source Saver 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | namespace PHPOpenSourceSaver\JWTAuth\Http\Parser; 14 | 15 | use Illuminate\Contracts\Encryption\DecryptException; 16 | use Illuminate\Http\Request; 17 | use Illuminate\Support\Facades\Crypt; 18 | use PHPOpenSourceSaver\JWTAuth\Contracts\Http\Parser as ParserContract; 19 | use PHPOpenSourceSaver\JWTAuth\Exceptions\TokenInvalidException; 20 | 21 | class Cookies implements ParserContract 22 | { 23 | use KeyTrait; 24 | 25 | /** 26 | * Decrypt or not the cookie while parsing. 27 | * 28 | * @var bool 29 | */ 30 | private $decrypt; 31 | 32 | public function __construct($decrypt = true) 33 | { 34 | $this->decrypt = $decrypt; 35 | } 36 | 37 | /** 38 | * Try to parse the token from the request cookies. 39 | * 40 | * @return string|null 41 | * 42 | * @throws TokenInvalidException 43 | */ 44 | public function parse(Request $request) 45 | { 46 | if ($this->decrypt && $request->hasCookie($this->key)) { 47 | try { 48 | return Crypt::decrypt($request->cookie($this->key)); 49 | } catch (DecryptException $ex) { 50 | throw new TokenInvalidException('Token has not decrypted successfully.'); 51 | } 52 | } 53 | 54 | return $request->cookie($this->key); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/Http/Parser/InputSource.php: -------------------------------------------------------------------------------- 1 | 7 | * (c) 2021 PHP Open Source Saver 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | namespace PHPOpenSourceSaver\JWTAuth\Http\Parser; 14 | 15 | use Illuminate\Http\Request; 16 | use PHPOpenSourceSaver\JWTAuth\Contracts\Http\Parser as ParserContract; 17 | 18 | class InputSource implements ParserContract 19 | { 20 | use KeyTrait; 21 | 22 | /** 23 | * Try to parse the token from the request input source. 24 | * 25 | * @return string|null 26 | */ 27 | public function parse(Request $request) 28 | { 29 | return $request->input($this->key); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Http/Parser/KeyTrait.php: -------------------------------------------------------------------------------- 1 | 7 | * (c) 2021 PHP Open Source Saver 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | namespace PHPOpenSourceSaver\JWTAuth\Http\Parser; 14 | 15 | trait KeyTrait 16 | { 17 | /** 18 | * The key. 19 | * 20 | * @var string 21 | */ 22 | protected $key = 'token'; 23 | 24 | /** 25 | * Set the key. 26 | * 27 | * @param string $key 28 | * 29 | * @return $this 30 | */ 31 | public function setKey($key) 32 | { 33 | $this->key = $key; 34 | 35 | return $this; 36 | } 37 | 38 | /** 39 | * Get the key. 40 | * 41 | * @return string 42 | */ 43 | public function getKey() 44 | { 45 | return $this->key; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/Http/Parser/LumenRouteParams.php: -------------------------------------------------------------------------------- 1 | 7 | * (c) 2021 PHP Open Source Saver 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | namespace PHPOpenSourceSaver\JWTAuth\Http\Parser; 14 | 15 | use Illuminate\Http\Request; 16 | use Illuminate\Support\Arr; 17 | 18 | class LumenRouteParams extends RouteParams 19 | { 20 | /** 21 | * Try to get the token from the route parameters. 22 | * 23 | * @return string|null 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 | * (c) 2021 PHP Open Source Saver 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | namespace PHPOpenSourceSaver\JWTAuth\Http\Parser; 14 | 15 | use Illuminate\Http\Request; 16 | 17 | class Parser 18 | { 19 | /** 20 | * The chain. 21 | */ 22 | private array $chain; 23 | 24 | /** 25 | * The request. 26 | */ 27 | protected Request $request; 28 | 29 | /** 30 | * Constructor. 31 | * 32 | * @return void 33 | */ 34 | public function __construct(Request $request, array $chain = []) 35 | { 36 | $this->request = $request; 37 | $this->chain = $chain; 38 | } 39 | 40 | /** 41 | * Get the parser chain. 42 | * 43 | * @return array 44 | */ 45 | public function getChain() 46 | { 47 | return $this->chain; 48 | } 49 | 50 | /** 51 | * Add a new parser to the chain. 52 | * 53 | * @param array|\PHPOpenSourceSaver\JWTAuth\Contracts\Http\Parser $parsers 54 | * 55 | * @return $this 56 | */ 57 | public function addParser($parsers) 58 | { 59 | $this->chain = array_merge($this->chain, is_array($parsers) ? $parsers : [$parsers]); 60 | 61 | return $this; 62 | } 63 | 64 | /** 65 | * Set the order of the parser chain. 66 | * 67 | * @return $this 68 | */ 69 | public function setChain(array $chain) 70 | { 71 | $this->chain = $chain; 72 | 73 | return $this; 74 | } 75 | 76 | /** 77 | * Alias for setting the order of the chain. 78 | * 79 | * @return $this 80 | */ 81 | public function setChainOrder(array $chain) 82 | { 83 | return $this->setChain($chain); 84 | } 85 | 86 | /** 87 | * Iterate through the parsers and attempt to retrieve 88 | * a value, otherwise return null. 89 | * 90 | * @return string|null 91 | */ 92 | public function parseToken() 93 | { 94 | foreach ($this->chain as $parser) { 95 | if ($response = $parser->parse($this->request)) { 96 | return $response; 97 | } 98 | } 99 | } 100 | 101 | /** 102 | * Check whether a token exists in the chain. 103 | * 104 | * @return bool 105 | */ 106 | public function hasToken() 107 | { 108 | return null !== $this->parseToken(); 109 | } 110 | 111 | /** 112 | * Set the request instance. 113 | * 114 | * @return $this 115 | */ 116 | public function setRequest(Request $request) 117 | { 118 | $this->request = $request; 119 | 120 | return $this; 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/Http/Parser/QueryString.php: -------------------------------------------------------------------------------- 1 | 7 | * (c) 2021 PHP Open Source Saver 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | namespace PHPOpenSourceSaver\JWTAuth\Http\Parser; 14 | 15 | use Illuminate\Http\Request; 16 | use PHPOpenSourceSaver\JWTAuth\Contracts\Http\Parser as ParserContract; 17 | 18 | class QueryString implements ParserContract 19 | { 20 | use KeyTrait; 21 | 22 | /** 23 | * Try to parse the token from the request query string. 24 | * 25 | * @return string|null 26 | */ 27 | public function parse(Request $request) 28 | { 29 | return $request->query($this->key); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Http/Parser/RouteParams.php: -------------------------------------------------------------------------------- 1 | 7 | * (c) 2021 PHP Open Source Saver 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | namespace PHPOpenSourceSaver\JWTAuth\Http\Parser; 14 | 15 | use Illuminate\Http\Request; 16 | use PHPOpenSourceSaver\JWTAuth\Contracts\Http\Parser as ParserContract; 17 | 18 | class RouteParams implements ParserContract 19 | { 20 | use KeyTrait; 21 | 22 | /** 23 | * Try to get the token from the route parameters. 24 | * 25 | * @return string|null 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 | * (c) 2021 PHP Open Source Saver 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | namespace PHPOpenSourceSaver\JWTAuth; 14 | 15 | use Illuminate\Http\Request; 16 | use PHPOpenSourceSaver\JWTAuth\Contracts\JWTSubject; 17 | use PHPOpenSourceSaver\JWTAuth\Exceptions\JWTException; 18 | use PHPOpenSourceSaver\JWTAuth\Http\Parser\Parser; 19 | use PHPOpenSourceSaver\JWTAuth\Support\CustomClaims; 20 | 21 | class JWT 22 | { 23 | use CustomClaims; 24 | 25 | /** 26 | * The authentication manager. 27 | * 28 | * @var Manager 29 | */ 30 | protected $manager; 31 | 32 | /** 33 | * The HTTP parser. 34 | * 35 | * @var Parser 36 | */ 37 | protected $parser; 38 | 39 | /** 40 | * The token. 41 | * 42 | * @var 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 | * @return void 57 | */ 58 | public function __construct(Manager $manager, Parser $parser) 59 | { 60 | $this->manager = $manager; 61 | $this->parser = $parser; 62 | } 63 | 64 | /** 65 | * Generate a token for a given subject. 66 | * 67 | * @return string 68 | */ 69 | public function fromSubject(JWTSubject $subject) 70 | { 71 | $payload = $this->makePayload($subject); 72 | 73 | return $this->manager->encode($payload)->get(); 74 | } 75 | 76 | /** 77 | * Alias to generate a token for a given user. 78 | * 79 | * @return string 80 | */ 81 | public function fromUser(JWTSubject $user) 82 | { 83 | return $this->fromSubject($user); 84 | } 85 | 86 | /** 87 | * Refresh an expired token. 88 | * 89 | * @param bool $forceForever 90 | * @param bool $resetClaims 91 | * 92 | * @return string 93 | */ 94 | public function refresh($forceForever = false, $resetClaims = false) 95 | { 96 | $this->requireToken(); 97 | 98 | return $this->manager->customClaims($this->getCustomClaims()) 99 | ->refresh($this->token, $forceForever, $resetClaims) 100 | ->get(); 101 | } 102 | 103 | /** 104 | * Invalidate a token (add it to the blacklist). 105 | * 106 | * @param bool $forceForever 107 | * 108 | * @return $this 109 | */ 110 | public function invalidate($forceForever = false) 111 | { 112 | $this->requireToken(); 113 | 114 | $this->manager->invalidate($this->token, $forceForever); 115 | 116 | return $this; 117 | } 118 | 119 | /** 120 | * Alias to get the payload, and as a result checks that 121 | * the token is valid i.e. not expired or blacklisted. 122 | * 123 | * @return Payload 124 | * 125 | * @throws JWTException 126 | */ 127 | public function checkOrFail() 128 | { 129 | return $this->getPayload(); 130 | } 131 | 132 | /** 133 | * Check that the token is valid. 134 | * 135 | * @param bool $getPayload 136 | * 137 | * @return Payload|bool 138 | */ 139 | public function check($getPayload = false) 140 | { 141 | try { 142 | $payload = $this->checkOrFail(); 143 | } catch (JWTException $e) { 144 | return false; 145 | } 146 | 147 | return $getPayload ? $payload : true; 148 | } 149 | 150 | /** 151 | * Get the token. 152 | * 153 | * @return Token|null 154 | */ 155 | public function getToken() 156 | { 157 | if (null === $this->token) { 158 | try { 159 | $this->parseToken(); 160 | } catch (JWTException $e) { 161 | $this->token = null; 162 | } 163 | } 164 | 165 | return $this->token; 166 | } 167 | 168 | /** 169 | * Parse the token from the request. 170 | * 171 | * @return $this 172 | * 173 | * @throws JWTException 174 | */ 175 | public function parseToken() 176 | { 177 | if (!$token = $this->parser->parseToken()) { 178 | throw new JWTException('The token could not be parsed from the request'); 179 | } 180 | 181 | return $this->setToken($token); 182 | } 183 | 184 | /** 185 | * Get the raw Payload instance. 186 | * 187 | * @return Payload 188 | */ 189 | public function getPayload() 190 | { 191 | $this->requireToken(); 192 | 193 | return $this->manager->decode($this->token); 194 | } 195 | 196 | /** 197 | * Alias for getPayload(). 198 | * 199 | * @return Payload 200 | */ 201 | public function payload() 202 | { 203 | return $this->getPayload(); 204 | } 205 | 206 | /** 207 | * Convenience method to get a claim value. 208 | * 209 | * @param string $claim 210 | */ 211 | public function getClaim($claim) 212 | { 213 | return $this->payload()->get($claim); 214 | } 215 | 216 | /** 217 | * Create a Payload instance. 218 | * 219 | * @return Payload 220 | */ 221 | public function makePayload(JWTSubject $subject) 222 | { 223 | return $this->factory()->customClaims($this->getClaimsArray($subject))->make(); 224 | } 225 | 226 | /** 227 | * Build the claims array and return it. 228 | * 229 | * @return array 230 | */ 231 | protected function getClaimsArray(JWTSubject $subject) 232 | { 233 | return array_merge( 234 | $this->getClaimsForSubject($subject), 235 | $subject->getJWTCustomClaims(), // custom claims from JWTSubject method 236 | $this->customClaims // custom claims from inline setter 237 | ); 238 | } 239 | 240 | /** 241 | * Get the claims associated with a given subject. 242 | * 243 | * @return array 244 | */ 245 | protected function getClaimsForSubject(JWTSubject $subject) 246 | { 247 | return array_merge([ 248 | 'sub' => $subject->getJWTIdentifier(), 249 | ], $this->lockSubject ? ['prv' => $this->hashSubjectModel($subject)] : []); 250 | } 251 | 252 | /** 253 | * Hash the subject model and return it. 254 | * 255 | * @param string|object $model 256 | * 257 | * @return string 258 | */ 259 | protected function hashSubjectModel($model) 260 | { 261 | return sha1(is_object($model) ? get_class($model) : $model); 262 | } 263 | 264 | /** 265 | * Check if the subject model matches the one saved in the token. 266 | * 267 | * @param string|object $model 268 | * 269 | * @return bool 270 | */ 271 | public function checkSubjectModel($model) 272 | { 273 | if (($prv = $this->payload()->get('prv')) === null) { 274 | return true; 275 | } 276 | 277 | return $this->hashSubjectModel($model) === $prv; 278 | } 279 | 280 | /** 281 | * Set the token. 282 | * 283 | * @param Token|string $token 284 | * 285 | * @return $this 286 | */ 287 | public function setToken($token) 288 | { 289 | $this->token = $token instanceof Token ? $token : new Token($token); 290 | 291 | return $this; 292 | } 293 | 294 | /** 295 | * Unset the current token. 296 | * 297 | * @return $this 298 | */ 299 | public function unsetToken() 300 | { 301 | $this->token = null; 302 | 303 | return $this; 304 | } 305 | 306 | /** 307 | * Ensure that a token is available. 308 | * 309 | * @return void 310 | * 311 | * @throws JWTException 312 | */ 313 | protected function requireToken() 314 | { 315 | if (!$this->token) { 316 | throw new JWTException('A token is required'); 317 | } 318 | } 319 | 320 | /** 321 | * Set the request instance. 322 | * 323 | * @return $this 324 | */ 325 | public function setRequest(Request $request) 326 | { 327 | $this->parser->setRequest($request); 328 | 329 | return $this; 330 | } 331 | 332 | /** 333 | * Set whether the subject should be "locked". 334 | * 335 | * @param bool $lock 336 | * 337 | * @return $this 338 | */ 339 | public function lockSubject($lock) 340 | { 341 | $this->lockSubject = $lock; 342 | 343 | return $this; 344 | } 345 | 346 | /** 347 | * Get the Manager instance. 348 | * 349 | * @return Manager 350 | */ 351 | public function manager() 352 | { 353 | return $this->manager; 354 | } 355 | 356 | /** 357 | * Get the Parser instance. 358 | * 359 | * @return Parser 360 | */ 361 | public function parser() 362 | { 363 | return $this->parser; 364 | } 365 | 366 | /** 367 | * Get the Payload Factory. 368 | * 369 | * @return Factory 370 | */ 371 | public function factory() 372 | { 373 | return $this->manager->getPayloadFactory(); 374 | } 375 | 376 | /** 377 | * Get the Blacklist. 378 | * 379 | * @return Blacklist 380 | */ 381 | public function blacklist() 382 | { 383 | return $this->manager->getBlacklist(); 384 | } 385 | 386 | /** 387 | * Magically call the JWT Manager. 388 | * 389 | * @param string $method 390 | * @param array $parameters 391 | * 392 | * @throws \BadMethodCallException 393 | */ 394 | public function __call($method, $parameters) 395 | { 396 | if (method_exists($this->manager, $method)) { 397 | return call_user_func_array([$this->manager, $method], $parameters); 398 | } 399 | 400 | throw new \BadMethodCallException("Method [$method] does not exist."); 401 | } 402 | } 403 | -------------------------------------------------------------------------------- /src/JWTAuth.php: -------------------------------------------------------------------------------- 1 | 7 | * (c) 2021 PHP Open Source Saver 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | namespace PHPOpenSourceSaver\JWTAuth; 14 | 15 | use PHPOpenSourceSaver\JWTAuth\Contracts\JWTSubject; 16 | use PHPOpenSourceSaver\JWTAuth\Contracts\Providers\Auth; 17 | use PHPOpenSourceSaver\JWTAuth\Http\Parser\Parser; 18 | 19 | class JWTAuth extends JWT 20 | { 21 | /** 22 | * The authentication provider. 23 | * 24 | * @var Auth 25 | */ 26 | protected $auth; 27 | 28 | /** 29 | * Constructor. 30 | * 31 | * @return void 32 | */ 33 | public function __construct(Manager $manager, Auth $auth, Parser $parser) 34 | { 35 | parent::__construct($manager, $parser); 36 | $this->auth = $auth; 37 | } 38 | 39 | /** 40 | * Attempt to authenticate the user and return the token. 41 | * 42 | * @return false|string 43 | */ 44 | public function attempt(array $credentials) 45 | { 46 | if (!$this->auth->byCredentials($credentials)) { 47 | return false; 48 | } 49 | 50 | return $this->fromUser($this->user()); 51 | } 52 | 53 | /** 54 | * Authenticate a user via a token. 55 | * 56 | * @return JWTSubject|false 57 | */ 58 | public function authenticate() 59 | { 60 | $id = $this->getPayload()->get('sub') ?: $this->getPayload()->get('id'); 61 | 62 | if (!$this->auth->byId($id)) { 63 | return false; 64 | } 65 | 66 | return $this->user(); 67 | } 68 | 69 | /** 70 | * Alias for authenticate(). 71 | * 72 | * @return JWTSubject|false 73 | */ 74 | public function toUser() 75 | { 76 | return $this->authenticate(); 77 | } 78 | 79 | /** 80 | * Get the authenticated user. 81 | * 82 | * @return JWTSubject 83 | */ 84 | public function user() 85 | { 86 | return $this->auth->user(); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/JWTGuard.php: -------------------------------------------------------------------------------- 1 | 7 | * (c) 2021 PHP Open Source Saver 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | namespace PHPOpenSourceSaver\JWTAuth; 14 | 15 | use Illuminate\Auth\Events\Attempting; 16 | use Illuminate\Auth\Events\Authenticated; 17 | use Illuminate\Auth\Events\Failed; 18 | use Illuminate\Auth\Events\Login; 19 | use Illuminate\Auth\Events\Logout; 20 | use Illuminate\Auth\GuardHelpers; 21 | use Illuminate\Contracts\Auth\Authenticatable; 22 | use Illuminate\Contracts\Auth\Guard; 23 | use Illuminate\Contracts\Auth\UserProvider; 24 | use Illuminate\Contracts\Events\Dispatcher; 25 | use Illuminate\Http\Request; 26 | use Illuminate\Support\Traits\Macroable; 27 | use PHPOpenSourceSaver\JWTAuth\Contracts\JWTSubject; 28 | use PHPOpenSourceSaver\JWTAuth\Exceptions\JWTException; 29 | use PHPOpenSourceSaver\JWTAuth\Exceptions\UserNotDefinedException; 30 | 31 | /** 32 | * @mixin JWT 33 | */ 34 | class JWTGuard implements Guard 35 | { 36 | use GuardHelpers { 37 | setUser as guardHelperSetUser; 38 | } 39 | use Macroable { 40 | __call as macroCall; 41 | } 42 | 43 | /** 44 | * The user we last attempted to retrieve. 45 | * 46 | * @var Authenticatable 47 | */ 48 | protected $lastAttempted; 49 | 50 | /** 51 | * The JWT instance. 52 | * 53 | * @var JWT 54 | */ 55 | protected $jwt; 56 | 57 | /** 58 | * The request instance. 59 | * 60 | * @var Request 61 | */ 62 | protected $request; 63 | 64 | /** 65 | * The event dispatcher instance. 66 | * 67 | * @var Dispatcher 68 | */ 69 | protected $events; 70 | 71 | /** 72 | * The name of the Guard. 73 | * 74 | * @var string 75 | */ 76 | protected $name = 'tymon.jwt'; 77 | 78 | /** 79 | * Instantiate the class. 80 | * 81 | * @return void 82 | */ 83 | public function __construct(JWT $jwt, UserProvider $provider, Request $request, Dispatcher $eventDispatcher) 84 | { 85 | $this->jwt = $jwt; 86 | $this->provider = $provider; 87 | $this->request = $request; 88 | $this->events = $eventDispatcher; 89 | } 90 | 91 | /** 92 | * Get the currently authenticated user. 93 | * 94 | * @return Authenticatable|null 95 | */ 96 | public function user() 97 | { 98 | if (null !== $this->user) { 99 | return $this->user; 100 | } 101 | 102 | if ( 103 | $this->jwt->setRequest($this->request)->getToken() 104 | && ($payload = $this->jwt->check(true)) 105 | && $this->validateSubject() 106 | ) { 107 | return $this->user = $this->provider->retrieveById($payload['sub']); 108 | } 109 | } 110 | 111 | /** 112 | * @return int|string|null 113 | */ 114 | public function getUserId() 115 | { 116 | if (null !== $this->user) { 117 | return $this->user->getAuthIdentifier(); 118 | } 119 | 120 | if ( 121 | $this->jwt->setRequest($this->request)->getToken() 122 | && ($payload = $this->jwt->check(true)) 123 | && $this->validateSubject() 124 | ) { 125 | return $payload['sub']; 126 | } 127 | 128 | return null; 129 | } 130 | 131 | /** 132 | * Get the ID for the currently authenticated user. 133 | * 134 | * @return int|string|null 135 | */ 136 | public function id() 137 | { 138 | return $this->getUserId(); 139 | } 140 | 141 | /** 142 | * Get the currently authenticated user or throws an exception. 143 | * 144 | * @return Authenticatable 145 | * 146 | * @throws UserNotDefinedException 147 | */ 148 | public function userOrFail() 149 | { 150 | if (!$user = $this->user()) { 151 | throw new UserNotDefinedException(); 152 | } 153 | 154 | return $user; 155 | } 156 | 157 | /** 158 | * Validate a user's credentials. 159 | * 160 | * @return bool 161 | */ 162 | public function validate(array $credentials = []) 163 | { 164 | return (bool) $this->attempt($credentials, false); 165 | } 166 | 167 | /** 168 | * Attempt to authenticate the user using the given credentials and return the token. 169 | * 170 | * @param bool $login 171 | * 172 | * @return bool|string 173 | */ 174 | public function attempt(array $credentials = [], $login = true) 175 | { 176 | $this->lastAttempted = $user = $this->provider->retrieveByCredentials($credentials); 177 | 178 | $this->fireAttemptEvent($credentials); 179 | 180 | if ($this->hasValidCredentials($user, $credentials)) { 181 | return $login ? $this->login($user) : true; 182 | } 183 | 184 | $this->fireFailedEvent($user, $credentials); 185 | 186 | return false; 187 | } 188 | 189 | /** 190 | * Create a token for a user. 191 | * 192 | * @return string 193 | */ 194 | public function login(JWTSubject $user) 195 | { 196 | $token = $this->jwt->fromUser($user); 197 | $this->setToken($token)->setUser($user); 198 | 199 | $this->fireLoginEvent($user); 200 | 201 | return $token; 202 | } 203 | 204 | /** 205 | * Logout the user, thus invalidating the token. 206 | * 207 | * @param bool $forceForever 208 | * 209 | * @return void 210 | */ 211 | public function logout($forceForever = false) 212 | { 213 | try { 214 | $this->requireToken()->invalidate($forceForever); 215 | } catch (JWTException $e) { 216 | // Proceed with the logout as normal if we can't invalidate the token 217 | } 218 | 219 | $this->fireLogoutEvent($this->user); 220 | 221 | $this->user = null; 222 | $this->jwt->unsetToken(); 223 | } 224 | 225 | /** 226 | * Refresh the token. 227 | * 228 | * @param bool $forceForever 229 | * @param bool $resetClaims 230 | * 231 | * @return string 232 | */ 233 | public function refresh($forceForever = false, $resetClaims = false) 234 | { 235 | return $this->requireToken()->refresh($forceForever, $resetClaims); 236 | } 237 | 238 | /** 239 | * Invalidate the token. 240 | * 241 | * @param bool $forceForever 242 | * 243 | * @return JWT 244 | */ 245 | public function invalidate($forceForever = false) 246 | { 247 | return $this->requireToken()->invalidate($forceForever); 248 | } 249 | 250 | /** 251 | * Create a new token by User id. 252 | * 253 | * @return string|null 254 | */ 255 | public function tokenById($id) 256 | { 257 | if ($user = $this->provider->retrieveById($id)) { 258 | return $this->jwt->fromUser($user); 259 | } 260 | } 261 | 262 | /** 263 | * Log a user into the application using their credentials. 264 | * 265 | * @return bool 266 | */ 267 | public function once(array $credentials = []) 268 | { 269 | if ($this->validate($credentials)) { 270 | $this->setUser($this->lastAttempted); 271 | 272 | return true; 273 | } 274 | 275 | return false; 276 | } 277 | 278 | /** 279 | * Log the given User into the application. 280 | * 281 | * @return bool 282 | */ 283 | public function onceUsingId($id) 284 | { 285 | if ($user = $this->provider->retrieveById($id)) { 286 | $this->setUser($user); 287 | 288 | return true; 289 | } 290 | 291 | return false; 292 | } 293 | 294 | /** 295 | * Alias for onceUsingId. 296 | * 297 | * @return bool 298 | */ 299 | public function byId($id) 300 | { 301 | return $this->onceUsingId($id); 302 | } 303 | 304 | /** 305 | * Add any custom claims. 306 | * 307 | * @return $this 308 | */ 309 | public function claims(array $claims) 310 | { 311 | $this->jwt->claims($claims); 312 | 313 | return $this; 314 | } 315 | 316 | /** 317 | * Get the raw Payload instance. 318 | * 319 | * @return Payload 320 | */ 321 | public function getPayload() 322 | { 323 | return $this->requireToken()->getPayload(); 324 | } 325 | 326 | /** 327 | * Alias for getPayload(). 328 | * 329 | * @return Payload 330 | */ 331 | public function payload() 332 | { 333 | return $this->getPayload(); 334 | } 335 | 336 | /** 337 | * Set the token. 338 | * 339 | * @param Token|string $token 340 | * 341 | * @return $this 342 | */ 343 | public function setToken($token) 344 | { 345 | $this->jwt->setToken($token); 346 | 347 | return $this; 348 | } 349 | 350 | /** 351 | * Get the token ttl. 352 | * 353 | * @return int|null 354 | */ 355 | public function getTTL() 356 | { 357 | return $this->jwt->factory()->getTTL(); 358 | } 359 | 360 | /** 361 | * Set the token ttl. 362 | * 363 | * @param int|null $ttl 364 | * 365 | * @return $this 366 | */ 367 | public function setTTL($ttl) 368 | { 369 | $this->jwt->factory()->setTTL($ttl); 370 | 371 | return $this; 372 | } 373 | 374 | /** 375 | * Get the user provider used by the guard. 376 | * 377 | * @return UserProvider 378 | */ 379 | public function getProvider() 380 | { 381 | return $this->provider; 382 | } 383 | 384 | /** 385 | * Set the user provider used by the guard. 386 | * 387 | * @return $this 388 | */ 389 | public function setProvider(UserProvider $provider) 390 | { 391 | $this->provider = $provider; 392 | 393 | return $this; 394 | } 395 | 396 | /** 397 | * Return the currently cached user. 398 | * 399 | * @return Authenticatable|null 400 | */ 401 | public function getUser() 402 | { 403 | return $this->user; 404 | } 405 | 406 | /** 407 | * Set the current user. 408 | * 409 | * @return $this 410 | */ 411 | public function setUser(Authenticatable $user) 412 | { 413 | $result = $this->guardHelperSetUser($user); 414 | 415 | $this->fireAuthenticatedEvent($user); 416 | 417 | return $result; 418 | } 419 | 420 | /** 421 | * Get the current request instance. 422 | * 423 | * @return Request 424 | */ 425 | public function getRequest() 426 | { 427 | return $this->request ?: Request::createFromGlobals(); 428 | } 429 | 430 | /** 431 | * Set the current request instance. 432 | * 433 | * @return $this 434 | */ 435 | public function setRequest(Request $request) 436 | { 437 | $this->request = $request; 438 | 439 | return $this; 440 | } 441 | 442 | /** 443 | * Get the last user we attempted to authenticate. 444 | * 445 | * @return Authenticatable 446 | */ 447 | public function getLastAttempted() 448 | { 449 | return $this->lastAttempted; 450 | } 451 | 452 | /** 453 | * Determine if the user matches the credentials. 454 | * 455 | * @param array $credentials 456 | * 457 | * @return bool 458 | */ 459 | protected function hasValidCredentials($user, $credentials) 460 | { 461 | $validated = null !== $user && $this->provider->validateCredentials($user, $credentials); 462 | 463 | if ($validated) { 464 | $this->fireValidatedEvent($user); 465 | } 466 | 467 | return $validated; 468 | } 469 | 470 | /** 471 | * Ensure the JWTSubject matches what is in the token. 472 | * 473 | * @return bool 474 | */ 475 | protected function validateSubject() 476 | { 477 | // If the provider doesn't have the necessary method 478 | // to get the underlying model name then allow. 479 | if (!method_exists($this->provider, 'getModel')) { 480 | return true; 481 | } 482 | 483 | return $this->jwt->checkSubjectModel($this->provider->getModel()); 484 | } 485 | 486 | /** 487 | * Ensure that a token is available in the request. 488 | * 489 | * @return JWT 490 | * 491 | * @throws JWTException 492 | */ 493 | protected function requireToken() 494 | { 495 | if (!$this->jwt->setRequest($this->getRequest())->getToken()) { 496 | throw new JWTException('Token could not be parsed from the request.'); 497 | } 498 | 499 | return $this->jwt; 500 | } 501 | 502 | /** 503 | * Fire the attempt event. 504 | * 505 | * @return void 506 | */ 507 | protected function fireAttemptEvent(array $credentials) 508 | { 509 | $this->events->dispatch(new Attempting( 510 | $this->name, 511 | $credentials, 512 | false 513 | )); 514 | } 515 | 516 | /** 517 | * Fires the validated event. 518 | * 519 | * @param Authenticatable $user 520 | * 521 | * @return void 522 | */ 523 | protected function fireValidatedEvent($user) 524 | { 525 | if (class_exists('Illuminate\Auth\Events\Validated')) { 526 | $this->events->dispatch( 527 | new \Illuminate\Auth\Events\Validated( 528 | $this->name, 529 | $user 530 | ) 531 | ); 532 | } 533 | } 534 | 535 | /** 536 | * Fire the failed authentication attempt event. 537 | * 538 | * @param Authenticatable|null $user 539 | * 540 | * @return void 541 | */ 542 | protected function fireFailedEvent($user, array $credentials) 543 | { 544 | $this->events->dispatch(new Failed( 545 | $this->name, 546 | $user, 547 | $credentials 548 | )); 549 | } 550 | 551 | /** 552 | * Fire the authenticated event. 553 | * 554 | * @param Authenticatable $user 555 | * 556 | * @return void 557 | */ 558 | protected function fireAuthenticatedEvent($user) 559 | { 560 | $this->events->dispatch(new Authenticated( 561 | $this->name, 562 | $user 563 | )); 564 | } 565 | 566 | /** 567 | * Fire the login event. 568 | * 569 | * @param Authenticatable $user 570 | * @param bool $remember 571 | * 572 | * @return void 573 | */ 574 | protected function fireLoginEvent($user, $remember = false) 575 | { 576 | $this->events->dispatch(new Login( 577 | $this->name, 578 | $user, 579 | $remember 580 | )); 581 | } 582 | 583 | /** 584 | * Fire the logout event. 585 | * 586 | * @param Authenticatable $user 587 | * @param bool $remember 588 | * 589 | * @return void 590 | */ 591 | protected function fireLogoutEvent($user, $remember = false) 592 | { 593 | $this->events->dispatch(new Logout( 594 | $this->name, 595 | $user 596 | )); 597 | } 598 | 599 | /** 600 | * Magically call the JWT instance. 601 | * 602 | * @param string $method 603 | * @param array $parameters 604 | * 605 | * @throws \BadMethodCallException 606 | */ 607 | public function __call($method, $parameters) 608 | { 609 | if (method_exists($this->jwt, $method)) { 610 | return call_user_func_array([$this->jwt, $method], $parameters); 611 | } 612 | 613 | if (static::hasMacro($method)) { 614 | return $this->macroCall($method, $parameters); 615 | } 616 | 617 | throw new \BadMethodCallException("Method [$method] does not exist."); 618 | } 619 | } 620 | -------------------------------------------------------------------------------- /src/Manager.php: -------------------------------------------------------------------------------- 1 | 7 | * (c) 2021 PHP Open Source Saver 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | namespace PHPOpenSourceSaver\JWTAuth; 14 | 15 | use PHPOpenSourceSaver\JWTAuth\Contracts\Providers\JWT as JWTContract; 16 | use PHPOpenSourceSaver\JWTAuth\Exceptions\JWTException; 17 | use PHPOpenSourceSaver\JWTAuth\Exceptions\TokenBlacklistedException; 18 | use PHPOpenSourceSaver\JWTAuth\Support\CustomClaims; 19 | use PHPOpenSourceSaver\JWTAuth\Support\RefreshFlow; 20 | use PHPOpenSourceSaver\JWTAuth\Support\Utils; 21 | 22 | class Manager 23 | { 24 | use CustomClaims; 25 | use RefreshFlow; 26 | 27 | /** 28 | * The provider. 29 | * 30 | * @var JWTContract 31 | */ 32 | protected $provider; 33 | 34 | /** 35 | * The blacklist. 36 | * 37 | * @var Blacklist 38 | */ 39 | protected $blacklist; 40 | 41 | /** 42 | * the payload factory. 43 | * 44 | * @var Factory 45 | */ 46 | protected $payloadFactory; 47 | 48 | /** 49 | * The blacklist flag. 50 | * 51 | * @var bool 52 | */ 53 | protected $blacklistEnabled = true; 54 | 55 | /** 56 | * the persistent claims. 57 | * 58 | * @var array 59 | */ 60 | protected $persistentClaims = []; 61 | 62 | /** 63 | * @var bool 64 | */ 65 | protected $showBlackListException = true; 66 | 67 | /** 68 | * Constructor. 69 | * 70 | * @return void 71 | */ 72 | public function __construct(JWTContract $provider, Blacklist $blacklist, Factory $payloadFactory) 73 | { 74 | $this->provider = $provider; 75 | $this->blacklist = $blacklist; 76 | $this->payloadFactory = $payloadFactory; 77 | } 78 | 79 | /** 80 | * Encode a Payload and return the Token. 81 | * 82 | * @return Token 83 | */ 84 | public function encode(Payload $payload) 85 | { 86 | $token = $this->provider->encode($payload->get()); 87 | 88 | return new Token($token); 89 | } 90 | 91 | /** 92 | * Decode a Token and return the Payload. 93 | * 94 | * @param bool $checkBlacklist 95 | * 96 | * @return Payload 97 | * 98 | * @throws TokenBlacklistedException 99 | */ 100 | public function decode(Token $token, $checkBlacklist = true) 101 | { 102 | $payloadArray = $this->provider->decode($token->get()); 103 | 104 | $payload = $this->payloadFactory 105 | ->setRefreshFlow($this->refreshFlow) 106 | ->customClaims($payloadArray) 107 | ->make(); 108 | 109 | if ( 110 | $checkBlacklist 111 | && $this->blacklistEnabled 112 | && $this->getBlackListExceptionEnabled() 113 | && $this->blacklist->has($payload) 114 | ) { 115 | throw new TokenBlacklistedException('The token has been blacklisted'); 116 | } 117 | 118 | return $payload; 119 | } 120 | 121 | /** 122 | * Refresh a Token and return a new Token. 123 | * 124 | * @param bool $forceForever 125 | * @param bool $resetClaims 126 | * 127 | * @return Token 128 | */ 129 | public function refresh(Token $token, $forceForever = false, $resetClaims = false) 130 | { 131 | $this->setRefreshFlow(); 132 | 133 | $claims = $this->buildRefreshClaims($this->decode($token)); 134 | 135 | if ($this->blacklistEnabled) { 136 | // Invalidate old token 137 | $this->invalidate($token, $forceForever); 138 | } 139 | 140 | // Return the new token 141 | return $this->encode( 142 | $this->payloadFactory->customClaims($claims)->make($resetClaims) 143 | ); 144 | } 145 | 146 | /** 147 | * Invalidate a Token by adding it to the blacklist. 148 | * 149 | * @param bool $forceForever 150 | * 151 | * @return bool 152 | * 153 | * @throws JWTException 154 | */ 155 | public function invalidate(Token $token, $forceForever = false) 156 | { 157 | if (!$this->blacklistEnabled) { 158 | throw new JWTException('You must have the blacklist enabled to invalidate a token.'); 159 | } 160 | 161 | return call_user_func( 162 | [$this->blacklist, $forceForever ? 'addForever' : 'add'], 163 | $this->decode($token, false) 164 | ); 165 | } 166 | 167 | /** 168 | * Build the claims to go into the refreshed token. 169 | * 170 | * @return array 171 | */ 172 | protected function buildRefreshClaims(Payload $payload) 173 | { 174 | // Get the claims to be persisted from the payload 175 | $persistentClaims = collect($payload->toArray()) 176 | ->only($this->persistentClaims) 177 | ->toArray(); 178 | 179 | // persist the relevant claims 180 | return array_merge( 181 | $this->customClaims, 182 | $persistentClaims, 183 | [ 184 | 'sub' => $payload['sub'], 185 | 'iat' => Utils::now()->timestamp, 186 | ] 187 | ); 188 | } 189 | 190 | /** 191 | * Get the Payload Factory instance. 192 | * 193 | * @return Factory 194 | */ 195 | public function getPayloadFactory() 196 | { 197 | return $this->payloadFactory; 198 | } 199 | 200 | /** 201 | * Get the JWTProvider instance. 202 | * 203 | * @return JWTContract 204 | */ 205 | public function getJWTProvider() 206 | { 207 | return $this->provider; 208 | } 209 | 210 | /** 211 | * Get the Blacklist instance. 212 | * 213 | * @return Blacklist 214 | */ 215 | public function getBlacklist() 216 | { 217 | return $this->blacklist; 218 | } 219 | 220 | /** 221 | * Set whether the blacklist is enabled. 222 | * 223 | * @param bool $enabled 224 | * 225 | * @return $this 226 | */ 227 | public function setBlacklistEnabled($enabled) 228 | { 229 | $this->blacklistEnabled = $enabled; 230 | 231 | return $this; 232 | } 233 | 234 | /** 235 | * Configuration to set up if show the TokenBlacklistedException 236 | * can be throwable or not. 237 | * 238 | * @param bool $showBlackListException 239 | * 240 | * @removed this 241 | */ 242 | public function setBlackListExceptionEnabled($showBlackListException = true) 243 | { 244 | $this->showBlackListException = $showBlackListException; 245 | 246 | return $this; 247 | } 248 | 249 | /** 250 | * Get if the blacklist instance is enabled. 251 | * 252 | * @return bool 253 | */ 254 | public function getBlackListExceptionEnabled() 255 | { 256 | return $this->showBlackListException; 257 | } 258 | 259 | /** 260 | * Set the claims to be persisted when refreshing a token. 261 | * 262 | * @return $this 263 | */ 264 | public function setPersistentClaims(array $claims) 265 | { 266 | $this->persistentClaims = $claims; 267 | 268 | return $this; 269 | } 270 | } 271 | -------------------------------------------------------------------------------- /src/Payload.php: -------------------------------------------------------------------------------- 1 | 7 | * (c) 2021 PHP Open Source Saver 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | namespace PHPOpenSourceSaver\JWTAuth; 14 | 15 | use Illuminate\Contracts\Support\Arrayable; 16 | use Illuminate\Contracts\Support\Jsonable; 17 | use Illuminate\Support\Arr; 18 | use Illuminate\Support\Str; 19 | use PHPOpenSourceSaver\JWTAuth\Claims\Claim; 20 | use PHPOpenSourceSaver\JWTAuth\Claims\Collection; 21 | use PHPOpenSourceSaver\JWTAuth\Exceptions\PayloadException; 22 | use PHPOpenSourceSaver\JWTAuth\Validators\PayloadValidator; 23 | 24 | class Payload implements \ArrayAccess, Arrayable, \Countable, Jsonable, \JsonSerializable 25 | { 26 | /** 27 | * The collection of claims. 28 | * 29 | * @var Collection 30 | */ 31 | private $claims; 32 | 33 | /** 34 | * Build the Payload. 35 | * 36 | * @param bool $refreshFlow 37 | * 38 | * @return void 39 | */ 40 | public function __construct(Collection $claims, PayloadValidator $validator, $refreshFlow = false) 41 | { 42 | $this->claims = $validator->setRefreshFlow($refreshFlow)->check($claims); 43 | } 44 | 45 | /** 46 | * Get the array of claim instances. 47 | * 48 | * @return Collection 49 | */ 50 | public function getClaims() 51 | { 52 | return $this->claims; 53 | } 54 | 55 | /** 56 | * Checks if a payload matches some expected values. 57 | * 58 | * @param bool $strict 59 | * 60 | * @return bool 61 | */ 62 | public function matches(array $values, $strict = false) 63 | { 64 | if (empty($values)) { 65 | return false; 66 | } 67 | 68 | $claims = $this->getClaims(); 69 | 70 | foreach ($values as $key => $value) { 71 | if (!$claims->has($key) || !$claims->get($key)->matches($value, $strict)) { 72 | return false; 73 | } 74 | } 75 | 76 | return true; 77 | } 78 | 79 | /** 80 | * Checks if a payload strictly matches some expected values. 81 | * 82 | * @return bool 83 | */ 84 | public function matchesStrict(array $values) 85 | { 86 | return $this->matches($values, true); 87 | } 88 | 89 | /** 90 | * Get the payload. 91 | */ 92 | public function get($claim = null) 93 | { 94 | $claim = value($claim); 95 | 96 | if (null !== $claim) { 97 | if (is_array($claim)) { 98 | return array_map([$this, 'get'], $claim); 99 | } 100 | 101 | return Arr::get($this->toArray(), $claim); 102 | } 103 | 104 | return $this->toArray(); 105 | } 106 | 107 | /** 108 | * Get the underlying Claim instance. 109 | * 110 | * @param string $claim 111 | * 112 | * @return Claim 113 | */ 114 | public function getInternal($claim) 115 | { 116 | return $this->claims->getByClaimName($claim); 117 | } 118 | 119 | /** 120 | * Determine whether the payload has the claim (by instance). 121 | * 122 | * @return bool 123 | */ 124 | public function has(Claim $claim) 125 | { 126 | return $this->claims->has($claim->getName()); 127 | } 128 | 129 | /** 130 | * Determine whether the payload has the claim (by key). 131 | * 132 | * @param string $claim 133 | * 134 | * @return bool 135 | */ 136 | public function hasKey($claim) 137 | { 138 | return $this->offsetExists($claim); 139 | } 140 | 141 | /** 142 | * Get the array of claims. 143 | * 144 | * @return array 145 | */ 146 | public function toArray() 147 | { 148 | return $this->claims->toPlainArray(); 149 | } 150 | 151 | /** 152 | * Convert the object into something JSON serializable. 153 | * 154 | * @return array 155 | */ 156 | #[\ReturnTypeWillChange] 157 | public function jsonSerialize() 158 | { 159 | return $this->toArray(); 160 | } 161 | 162 | /** 163 | * Get the payload as JSON. 164 | * 165 | * @param int $options 166 | * 167 | * @return string 168 | */ 169 | public function toJson($options = JSON_UNESCAPED_SLASHES) 170 | { 171 | return json_encode($this->toArray(), $options); 172 | } 173 | 174 | /** 175 | * Get the payload as a string. 176 | * 177 | * @return string 178 | */ 179 | public function __toString() 180 | { 181 | return $this->toJson(); 182 | } 183 | 184 | /** 185 | * Determine if an item exists at an offset. 186 | * 187 | * @return bool 188 | */ 189 | #[\ReturnTypeWillChange] 190 | public function offsetExists($key) 191 | { 192 | return Arr::has($this->toArray(), $key); 193 | } 194 | 195 | /** 196 | * Get an item at a given offset. 197 | */ 198 | #[\ReturnTypeWillChange] 199 | public function offsetGet($key) 200 | { 201 | return Arr::get($this->toArray(), $key); 202 | } 203 | 204 | /** 205 | * Don't allow changing the payload as it should be immutable. 206 | * 207 | * @throws PayloadException 208 | */ 209 | #[\ReturnTypeWillChange] 210 | public function offsetSet($key, $value) 211 | { 212 | throw new PayloadException('The payload is immutable'); 213 | } 214 | 215 | /** 216 | * Don't allow changing the payload as it should be immutable. 217 | * 218 | * @param string $key 219 | * 220 | * @return void 221 | * 222 | * @throws PayloadException 223 | */ 224 | #[\ReturnTypeWillChange] 225 | public function offsetUnset($key) 226 | { 227 | throw new PayloadException('The payload is immutable'); 228 | } 229 | 230 | /** 231 | * Count the number of claims. 232 | * 233 | * @return int 234 | */ 235 | #[\ReturnTypeWillChange] 236 | public function count() 237 | { 238 | return count($this->toArray()); 239 | } 240 | 241 | /** 242 | * Invoke the Payload as a callable function. 243 | */ 244 | public function __invoke($claim = null) 245 | { 246 | return $this->get($claim); 247 | } 248 | 249 | /** 250 | * Magically get a claim value. 251 | * 252 | * @param string $method 253 | * @param array $parameters 254 | * 255 | * @throws \BadMethodCallException 256 | */ 257 | public function __call($method, $parameters) 258 | { 259 | if (preg_match('/get(.+)\b/i', $method, $matches)) { 260 | foreach ($this->claims as $claim) { 261 | if (get_class($claim) === 'PHPOpenSourceSaver\\JWTAuth\\Claims\\'.$matches[1]) { 262 | return $claim->getValue(); 263 | } 264 | } 265 | } 266 | 267 | throw new \BadMethodCallException(sprintf('The claim [%s] does not exist on the payload.', Str::after($method, 'get'))); 268 | } 269 | } 270 | -------------------------------------------------------------------------------- /src/Providers/AbstractServiceProvider.php: -------------------------------------------------------------------------------- 1 | 7 | * (c) 2021 PHP Open Source Saver 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | namespace PHPOpenSourceSaver\JWTAuth\Providers; 14 | 15 | use Illuminate\Contracts\Foundation\Application; 16 | use Illuminate\Support\Arr; 17 | use Illuminate\Support\ServiceProvider; 18 | use Namshi\JOSE\JWS; 19 | use PHPOpenSourceSaver\JWTAuth\Blacklist; 20 | use PHPOpenSourceSaver\JWTAuth\Claims\Factory as ClaimFactory; 21 | use PHPOpenSourceSaver\JWTAuth\Console\JWTGenerateCertCommand; 22 | use PHPOpenSourceSaver\JWTAuth\Console\JWTGenerateSecretCommand; 23 | use PHPOpenSourceSaver\JWTAuth\Contracts\Providers\Auth; 24 | use PHPOpenSourceSaver\JWTAuth\Contracts\Providers\JWT as JWTContract; 25 | use PHPOpenSourceSaver\JWTAuth\Contracts\Providers\Storage; 26 | use PHPOpenSourceSaver\JWTAuth\Factory; 27 | use PHPOpenSourceSaver\JWTAuth\Http\Middleware\Authenticate; 28 | use PHPOpenSourceSaver\JWTAuth\Http\Middleware\AuthenticateAndRenew; 29 | use PHPOpenSourceSaver\JWTAuth\Http\Middleware\Check; 30 | use PHPOpenSourceSaver\JWTAuth\Http\Middleware\RefreshToken; 31 | use PHPOpenSourceSaver\JWTAuth\Http\Parser\AuthHeaders; 32 | use PHPOpenSourceSaver\JWTAuth\Http\Parser\InputSource; 33 | use PHPOpenSourceSaver\JWTAuth\Http\Parser\Parser; 34 | use PHPOpenSourceSaver\JWTAuth\Http\Parser\QueryString; 35 | use PHPOpenSourceSaver\JWTAuth\JWT; 36 | use PHPOpenSourceSaver\JWTAuth\JWTAuth; 37 | use PHPOpenSourceSaver\JWTAuth\JWTGuard; 38 | use PHPOpenSourceSaver\JWTAuth\Manager; 39 | use PHPOpenSourceSaver\JWTAuth\Providers\JWT\Lcobucci; 40 | use PHPOpenSourceSaver\JWTAuth\Providers\JWT\Namshi; 41 | use PHPOpenSourceSaver\JWTAuth\Validators\PayloadValidator; 42 | 43 | abstract class AbstractServiceProvider extends ServiceProvider 44 | { 45 | /** 46 | * The middleware aliases. 47 | */ 48 | protected array $middlewareAliases = [ 49 | 'jwt.auth' => Authenticate::class, 50 | 'jwt.check' => Check::class, 51 | 'jwt.refresh' => RefreshToken::class, 52 | 'jwt.renew' => AuthenticateAndRenew::class, 53 | ]; 54 | 55 | /** 56 | * Boot the service provider. 57 | * 58 | * @return void 59 | */ 60 | abstract public function boot(); 61 | 62 | /** 63 | * Register the service provider. 64 | * 65 | * @return void 66 | */ 67 | public function register() 68 | { 69 | $this->registerAliases(); 70 | 71 | $this->registerJWTProvider(); 72 | $this->registerAuthProvider(); 73 | $this->registerStorageProvider(); 74 | $this->registerJWTBlacklist(); 75 | 76 | $this->registerManager(); 77 | $this->registerTokenParser(); 78 | 79 | $this->registerJWT(); 80 | $this->registerJWTAuth(); 81 | $this->registerPayloadValidator(); 82 | $this->registerClaimFactory(); 83 | $this->registerPayloadFactory(); 84 | $this->registerJWTCommands(); 85 | 86 | $this->commands([ 87 | 'tymon.jwt.secret', 88 | 'tymon.jwt.cert', 89 | ]); 90 | } 91 | 92 | /** 93 | * Extend Laravel's Auth. 94 | * 95 | * @return void 96 | */ 97 | protected function extendAuthGuard() 98 | { 99 | $this->app['auth']->extend('jwt', function ($app, $name, array $config) { 100 | $guard = new JWTGuard( 101 | $app['tymon.jwt'], 102 | $app['auth']->createUserProvider($config['provider']), 103 | $app['request'], 104 | $app['events'] 105 | ); 106 | 107 | if (Arr::has($config, 'ttl')) { 108 | $guard->setTTL(Arr::get($config, 'ttl')); 109 | } else { 110 | $guard->setTTL($app->make('config')->get('jwt.ttl')); 111 | } 112 | 113 | $app->refresh('request', $guard, 'setRequest'); 114 | 115 | return $guard; 116 | }); 117 | } 118 | 119 | /** 120 | * Bind some aliases. 121 | * 122 | * @return void 123 | */ 124 | protected function registerAliases() 125 | { 126 | $this->app->alias('tymon.jwt', JWT::class); 127 | $this->app->alias('tymon.jwt.auth', JWTAuth::class); 128 | $this->app->alias('tymon.jwt.provider.jwt', JWTContract::class); 129 | $this->app->alias('tymon.jwt.provider.jwt.namshi', Namshi::class); 130 | $this->app->alias('tymon.jwt.provider.jwt.lcobucci', Lcobucci::class); 131 | $this->app->alias('tymon.jwt.provider.auth', Auth::class); 132 | $this->app->alias('tymon.jwt.provider.storage', Storage::class); 133 | $this->app->alias('tymon.jwt.manager', Manager::class); 134 | $this->app->alias('tymon.jwt.blacklist', Blacklist::class); 135 | $this->app->alias('tymon.jwt.payload.factory', Factory::class); 136 | $this->app->alias('tymon.jwt.validators.payload', PayloadValidator::class); 137 | } 138 | 139 | /** 140 | * Register the bindings for the JSON Web Token provider. 141 | * 142 | * @return void 143 | */ 144 | protected function registerJWTProvider() 145 | { 146 | $this->registerNamshiProvider(); 147 | $this->registerLcobucciProvider(); 148 | 149 | $this->app->singleton('tymon.jwt.provider.jwt', fn ($app) => $this->getConfigInstance($app, 'providers.jwt')); 150 | } 151 | 152 | /** 153 | * Register the bindings for the Namshi JWT provider. 154 | * 155 | * @return void 156 | */ 157 | protected function registerNamshiProvider() 158 | { 159 | $this->app->singleton('tymon.jwt.provider.jwt.namshi', fn ($app) => new Namshi( 160 | new JWS(['typ' => 'JWT', 'alg' => $app->make('config')->get('jwt.algo')]), 161 | $app->make('config')->get('jwt.secret'), 162 | $app->make('config')->get('jwt.algo'), 163 | $app->make('config')->get('jwt.keys') 164 | )); 165 | } 166 | 167 | /** 168 | * Register the bindings for the Lcobucci JWT provider. 169 | * 170 | * @return void 171 | */ 172 | protected function registerLcobucciProvider() 173 | { 174 | $this->app->singleton('tymon.jwt.provider.jwt.lcobucci', fn ($app) => new Lcobucci( 175 | $app->make('config')->get('jwt.secret'), 176 | $app->make('config')->get('jwt.algo'), 177 | $app->make('config')->get('jwt.keys') 178 | )); 179 | } 180 | 181 | /** 182 | * Register the bindings for the Auth provider. 183 | * 184 | * @return void 185 | */ 186 | protected function registerAuthProvider() 187 | { 188 | $this->app->singleton('tymon.jwt.provider.auth', fn ($app) => $this->getConfigInstance($app, 'providers.auth')); 189 | } 190 | 191 | /** 192 | * Register the bindings for the Storage provider. 193 | * 194 | * @return void 195 | */ 196 | protected function registerStorageProvider() 197 | { 198 | $this->app->singleton('tymon.jwt.provider.storage', fn ($app) => $this->getConfigInstance($app, 'providers.storage')); 199 | } 200 | 201 | /** 202 | * Register the bindings for the JWT Manager. 203 | * 204 | * @return void 205 | */ 206 | protected function registerManager() 207 | { 208 | $this->app->singleton('tymon.jwt.manager', function ($app) { 209 | $instance = new Manager( 210 | $app['tymon.jwt.provider.jwt'], 211 | $app['tymon.jwt.blacklist'], 212 | $app['tymon.jwt.payload.factory'] 213 | ); 214 | 215 | return $instance->setBlacklistEnabled((bool) $app->make('config')->get('jwt.blacklist_enabled')) 216 | ->setPersistentClaims($app->make('config')->get('jwt.persistent_claims')) 217 | ->setBlackListExceptionEnabled((bool) $app->make('config')->get('jwt.show_black_list_exception', 0)); 218 | }); 219 | } 220 | 221 | /** 222 | * Register the bindings for the Token Parser. 223 | * 224 | * @return void 225 | */ 226 | protected function registerTokenParser() 227 | { 228 | $this->app->singleton('tymon.jwt.parser', function ($app) { 229 | $parser = new Parser( 230 | $app['request'], 231 | [ 232 | new AuthHeaders(), 233 | new QueryString(), 234 | new InputSource(), 235 | ] 236 | ); 237 | 238 | $app->refresh('request', $parser, 'setRequest'); 239 | 240 | return $parser; 241 | }); 242 | } 243 | 244 | /** 245 | * Register the bindings for the main JWT class. 246 | * 247 | * @return void 248 | */ 249 | protected function registerJWT() 250 | { 251 | $this->app->singleton('tymon.jwt', fn ($app) => (new JWT( 252 | $app['tymon.jwt.manager'], 253 | $app['tymon.jwt.parser'] 254 | ))->lockSubject($app->make('config')->get('jwt.lock_subject'))); 255 | } 256 | 257 | /** 258 | * Register the bindings for the main JWTAuth class. 259 | * 260 | * @return void 261 | */ 262 | protected function registerJWTAuth() 263 | { 264 | $this->app->singleton('tymon.jwt.auth', fn ($app) => (new JWTAuth( 265 | $app['tymon.jwt.manager'], 266 | $app['tymon.jwt.provider.auth'], 267 | $app['tymon.jwt.parser'] 268 | ))->lockSubject($app->make('config')->get('jwt.lock_subject'))); 269 | } 270 | 271 | /** 272 | * Register the bindings for the Blacklist. 273 | * 274 | * @return void 275 | */ 276 | protected function registerJWTBlacklist() 277 | { 278 | $this->app->singleton('tymon.jwt.blacklist', function ($app) { 279 | $instance = new Blacklist($app['tymon.jwt.provider.storage']); 280 | 281 | return $instance->setGracePeriod($app->make('config')->get('jwt.blacklist_grace_period')) 282 | ->setRefreshTTL($app->make('config')->get('jwt.refresh_ttl')); 283 | }); 284 | } 285 | 286 | /** 287 | * Register the bindings for the payload validator. 288 | * 289 | * @return void 290 | */ 291 | protected function registerPayloadValidator() 292 | { 293 | $this->app->singleton('tymon.jwt.validators.payload', fn ($app) => (new PayloadValidator()) 294 | ->setRefreshTTL($app->make('config')->get('jwt.refresh_ttl')) 295 | ->setRequiredClaims($app->make('config')->get('jwt.required_claims'))); 296 | } 297 | 298 | /** 299 | * Register the bindings for the Claim Factory. 300 | * 301 | * @return void 302 | */ 303 | protected function registerClaimFactory() 304 | { 305 | $this->app->singleton('tymon.jwt.claim.factory', function ($app) { 306 | $factory = new ClaimFactory($app['request']); 307 | $app->refresh('request', $factory, 'setRequest'); 308 | 309 | return $factory->setTTL($app->make('config')->get('jwt.ttl')) 310 | ->setLeeway($app->make('config')->get('jwt.leeway')); 311 | }); 312 | } 313 | 314 | /** 315 | * Register the bindings for the Payload Factory. 316 | * 317 | * @return void 318 | */ 319 | protected function registerPayloadFactory() 320 | { 321 | $this->app->singleton('tymon.jwt.payload.factory', fn ($app) => new Factory( 322 | $app['tymon.jwt.claim.factory'], 323 | $app['tymon.jwt.validators.payload'] 324 | )); 325 | } 326 | 327 | /** 328 | * Register the Artisan command. 329 | * 330 | * @return void 331 | */ 332 | protected function registerJWTCommands() 333 | { 334 | $this->app->singleton('tymon.jwt.secret', fn () => new JWTGenerateSecretCommand()); 335 | $this->app->singleton('tymon.jwt.cert', fn () => new JWTGenerateCertCommand()); 336 | } 337 | 338 | /** 339 | * Get an instantiable configuration instance. 340 | * 341 | * @param Application $app 342 | * @param string $key 343 | */ 344 | protected function getConfigInstance($app, $key) 345 | { 346 | $instance = $app->make('config')->get('jwt.'.$key); 347 | 348 | if (is_string($instance)) { 349 | return $this->app->make($instance); 350 | } 351 | 352 | return $instance; 353 | } 354 | } 355 | -------------------------------------------------------------------------------- /src/Providers/Auth/Illuminate.php: -------------------------------------------------------------------------------- 1 | 7 | * (c) 2021 PHP Open Source Saver 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | namespace PHPOpenSourceSaver\JWTAuth\Providers\Auth; 14 | 15 | use Illuminate\Contracts\Auth\Guard as GuardContract; 16 | use PHPOpenSourceSaver\JWTAuth\Contracts\Providers\Auth; 17 | 18 | class Illuminate implements Auth 19 | { 20 | /** 21 | * The authentication guard. 22 | * 23 | * @var GuardContract 24 | */ 25 | protected $auth; 26 | 27 | /** 28 | * Constructor. 29 | * 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 | * @return bool 41 | */ 42 | public function byCredentials(array $credentials) 43 | { 44 | return $this->auth->once($credentials); 45 | } 46 | 47 | /** 48 | * Authenticate a user via the id. 49 | * 50 | * @return bool 51 | */ 52 | public function byId($id) 53 | { 54 | return $this->auth->onceUsingId($id); 55 | } 56 | 57 | /** 58 | * Get the currently authenticated user. 59 | */ 60 | public function user() 61 | { 62 | return $this->auth->user(); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/Providers/JWT/Lcobucci.php: -------------------------------------------------------------------------------- 1 | 7 | * (c) 2021 PHP Open Source Saver 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | namespace PHPOpenSourceSaver\JWTAuth\Providers\JWT; 14 | 15 | use Illuminate\Support\Collection; 16 | use Lcobucci\JWT\Builder; 17 | use Lcobucci\JWT\Configuration; 18 | use Lcobucci\JWT\Signer; 19 | use Lcobucci\JWT\Signer\Ecdsa; 20 | use Lcobucci\JWT\Signer\Ecdsa\Sha256 as ES256; 21 | use Lcobucci\JWT\Signer\Ecdsa\Sha384 as ES384; 22 | use Lcobucci\JWT\Signer\Ecdsa\Sha512 as ES512; 23 | use Lcobucci\JWT\Signer\Hmac\Sha256 as HS256; 24 | use Lcobucci\JWT\Signer\Hmac\Sha384 as HS384; 25 | use Lcobucci\JWT\Signer\Hmac\Sha512 as HS512; 26 | use Lcobucci\JWT\Signer\Key; 27 | use Lcobucci\JWT\Signer\Key\InMemory; 28 | use Lcobucci\JWT\Signer\Rsa; 29 | use Lcobucci\JWT\Signer\Rsa\Sha256 as RS256; 30 | use Lcobucci\JWT\Signer\Rsa\Sha384 as RS384; 31 | use Lcobucci\JWT\Signer\Rsa\Sha512 as RS512; 32 | use Lcobucci\JWT\Token\RegisteredClaims; 33 | use Lcobucci\JWT\Validation\Constraint\SignedWith; 34 | use PHPOpenSourceSaver\JWTAuth\Contracts\Providers\JWT; 35 | use PHPOpenSourceSaver\JWTAuth\Exceptions\JWTException; 36 | use PHPOpenSourceSaver\JWTAuth\Exceptions\TokenInvalidException; 37 | 38 | class Lcobucci extends Provider implements JWT 39 | { 40 | /** 41 | * The builder instance. 42 | * 43 | * @var Builder 44 | */ 45 | protected $builder; 46 | 47 | /** 48 | * The configuration instance. 49 | * 50 | * @var Configuration 51 | */ 52 | protected $config; 53 | 54 | /** 55 | * The Signer instance. 56 | * 57 | * @var Signer 58 | */ 59 | protected $signer; 60 | 61 | /** 62 | * Create the Lcobucci provider. 63 | * 64 | * @param string $secret 65 | * @param string $algo 66 | * @param Configuration $config optional, to pass an existing configuration to be used 67 | * 68 | * @return void 69 | */ 70 | public function __construct( 71 | $secret, 72 | $algo, 73 | array $keys, 74 | $config = null, 75 | ) { 76 | parent::__construct($secret, $algo, $keys); 77 | $this->generateConfig($config); 78 | } 79 | 80 | /** 81 | * Generate the config. 82 | * 83 | * @param Configuration $config optional, to pass an existing configuration to be used 84 | * 85 | * @return void 86 | */ 87 | private function generateConfig($config = null) 88 | { 89 | $this->signer = $this->getSigner(); 90 | 91 | if (!is_null($config)) { 92 | $this->config = $config; 93 | } elseif ($this->isAsymmetric()) { 94 | $this->config = Configuration::forAsymmetricSigner($this->signer, $this->getSigningKey(), $this->getVerificationKey()); 95 | } else { 96 | $this->config = Configuration::forSymmetricSigner($this->signer, InMemory::plainText($this->getSecret())); 97 | } 98 | if (!count($this->config->validationConstraints())) { 99 | $this->config->setValidationConstraints( 100 | new SignedWith($this->signer, $this->getVerificationKey()), 101 | ); 102 | } 103 | } 104 | 105 | /** 106 | * Set the secret used to sign the token and regenerate the config using the secret. 107 | * 108 | * @param string $secret 109 | * 110 | * @return $this 111 | */ 112 | public function setSecret($secret) 113 | { 114 | $this->secret = $secret; 115 | $this->generateConfig(); 116 | 117 | return $this; 118 | } 119 | 120 | /** 121 | * Gets the {@see $config} attribute. 122 | * 123 | * @return Configuration 124 | */ 125 | public function getConfig() 126 | { 127 | return $this->config; 128 | } 129 | 130 | /** 131 | * Signers that this provider supports. 132 | * 133 | * @var array 134 | */ 135 | protected $signers = [ 136 | 'HS256' => HS256::class, 137 | 'HS384' => HS384::class, 138 | 'HS512' => HS512::class, 139 | 'RS256' => RS256::class, 140 | 'RS384' => RS384::class, 141 | 'RS512' => RS512::class, 142 | 'ES256' => ES256::class, 143 | 'ES384' => ES384::class, 144 | 'ES512' => ES512::class, 145 | ]; 146 | 147 | /** 148 | * Create a JSON Web Token. 149 | * 150 | * @return string 151 | * 152 | * @throws JWTException 153 | */ 154 | public function encode(array $payload) 155 | { 156 | $this->builder = null; 157 | $this->builder = $this->config->builder(); 158 | 159 | try { 160 | foreach ($payload as $key => $value) { 161 | $this->builder = $this->addClaim($key, $value); 162 | } 163 | 164 | return $this->builder->getToken($this->config->signer(), $this->config->signingKey())->toString(); 165 | } catch (\Exception $e) { 166 | throw new JWTException('Could not create token: '.$e->getMessage(), $e->getCode(), $e); 167 | } 168 | } 169 | 170 | /** 171 | * Decode a JSON Web Token. 172 | * 173 | * @param string $token 174 | * 175 | * @return array 176 | * 177 | * @throws JWTException 178 | */ 179 | public function decode($token) 180 | { 181 | try { 182 | $jwt = $this->config->parser()->parse($token); 183 | } catch (\Exception $e) { 184 | throw new TokenInvalidException('Could not decode token: '.$e->getMessage(), $e->getCode(), $e); 185 | } 186 | 187 | if (!$this->config->validator()->validate($jwt, ...$this->config->validationConstraints())) { 188 | throw new TokenInvalidException('Token Signature could not be verified.'); 189 | } 190 | 191 | return (new Collection($jwt->claims()->all()))->map(function ($claim) { 192 | if (is_a($claim, \DateTimeImmutable::class)) { 193 | return $claim->getTimestamp(); 194 | } 195 | if (is_object($claim) && method_exists($claim, 'getValue')) { 196 | return $claim->getValue(); 197 | } 198 | 199 | return $claim; 200 | })->toArray(); 201 | } 202 | 203 | /** 204 | * Adds a claim to the {@see $config}. 205 | * 206 | * @param string $key 207 | */ 208 | protected function addClaim($key, $value): Builder 209 | { 210 | if (!isset($this->builder)) { 211 | $this->builder = $this->config->builder(); 212 | } 213 | 214 | return match ($key) { 215 | RegisteredClaims::ID => $this->builder->identifiedBy($value), 216 | RegisteredClaims::EXPIRATION_TIME => $this->builder->expiresAt(\DateTimeImmutable::createFromFormat('U', $value)), 217 | RegisteredClaims::NOT_BEFORE => $this->builder->canOnlyBeUsedAfter(\DateTimeImmutable::createFromFormat('U', $value)), 218 | RegisteredClaims::ISSUED_AT => $this->builder->issuedAt(\DateTimeImmutable::createFromFormat('U', $value)), 219 | RegisteredClaims::ISSUER => $this->builder->issuedBy($value), 220 | RegisteredClaims::AUDIENCE => is_array($value) ? $this->builder->permittedFor(...$value) : $this->builder->permittedFor($value), 221 | RegisteredClaims::SUBJECT => $this->builder->relatedTo($value), 222 | default => $this->builder->withClaim($key, $value), 223 | }; 224 | } 225 | 226 | /** 227 | * Get the signer instance. 228 | * 229 | * @return Signer 230 | * 231 | * @throws JWTException 232 | */ 233 | protected function getSigner() 234 | { 235 | if (!array_key_exists($this->algo, $this->signers)) { 236 | throw new JWTException('The given algorithm could not be found'); 237 | } 238 | 239 | $signer = $this->signers[$this->algo]; 240 | 241 | return new $signer(); 242 | } 243 | 244 | protected function isAsymmetric() 245 | { 246 | $reflect = new \ReflectionClass($this->signer); 247 | 248 | return $reflect->isSubclassOf(Rsa::class) || $reflect->isSubclassOf(Ecdsa::class); 249 | } 250 | 251 | /** 252 | * Get the key used to sign the tokens. 253 | * 254 | * @return Key|string 255 | */ 256 | protected function getSigningKey() 257 | { 258 | return $this->isAsymmetric() ? 259 | InMemory::plainText($this->getPrivateKey(), $this->getPassphrase() ?? '') : 260 | InMemory::plainText($this->getSecret()); 261 | } 262 | 263 | /** 264 | * Get the key used to verify the tokens. 265 | * 266 | * @return Key|string 267 | */ 268 | protected function getVerificationKey() 269 | { 270 | return $this->isAsymmetric() ? 271 | InMemory::plainText($this->getPublicKey()) : 272 | InMemory::plainText($this->getSecret()); 273 | } 274 | } 275 | -------------------------------------------------------------------------------- /src/Providers/JWT/Namshi.php: -------------------------------------------------------------------------------- 1 | 7 | * (c) 2021 PHP Open Source Saver 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | namespace PHPOpenSourceSaver\JWTAuth\Providers\JWT; 14 | 15 | use Namshi\JOSE\JWS; 16 | use Namshi\JOSE\Signer\OpenSSL\PublicKey; 17 | use PHPOpenSourceSaver\JWTAuth\Contracts\Providers\JWT; 18 | use PHPOpenSourceSaver\JWTAuth\Exceptions\JWTException; 19 | use PHPOpenSourceSaver\JWTAuth\Exceptions\TokenInvalidException; 20 | 21 | class Namshi extends Provider implements JWT 22 | { 23 | /** 24 | * The JWS. 25 | * 26 | * @var JWS 27 | */ 28 | protected $jws; 29 | 30 | /** 31 | * Constructor. 32 | * 33 | * @param string $secret 34 | * @param string $algo 35 | * 36 | * @return void 37 | */ 38 | public function __construct(JWS $jws, $secret, $algo, array $keys) 39 | { 40 | parent::__construct($secret, $algo, $keys); 41 | 42 | $this->jws = $jws; 43 | } 44 | 45 | /** 46 | * Create a JSON Web Token. 47 | * 48 | * @return string 49 | * 50 | * @throws JWTException 51 | */ 52 | public function encode(array $payload) 53 | { 54 | try { 55 | $this->jws->setPayload($payload)->sign($this->getSigningKey(), $this->getPassphrase()); 56 | 57 | return (string) $this->jws->getTokenString(); 58 | } catch (\Exception $e) { 59 | throw new JWTException('Could not create token: '.$e->getMessage(), $e->getCode(), $e); 60 | } 61 | } 62 | 63 | /** 64 | * Decode a JSON Web Token. 65 | * 66 | * @param string $token 67 | * 68 | * @return array 69 | * 70 | * @throws JWTException 71 | */ 72 | public function decode($token) 73 | { 74 | try { 75 | // Let's never allow insecure tokens 76 | $jws = $this->jws->load($token, false); 77 | } catch (\InvalidArgumentException $e) { 78 | throw new TokenInvalidException('Could not decode token: '.$e->getMessage(), $e->getCode(), $e); 79 | } 80 | 81 | if (!$jws->verify($this->getVerificationKey(), $this->getAlgo())) { 82 | throw new TokenInvalidException('Token Signature could not be verified.'); 83 | } 84 | 85 | return (array) $jws->getPayload(); 86 | } 87 | 88 | protected function isAsymmetric() 89 | { 90 | try { 91 | return (new \ReflectionClass(sprintf('Namshi\\JOSE\\Signer\\OpenSSL\\%s', $this->getAlgo())))->isSubclassOf(PublicKey::class); 92 | } catch (\ReflectionException $e) { 93 | throw new JWTException('The given algorithm could not be found', $e->getCode(), $e); 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/Providers/JWT/Provider.php: -------------------------------------------------------------------------------- 1 | 7 | * (c) 2021 PHP Open Source Saver 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | namespace PHPOpenSourceSaver\JWTAuth\Providers\JWT; 14 | 15 | use Illuminate\Support\Arr; 16 | use PHPOpenSourceSaver\JWTAuth\Exceptions\JWTException; 17 | use PHPOpenSourceSaver\JWTAuth\Exceptions\SecretMissingException; 18 | 19 | abstract class Provider 20 | { 21 | /** 22 | * The secret. 23 | */ 24 | protected ?string $secret; 25 | 26 | /** 27 | * The array of keys. 28 | */ 29 | protected array $keys; 30 | 31 | /** 32 | * The used algorithm. 33 | */ 34 | protected string $algo; 35 | 36 | /** 37 | * Constructor. 38 | * 39 | * @param string $secret 40 | * @param string $algo 41 | * 42 | * @return void 43 | */ 44 | public function __construct($secret, $algo, array $keys) 45 | { 46 | if (is_null($secret) && (is_null($keys['public']) || is_null($keys['private']))) { 47 | throw new SecretMissingException(); 48 | } 49 | 50 | $this->secret = $secret; 51 | $this->algo = $algo; 52 | $this->keys = $keys; 53 | } 54 | 55 | /** 56 | * Set the algorithm used to sign the token. 57 | * 58 | * @param string $algo 59 | * 60 | * @return $this 61 | */ 62 | public function setAlgo($algo) 63 | { 64 | $this->algo = $algo; 65 | 66 | return $this; 67 | } 68 | 69 | /** 70 | * Get the algorithm used to sign the token. 71 | * 72 | * @return string 73 | */ 74 | public function getAlgo() 75 | { 76 | return $this->algo; 77 | } 78 | 79 | /** 80 | * Set the secret used to sign the token. 81 | * 82 | * @param string $secret 83 | * 84 | * @return $this 85 | */ 86 | public function setSecret($secret) 87 | { 88 | $this->secret = $secret; 89 | 90 | return $this; 91 | } 92 | 93 | /** 94 | * Get the secret used to sign the token. 95 | * 96 | * @return string 97 | */ 98 | public function getSecret() 99 | { 100 | return $this->secret; 101 | } 102 | 103 | /** 104 | * Set the keys used to sign the token. 105 | * 106 | * @return $this 107 | */ 108 | public function setKeys(array $keys) 109 | { 110 | $this->keys = $keys; 111 | 112 | return $this; 113 | } 114 | 115 | /** 116 | * Get the array of keys used to sign tokens 117 | * with an asymmetric algorithm. 118 | * 119 | * @return array 120 | */ 121 | public function getKeys() 122 | { 123 | return $this->keys; 124 | } 125 | 126 | /** 127 | * Get the public key used to sign tokens 128 | * with an asymmetric algorithm. 129 | * 130 | * @return resource|string 131 | */ 132 | public function getPublicKey() 133 | { 134 | return Arr::get($this->keys, 'public'); 135 | } 136 | 137 | /** 138 | * Get the private key used to sign tokens 139 | * with an asymmetric algorithm. 140 | * 141 | * @return resource|string 142 | */ 143 | public function getPrivateKey() 144 | { 145 | return Arr::get($this->keys, 'private'); 146 | } 147 | 148 | /** 149 | * Get the passphrase used to sign tokens 150 | * with an asymmetric algorithm. 151 | * 152 | * @return string 153 | */ 154 | public function getPassphrase() 155 | { 156 | return Arr::get($this->keys, 'passphrase'); 157 | } 158 | 159 | /** 160 | * Get the key used to sign the tokens. 161 | * 162 | * @return resource|string 163 | */ 164 | protected function getSigningKey() 165 | { 166 | return $this->isAsymmetric() ? $this->getPrivateKey() : $this->getSecret(); 167 | } 168 | 169 | /** 170 | * Get the key used to verify the tokens. 171 | * 172 | * @return resource|string 173 | */ 174 | protected function getVerificationKey() 175 | { 176 | return $this->isAsymmetric() ? $this->getPublicKey() : $this->getSecret(); 177 | } 178 | 179 | /** 180 | * Determine if the algorithm is asymmetric, and thus 181 | * requires a public/private key combo. 182 | * 183 | * @return bool 184 | * 185 | * @throws JWTException 186 | */ 187 | abstract protected function isAsymmetric(); 188 | } 189 | -------------------------------------------------------------------------------- /src/Providers/LaravelServiceProvider.php: -------------------------------------------------------------------------------- 1 | 7 | * (c) 2021 PHP Open Source Saver 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | namespace PHPOpenSourceSaver\JWTAuth\Providers; 14 | 15 | use Laravel\Octane\Events\RequestReceived; 16 | use Laravel\Octane\Events\TaskReceived; 17 | use Laravel\Octane\Events\TickReceived; 18 | use PHPOpenSourceSaver\JWTAuth\Facades\JWTAuth; 19 | use PHPOpenSourceSaver\JWTAuth\Facades\JWTFactory; 20 | use PHPOpenSourceSaver\JWTAuth\Facades\JWTProvider; 21 | use PHPOpenSourceSaver\JWTAuth\Http\Parser\Cookies; 22 | use PHPOpenSourceSaver\JWTAuth\Http\Parser\RouteParams; 23 | 24 | class LaravelServiceProvider extends AbstractServiceProvider 25 | { 26 | public function boot() 27 | { 28 | $path = realpath(__DIR__.'/../../config/config.php'); 29 | 30 | $this->publishes([$path => $this->app->configPath('jwt.php')], 'config'); 31 | $this->mergeConfigFrom($path, 'jwt'); 32 | 33 | $this->aliasMiddleware(); 34 | 35 | $this->extendAuthGuard(); 36 | 37 | $config = $this->app->make('config'); 38 | 39 | $this->app['tymon.jwt.parser']->addParser([ 40 | new RouteParams(), 41 | (new Cookies( 42 | $config->get('jwt.decrypt_cookies'), 43 | ))->setKey($config->get('jwt.cookie_key_name', 'token')), 44 | ]); 45 | 46 | if (isset($_SERVER['LARAVEL_OCTANE'])) { 47 | $clear = function () { 48 | JWTAuth::clearResolvedInstances(); 49 | JWTFactory::clearResolvedInstances(); 50 | JWTProvider::clearResolvedInstances(); 51 | }; 52 | 53 | $this->app['events']->listen(RequestReceived::class, $clear); 54 | $this->app['events']->listen(TaskReceived::class, $clear); 55 | $this->app['events']->listen(TickReceived::class, $clear); 56 | } 57 | } 58 | 59 | protected function registerStorageProvider() 60 | { 61 | $this->app->singleton('tymon.jwt.provider.storage', function ($app) { 62 | $instance = $this->getConfigInstance($app, 'providers.storage'); 63 | 64 | if (method_exists($instance, 'setLaravelVersion')) { 65 | $instance->setLaravelVersion($this->app->version()); 66 | } 67 | 68 | return $instance; 69 | }); 70 | } 71 | 72 | /** 73 | * Alias the middleware. 74 | * 75 | * @return void 76 | */ 77 | protected function aliasMiddleware() 78 | { 79 | $router = $this->app['router']; 80 | 81 | $method = method_exists($router, 'aliasMiddleware') ? 'aliasMiddleware' : 'middleware'; 82 | 83 | foreach ($this->middlewareAliases as $alias => $middleware) { 84 | $router->$method($alias, $middleware); 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/Providers/LumenServiceProvider.php: -------------------------------------------------------------------------------- 1 | 7 | * (c) 2021 PHP Open Source Saver 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | namespace PHPOpenSourceSaver\JWTAuth\Providers; 14 | 15 | use PHPOpenSourceSaver\JWTAuth\Http\Parser\LumenRouteParams; 16 | 17 | class LumenServiceProvider extends AbstractServiceProvider 18 | { 19 | public function boot() 20 | { 21 | $this->app->configure('jwt'); 22 | 23 | $path = realpath(__DIR__.'/../../config/config.php'); 24 | $this->mergeConfigFrom($path, 'jwt'); 25 | 26 | $this->app->routeMiddleware($this->middlewareAliases); 27 | 28 | $this->extendAuthGuard(); 29 | 30 | $this->app['tymon.jwt.parser']->addParser(new LumenRouteParams()); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Providers/Storage/Illuminate.php: -------------------------------------------------------------------------------- 1 | 7 | * (c) 2021 PHP Open Source Saver 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | namespace PHPOpenSourceSaver\JWTAuth\Providers\Storage; 14 | 15 | use Illuminate\Contracts\Cache\Repository as CacheContract; 16 | use PHPOpenSourceSaver\JWTAuth\Contracts\Providers\Storage; 17 | use Psr\SimpleCache\CacheInterface as PsrCacheInterface; 18 | 19 | class Illuminate implements Storage 20 | { 21 | /** 22 | * The cache repository contract. 23 | * 24 | * @var CacheContract 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 | * @return void 49 | */ 50 | public function __construct(CacheContract $cache) 51 | { 52 | $this->cache = $cache; 53 | } 54 | 55 | /** 56 | * Add a new item into storage. 57 | * 58 | * @param string $key 59 | * @param int $minutes 60 | * 61 | * @return void 62 | */ 63 | public function add($key, $value, $minutes) 64 | { 65 | // If the laravel version is 5.8 or higher then convert minutes to seconds. 66 | if (null !== $this->laravelVersion 67 | && is_int($minutes) 68 | && version_compare($this->laravelVersion, '5.8', '>=') 69 | ) { 70 | $minutes = $minutes * 60; 71 | } 72 | 73 | $this->cache()->put($key, $value, $minutes); 74 | } 75 | 76 | /** 77 | * Add a new item into storage forever. 78 | * 79 | * @param string $key 80 | * 81 | * @return void 82 | */ 83 | public function forever($key, $value) 84 | { 85 | $this->cache()->forever($key, $value); 86 | } 87 | 88 | /** 89 | * Get an item from storage. 90 | * 91 | * @param string $key 92 | */ 93 | public function get($key) 94 | { 95 | return $this->cache()->get($key); 96 | } 97 | 98 | /** 99 | * Remove an item from storage. 100 | * 101 | * @param string $key 102 | * 103 | * @return bool 104 | */ 105 | public function destroy($key) 106 | { 107 | return $this->cache()->forget($key); 108 | } 109 | 110 | /** 111 | * Remove all items associated with the tag. 112 | * 113 | * @return void 114 | */ 115 | public function flush() 116 | { 117 | $this->cache()->flush(); 118 | } 119 | 120 | /** 121 | * Return the cache instance with tags attached. 122 | * 123 | * @return CacheContract 124 | */ 125 | protected function cache() 126 | { 127 | if (null === $this->supportsTags) { 128 | $this->determineTagSupport(); 129 | } 130 | 131 | if ($this->supportsTags) { 132 | return $this->cache->tags($this->tag); 133 | } 134 | 135 | return $this->cache; 136 | } 137 | 138 | /** 139 | * Set the laravel version. 140 | */ 141 | public function setLaravelVersion($version) 142 | { 143 | $this->laravelVersion = $version; 144 | 145 | return $this; 146 | } 147 | 148 | /** 149 | * Detect as best we can whether tags are supported with this repository & store, 150 | * and save our result on the $supportsTags flag. 151 | * 152 | * @return void 153 | */ 154 | protected function determineTagSupport() 155 | { 156 | // Laravel >= 5.1.28 157 | if (method_exists($this->cache, 'tags') || $this->cache instanceof PsrCacheInterface) { 158 | try { 159 | // Attempt the repository tags command, which throws exceptions when unsupported 160 | $this->cache->tags($this->tag); 161 | $this->supportsTags = true; 162 | } catch (\BadMethodCallException $ex) { 163 | $this->supportsTags = false; 164 | } 165 | } else { 166 | // Laravel <= 5.1.27 167 | if (method_exists($this->cache, 'getStore')) { 168 | // Check for the tags function directly on the store 169 | $this->supportsTags = method_exists($this->cache->getStore(), 'tags'); 170 | } else { 171 | // Must be using custom cache repository without getStore(), and all bets are off, 172 | // or we are mocking the cache contract (in testing), which will not create a getStore method 173 | $this->supportsTags = false; 174 | } 175 | } 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /src/Support/CustomClaims.php: -------------------------------------------------------------------------------- 1 | 7 | * (c) 2021 PHP Open Source Saver 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | namespace PHPOpenSourceSaver\JWTAuth\Support; 14 | 15 | trait CustomClaims 16 | { 17 | /** 18 | * Custom claims. 19 | * 20 | * @var array 21 | */ 22 | protected $customClaims = []; 23 | 24 | /** 25 | * Set the custom claims. 26 | * 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 | * @return $this 40 | */ 41 | public function claims(array $customClaims) 42 | { 43 | return $this->customClaims($customClaims); 44 | } 45 | 46 | /** 47 | * Get the custom claims. 48 | * 49 | * @return array 50 | */ 51 | public function getCustomClaims() 52 | { 53 | return $this->customClaims; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/Support/RefreshFlow.php: -------------------------------------------------------------------------------- 1 | 7 | * (c) 2021 PHP Open Source Saver 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | namespace PHPOpenSourceSaver\JWTAuth\Support; 14 | 15 | trait RefreshFlow 16 | { 17 | /** 18 | * The refresh flow flag. 19 | * 20 | * @var bool 21 | */ 22 | protected $refreshFlow = false; 23 | 24 | /** 25 | * Set the refresh flow flag. 26 | * 27 | * @param bool $refreshFlow 28 | * 29 | * @return $this 30 | */ 31 | public function setRefreshFlow($refreshFlow = true) 32 | { 33 | $this->refreshFlow = $refreshFlow; 34 | 35 | return $this; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Support/Utils.php: -------------------------------------------------------------------------------- 1 | 7 | * (c) 2021 PHP Open Source Saver 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | namespace PHPOpenSourceSaver\JWTAuth\Support; 14 | 15 | use Carbon\Carbon; 16 | 17 | class Utils 18 | { 19 | /** 20 | * Get the Carbon instance for the current time. 21 | * 22 | * @return Carbon 23 | */ 24 | public static function now() 25 | { 26 | return Carbon::now('UTC'); 27 | } 28 | 29 | /** 30 | * Get the Carbon instance for the timestamp. 31 | * 32 | * @param int $timestamp 33 | * 34 | * @return Carbon 35 | */ 36 | public static function timestamp($timestamp) 37 | { 38 | return Carbon::createFromTimestampUTC($timestamp)->timezone('UTC'); 39 | } 40 | 41 | /** 42 | * Checks if a timestamp is in the past. 43 | * 44 | * @param int $timestamp 45 | * @param int $leeway 46 | * 47 | * @return bool 48 | */ 49 | public static function isPast($timestamp, $leeway = 0) 50 | { 51 | $timestamp = static::timestamp($timestamp); 52 | 53 | return $leeway > 0 54 | ? $timestamp->addSeconds($leeway)->isPast() 55 | : $timestamp->isPast(); 56 | } 57 | 58 | /** 59 | * Checks if a timestamp is in the future. 60 | * 61 | * @param int $timestamp 62 | * @param int $leeway 63 | * 64 | * @return bool 65 | */ 66 | public static function isFuture($timestamp, $leeway = 0) 67 | { 68 | $timestamp = static::timestamp($timestamp); 69 | 70 | return $leeway > 0 71 | ? $timestamp->subSeconds($leeway)->isFuture() 72 | : $timestamp->isFuture(); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/Token.php: -------------------------------------------------------------------------------- 1 | 7 | * (c) 2021 PHP Open Source Saver 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | namespace PHPOpenSourceSaver\JWTAuth; 14 | 15 | use PHPOpenSourceSaver\JWTAuth\Validators\TokenValidator; 16 | 17 | class Token 18 | { 19 | private string $value; 20 | 21 | /** 22 | * Create a new JSON Web Token. 23 | * 24 | * @param string $value 25 | * 26 | * @return void 27 | * 28 | * @throws Exceptions\TokenInvalidException 29 | */ 30 | public function __construct($value) 31 | { 32 | $this->value = (string) (new TokenValidator())->check($value); 33 | } 34 | 35 | /** 36 | * Get the token. 37 | * 38 | * @return string 39 | */ 40 | public function get() 41 | { 42 | return $this->value; 43 | } 44 | 45 | /** 46 | * Get the token when casting to string. 47 | * 48 | * @return string 49 | */ 50 | public function __toString() 51 | { 52 | return $this->get(); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/Validators/PayloadValidator.php: -------------------------------------------------------------------------------- 1 | 7 | * (c) 2021 PHP Open Source Saver 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | namespace PHPOpenSourceSaver\JWTAuth\Validators; 14 | 15 | use PHPOpenSourceSaver\JWTAuth\Claims\Collection; 16 | use PHPOpenSourceSaver\JWTAuth\Exceptions\TokenInvalidException; 17 | 18 | class PayloadValidator extends Validator 19 | { 20 | /** 21 | * The required claims. 22 | * 23 | * @var array 24 | */ 25 | protected $requiredClaims = [ 26 | 'iss', 27 | 'iat', 28 | 'exp', 29 | 'nbf', 30 | 'sub', 31 | 'jti', 32 | ]; 33 | 34 | /** 35 | * The refresh TTL. 36 | * 37 | * @var int 38 | */ 39 | protected $refreshTTL = 20160; 40 | 41 | /** 42 | * Run the validations on the payload array. 43 | * 44 | * @param Collection $value 45 | * 46 | * @return Collection 47 | */ 48 | public function check($value) 49 | { 50 | $this->validateStructure($value); 51 | 52 | return $this->refreshFlow ? $this->validateRefresh($value) : $this->validatePayload($value); 53 | } 54 | 55 | /** 56 | * Ensure the payload contains the required claims and 57 | * the claims have the relevant type. 58 | * 59 | * @return void 60 | * 61 | * @throws TokenInvalidException 62 | */ 63 | protected function validateStructure(Collection $claims) 64 | { 65 | if ($this->requiredClaims && !$claims->hasAllClaims($this->requiredClaims)) { 66 | throw new TokenInvalidException('JWT payload does not contain the required claims'); 67 | } 68 | } 69 | 70 | /** 71 | * Validate the payload timestamps. 72 | * 73 | * @return Collection 74 | * 75 | * @throws TokenInvalidException 76 | * @throws \PHPOpenSourceSaver\JWTAuth\Exceptions\TokenExpiredException 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 | * @return Collection 87 | * 88 | * @throws \PHPOpenSourceSaver\JWTAuth\Exceptions\TokenExpiredException 89 | */ 90 | protected function validateRefresh(Collection $claims) 91 | { 92 | return null === $this->refreshTTL ? $claims : $claims->validate('refresh', $this->refreshTTL); 93 | } 94 | 95 | /** 96 | * Set the required claims. 97 | * 98 | * @return $this 99 | */ 100 | public function setRequiredClaims(array $claims) 101 | { 102 | $this->requiredClaims = $claims; 103 | 104 | return $this; 105 | } 106 | 107 | /** 108 | * Set the refresh ttl. 109 | * 110 | * @param int $ttl 111 | * 112 | * @return $this 113 | */ 114 | public function setRefreshTTL($ttl) 115 | { 116 | $this->refreshTTL = $ttl; 117 | 118 | return $this; 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /src/Validators/TokenValidator.php: -------------------------------------------------------------------------------- 1 | 7 | * (c) 2021 PHP Open Source Saver 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | namespace PHPOpenSourceSaver\JWTAuth\Validators; 14 | 15 | use PHPOpenSourceSaver\JWTAuth\Exceptions\TokenInvalidException; 16 | 17 | class TokenValidator extends Validator 18 | { 19 | /** 20 | * Check the structure of the token. 21 | * 22 | * @param string $value 23 | * 24 | * @return string 25 | * 26 | * @throws TokenInvalidException 27 | */ 28 | public function check($value) 29 | { 30 | return $this->validateStructure($value); 31 | } 32 | 33 | /** 34 | * @param string $token 35 | * 36 | * @return string 37 | * 38 | * @throws TokenInvalidException 39 | */ 40 | protected function validateStructure($token) 41 | { 42 | $parts = explode('.', $token); 43 | 44 | if (3 !== count($parts)) { 45 | throw new TokenInvalidException('Wrong number of segments'); 46 | } 47 | 48 | $parts = array_filter(array_map('trim', $parts)); 49 | 50 | if (3 !== count($parts) || implode('.', $parts) !== $token) { 51 | throw new TokenInvalidException('Malformed token'); 52 | } 53 | 54 | return $token; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/Validators/Validator.php: -------------------------------------------------------------------------------- 1 | 7 | * (c) 2021 PHP Open Source Saver 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | namespace PHPOpenSourceSaver\JWTAuth\Validators; 14 | 15 | use PHPOpenSourceSaver\JWTAuth\Contracts\Validator as ValidatorContract; 16 | use PHPOpenSourceSaver\JWTAuth\Exceptions\JWTException; 17 | use PHPOpenSourceSaver\JWTAuth\Support\RefreshFlow; 18 | 19 | abstract class Validator implements ValidatorContract 20 | { 21 | use RefreshFlow; 22 | 23 | /** 24 | * Helper function to return a boolean. 25 | * 26 | * @param array $value 27 | * 28 | * @return bool 29 | */ 30 | public function isValid($value) 31 | { 32 | try { 33 | $this->check($value); 34 | } catch (JWTException $e) { 35 | return false; 36 | } 37 | 38 | return true; 39 | } 40 | 41 | /** 42 | * Run the validation. 43 | * 44 | * @param array $value 45 | * 46 | * @return void 47 | */ 48 | abstract public function check($value); 49 | } 50 | --------------------------------------------------------------------------------