├── 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 |
--------------------------------------------------------------------------------