add projet

This commit is contained in:
root
2026-03-05 12:29:37 -05:00
parent 8d1eef8ba8
commit 23b7db1107
9109 changed files with 1106501 additions and 73 deletions
+162
View File
@@ -0,0 +1,162 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
You can find and compare releases at the [GitHub release page](https://github.com/PHP-Open-Source-Saver/jwt-auth/releases).
## [Unreleased]
### Fixed
- Fixed the return type of getMinutesUntilExpired in BlackList, which returned a float instead of an int when using Carbon v2.
- Fixed PHPStan issue in JWTGenerateSecretCommand by ensuring displayKey($key); is called before returning, avoiding returning a void method.
- Fixed missing return true; statements in validatePayload() and validateRefresh() methods of Expiration.php, IssuedAt.php, and NotBefore.php to resolve PHPStan errors.
- Fixed PHPStan error related to new static() by refactoring hasAllClaims method in Collection class.
## [2.8.0] 2025-02-11
Please see (https://github.com/PHP-Open-Source-Saver/jwt-auth/releases/tag/2.8.0)
### Added
- #268 Implement config variable to allow iat to remain unchanged claim when refreshing a token
- Adds support for Laravel 12
- Adds CI testing for PHP 8.4
- Don't show jwt secret if show option is false even if the key is updated
- Casts config ints to int by default in new config file publishes
- Override "id" method in JWTGuard
### Removed
- 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.
## [2.7.2] 2024-09-28
### Added
- Add `cookie_key_name` config to customize cookie name for authentication
- Delegate `Auth::id()` calls to the newly added `getUserId` method
## [2.7.0] 2024-07-24
### Fixed
- Support for Carbon 3 alongside Carbon 2
## [2.6.0] 2024-07-11
### Added
- New `getUserId` method
## [2.5.0] 2024-07-03
### Added
- Refresh iat claim when refreshing a token
## [2.4.0] 2024-05-27
### Added
- Support for lcobucci/jwt^5.0 (and dropped support for ^4.0)
- SetSecret regenerates config with new secret in the Lcobucci provider
## [2.3.0] 2024-05-09
### Added
- Support for Carbon 3 (and drop Carbon 1, but it was unused anyway)
### Removed
- Dropped support for Laravel < 10 and PHP < 8.1
### Fixed
- Use `id` claim for identify user if `sub` doesn't exists.
## [2.2.0] 2024-03-12
### Added
- Different TTL configurations for each guard
- lcobucci/jwt: add array support for `aud` claim
- Laravel 11 support
## [2.1.0] 2023-02-17
### Added
- Laravel 10 support
## [2.0.0] 2022-09-08
- No changes to 2.0.0-RC1
### Added
- Arabic translation for docs by hawkiq
## [2.0.0-RC1] 2022-08-25
### Added
- Adds Octane Compatibility
- Added `ask-passphrase` parameter to generating certs command
- Support autocomplete guard
### Fixed
- Default config value for `show_black_list_exception` changed to true
- Auth header not ignoring other auth schemes
- Fixed replacing of values using regex
## [1.4.2] 2022-04-22
### Added
- Added exception if secret key or private/public key are missing
### Fixed
- Add command for generating certs
## [1.4.1] - 2022-01-24
### Fixed
- Add more ReturnTypeWillChange for PHP 8.1 compatibility
## [1.4.0] - 2022-01-18
### Added
### Fixed
- Fixes #101 - Secret is not nullable but should be according to the library config boilerplate
- Fixes #99 - Steps for migrating from tymons package
## [1.3.0] - 2022-01-13
### Added
- PHP 8.1 support (#58, #77, #87)
- Typed variables (#52)
### Fixed
- Compatability with Laravel 6 versions below 6.15
## [1.2.0] - 2021-11-16
### Added
- Dispatch Auth Events by @okaufmann in #45
## [1.1.1] - 2021-10-21
### Changed
- Blacklisted token exception no more thrown by default by @Messhias in #32
### Fixed
- ECDSA signers by @josecl in #31
## [1.1.0] - 2021-11-11
### Added
- PHP 8.0 and `lcobucci/jwt` version 4 compatability by @eschricker in #14
- Option to hide Blacklisted Token exception by @Messhias in #7
- Throw exception for invalid encrypted cookies by @eschricker in #22
### Fixed
- Typo in tests by @eschricker in #23
[Unreleased]: https://github.com/PHP-Open-Source-Saver/jwt-auth/compare/2.0.0...HEAD
[2.0.0]: https://github.com/PHP-Open-Source-Saver/jwt-auth/compare/1.4.2...2.0.0
[2.0.0-RC1]: https://github.com/PHP-Open-Source-Saver/jwt-auth/compare/1.4.2...2.0.0-RC1
[1.4.2]: https://github.com/PHP-Open-Source-Saver/jwt-auth/compare/1.4.1...1.4.2
[1.4.1]: https://github.com/PHP-Open-Source-Saver/jwt-auth/compare/1.4.0...1.4.1
[1.4.0]: https://github.com/PHP-Open-Source-Saver/jwt-auth/compare/1.3.0...1.4.0
[1.3.0]: https://github.com/PHP-Open-Source-Saver/jwt-auth/compare/1.2.0...1.3.0
[1.2.0]: https://github.com/PHP-Open-Source-Saver/jwt-auth/compare/1.1.1...1.2.0
[1.1.1]: https://github.com/PHP-Open-Source-Saver/jwt-auth/compare/1.1.0...1.1.1
[1.1.0]: https://github.com/PHP-Open-Source-Saver/jwt-auth/compare/1.0.2...1.1.0
+22
View File
@@ -0,0 +1,22 @@
The MIT License (MIT)
Copyright (c) 2014-2021 Sean Tymon
Copyright (c) 2021 PHP Open Source Saver
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+46
View File
@@ -0,0 +1,46 @@
<div dir="rtl" align="right">
## الحقوق
<p dir='rtl' align='right'>
ان هذا المشروع منسوخ من
[tymonsdesigns/jwt-auth](https://github.com/tymondesigns/jwt-auth/wiki)
</br> لقد قررنا نسخه بسبب ان صاحب المشروع الاصلي لم يحدثه منذ زمن طويل.
</p>
## <div dir="rtl" align="right">للتحديث الى المستودع الحالي من [`tymondesigns/jwt-auth`](https://github.com/tymondesigns/jwt-auth)</div>
<div dir="rtl" align="right">
ان هذا المشروع يستخدم نيم سبيس مختلف عن, `tymondesigns/jwt-auth`, ولكن يستخدم نفس الخصائص فلا داعي للقلق:
1) استخدم ايعاز `composer remove tymon/jwt-auth`
> **ملاحظة** قد تظهر بعض الاخطاء يمكنك تجاهلها.
2) قم باستبدال `Tymon\JWTAuth` ب `PHPOpenSourceSaver\JWTAuth`.
> **نصيحة**: يمكن استخدام الاختصارات بمحرر النصوص لديك مثلا. <kbd>Ctrl</kbd> + <kbd>Shift</kbd> + <kbd>R</kbd>
3) نفذ `composer require php-open-source-saver/jwt-auth`
### ملاحظات
بسبب بعض الاضافات في مشروعنا قد تواجه مشاكل بالتوافقية. _ولكن هذا لن يؤثر على مشروعك بشكل عام_, الا اذا كنت [implicitly disabled autodiscovery](https://laravel.com/docs/8.x/packages#opting-out-of-package-discovery) للبكج الاصلي.
العناصر الغير متوافقة حاليا:
- [`JWTGuard`](src/JWTGuard.php) يحتوي على متغير جديد في دالة الانشاء [`$eventDispatcher`](src/Providers/AbstractServiceProvider.php#L97)
</div>
## الوثائق
جميع الشروحات متوفرة على [laravel-jwt-auth.readthedocs.io](https://laravel-jwt-auth.readthedocs.io/)
-----------------------------------
## الأمان
اذا وجدت اي ثغرة يمكنك اتباع [بوليصة الامان](https://github.com/PHP-Open-Source-Saver/jwt-auth/security/policy)
## الرخصة
The MIT License (MIT)
</div>
+6
View File
@@ -0,0 +1,6 @@
# Security Policy
## Reporting a Vulnerability
If you discover any security related issues, please email messhias@gmail.com or
eric.schricker@adiutabyte.de instead of using the issue tracker.
+97
View File
@@ -0,0 +1,97 @@
{
"name": "php-open-source-saver/jwt-auth",
"description": "JSON Web Token Authentication for Laravel and Lumen",
"keywords": [
"auth",
"authentication",
"json web token",
"jwt",
"laravel"
],
"homepage": "https://github.com/PHP-Open-Source-Saver/jwt-auth",
"support": {
"issues": "https://github.com/PHP-Open-Source-Saver/jwt-auth/issues",
"source": "https://github.com/PHP-Open-Source-Saver/jwt-auth"
},
"license": "MIT",
"authors": [
{
"name": "Sean Tymon",
"email": "tymon148@gmail.com",
"homepage": "https://tymon.xyz",
"role": "Forked package creator | Developer"
},
{
"name": "Eric Schricker",
"email": "eric.schricker@adiutabyte.de",
"role": "Developer"
},
{
"name": "Fabio William Conceição",
"email": "messhias@gmail.com",
"role": "Developer"
},
{
"name": "Max Snow",
"email": "contact@maxsnow.me",
"role": "Developer"
}
],
"require": {
"php": "^8.2",
"ext-json": "*",
"illuminate/auth": "^10|^11|^12",
"illuminate/contracts": "^10|^11|^12",
"illuminate/http": "^10|^11|^12",
"illuminate/support": "^10|^11|^12",
"lcobucci/jwt": "^5.4",
"namshi/jose": "^7.0",
"nesbot/carbon": "^2.0|^3.0"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "^3",
"illuminate/console": "^10|^11|^12",
"illuminate/routing": "^10|^11|^12",
"orchestra/testbench": "^8|^9|^10",
"mockery/mockery": "^1.6",
"phpstan/phpstan": "^2",
"phpunit/phpunit": "^10.5|^11"
},
"autoload": {
"psr-4": {
"PHPOpenSourceSaver\\JWTAuth\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"PHPOpenSourceSaver\\JWTAuth\\Test\\": "tests/"
}
},
"extra": {
"branch-alias": {
"dev-develop": "2.0-dev"
},
"laravel": {
"aliases": {
"JWTAuth": "PHPOpenSourceSaver\\JWTAuth\\Facades\\JWTAuth",
"JWTFactory": "PHPOpenSourceSaver\\JWTAuth\\Facades\\JWTFactory"
},
"providers": [
"PHPOpenSourceSaver\\JWTAuth\\Providers\\LaravelServiceProvider"
]
}
},
"funding": [],
"config": {
"sort-packages": true
},
"prefer-stable": true,
"minimum-stability": "dev",
"scripts": {
"php-cs-fixer": "php-cs-fixer fix --diff",
"test": "phpunit --colors=always",
"test:ci": "composer test -- --coverage-text --coverage-clover=coverage.xml",
"phpstan": "phpstan analyse --memory-limit=256M",
"phpstan-baseline": "phpstan analyse --generate-baseline --memory-limit=256M"
}
}
+321
View File
@@ -0,0 +1,321 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| JWT Authentication Secret
|--------------------------------------------------------------------------
|
| Don't forget to set this in your .env file, as it will be used to sign
| your tokens. A helper command is provided for this:
| `php artisan jwt:secret`
|
| Note: This will be used for Symmetric algorithms only (HMAC),
| since RSA and ECDSA use a private/public key combo (See below).
|
*/
'secret' => env('JWT_SECRET'),
/*
|--------------------------------------------------------------------------
| JWT Authentication Keys
|--------------------------------------------------------------------------
|
| The algorithm you are using, will determine whether your tokens are
| signed with a random string (defined in `JWT_SECRET`) or using the
| following public & private keys.
|
| Symmetric Algorithms:
| HS256, HS384 & HS512 will use `JWT_SECRET`.
|
| Asymmetric Algorithms:
| RS256, RS384 & RS512 / ES256, ES384 & ES512 will use the keys below.
|
*/
'keys' => [
/*
|--------------------------------------------------------------------------
| Public Key
|--------------------------------------------------------------------------
|
| A path or resource to your public key.
|
| E.g. 'file://path/to/public/key'
|
*/
'public' => env('JWT_PUBLIC_KEY'),
/*
|--------------------------------------------------------------------------
| Private Key
|--------------------------------------------------------------------------
|
| A path or resource to your private key.
|
| E.g. 'file://path/to/private/key'
|
*/
'private' => env('JWT_PRIVATE_KEY'),
/*
|--------------------------------------------------------------------------
| Passphrase
|--------------------------------------------------------------------------
|
| The passphrase for your private key. Can be null if none set.
|
*/
'passphrase' => env('JWT_PASSPHRASE'),
],
/*
|--------------------------------------------------------------------------
| JWT time to live
|--------------------------------------------------------------------------
|
| Specify the length of time (in minutes) that the token will be valid for.
| Defaults to 1 hour.
|
| You can also set this to null, to yield a never expiring token.
| Some people may want this behaviour for e.g. a mobile app.
| This is not particularly recommended, so make sure you have appropriate
| systems in place to revoke the token if necessary.
| Notice: If you set this to null you should remove 'exp' element from 'required_claims' list.
|
*/
'ttl' => (int) env('JWT_TTL', 60),
/*
|--------------------------------------------------------------------------
| Refresh time to live
|--------------------------------------------------------------------------
|
| Specify the length of time (in minutes) that the token can be refreshed within.
| This defines the refresh window, during which the user can refresh their token
| before re-authentication is required.
|
| By default, a refresh will NOT issue a new "iat" (issued at) timestamp. If changed
| to true, each refresh will issue a new "iat" timestamp, extending the refresh
| period from the most recent refresh. This results in a rolling refresh
|
| To retain a fluid refresh window from the last refresh action (i.e., the behavior between
| version 2.5.0 and 2.8.2), set "refresh_iat" to true. With this setting, the refresh
| window will renew with each subsequent refresh.
|
| The refresh ttl defaults to 2 weeks.
|
| You can also set this to null, to yield an infinite refresh time.
| Some may want this instead of never expiring tokens for e.g. a mobile app.
| This is not particularly recommended, so make sure you have appropriate
| systems in place to revoke the token if necessary.
|
*/
'refresh_iat' => env('JWT_REFRESH_IAT', false),
'refresh_ttl' => (int) env('JWT_REFRESH_TTL', 20160),
/*
|--------------------------------------------------------------------------
| JWT hashing algorithm
|--------------------------------------------------------------------------
|
| Specify the hashing algorithm that will be used to sign the token.
|
| See here: https://github.com/namshi/jose/tree/master/src/Namshi/JOSE/Signer/OpenSSL
| for possible values.
|
*/
'algo' => env('JWT_ALGO', 'HS256'),
/*
|--------------------------------------------------------------------------
| Required Claims
|--------------------------------------------------------------------------
|
| Specify the required claims that must exist in any token.
| A TokenInvalidException will be thrown if any of these claims are not
| present in the payload.
|
*/
'required_claims' => [
'iss',
'iat',
'exp',
'nbf',
'sub',
'jti',
],
/*
|--------------------------------------------------------------------------
| Persistent Claims
|--------------------------------------------------------------------------
|
| Specify the claim keys to be persisted when refreshing a token.
| `sub` and `iat` will automatically be persisted, in
| addition to the these claims.
|
| Note: If a claim does not exist then it will be ignored.
|
*/
'persistent_claims' => [
// 'foo',
// 'bar',
],
/*
|--------------------------------------------------------------------------
| Lock Subject
|--------------------------------------------------------------------------
|
| This will determine whether a `prv` claim is automatically added to
| the token. The purpose of this is to ensure that if you have multiple
| authentication models e.g. `App\User` & `App\OtherPerson`, then we
| should prevent one authentication request from impersonating another,
| if 2 tokens happen to have the same id across the 2 different models.
|
| Under specific circumstances, you may want to disable this behaviour
| e.g. if you only have one authentication model, then you would save
| a little on token size.
|
*/
'lock_subject' => true,
/*
|--------------------------------------------------------------------------
| Leeway
|--------------------------------------------------------------------------
|
| This property gives the jwt timestamp claims some "leeway".
| Meaning that if you have any unavoidable slight clock skew on
| any of your servers then this will afford you some level of cushioning.
|
| This applies to the claims `iat`, `nbf` and `exp`.
|
| Specify in seconds - only if you know you need it.
|
*/
'leeway' => (int) env('JWT_LEEWAY', 0),
/*
|--------------------------------------------------------------------------
| Blacklist Enabled
|--------------------------------------------------------------------------
|
| In order to invalidate tokens, you must have the blacklist enabled.
| If you do not want or need this functionality, then set this to false.
|
*/
'blacklist_enabled' => env('JWT_BLACKLIST_ENABLED', true),
/*
| -------------------------------------------------------------------------
| Blacklist Grace Period
| -------------------------------------------------------------------------
|
| When multiple concurrent requests are made with the same JWT,
| it is possible that some of them fail, due to token regeneration
| on every request.
|
| Set grace period in seconds to prevent parallel request failure.
|
*/
'blacklist_grace_period' => (int) env('JWT_BLACKLIST_GRACE_PERIOD', 0),
/*
|--------------------------------------------------------------------------
| Show blacklisted token option
|--------------------------------------------------------------------------
|
| Specify if you want to show black listed token exception on the laravel logs.
|
*/
'show_black_list_exception' => env('JWT_SHOW_BLACKLIST_EXCEPTION', true),
/*
|--------------------------------------------------------------------------
| Cookies encryption
|--------------------------------------------------------------------------
|
| By default Laravel encrypt cookies for security reason.
| If you decide to not decrypt cookies, you will have to configure Laravel
| to not encrypt your cookie token by adding its name into the $except
| array available in the middleware "EncryptCookies" provided by Laravel.
| see https://laravel.com/docs/master/responses#cookies-and-encryption
| for details.
|
| Set it to true if you want to decrypt cookies.
|
*/
'decrypt_cookies' => false,
/*
|--------------------------------------------------------------------------
| Cookie key name
|--------------------------------------------------------------------------
|
| Specify the cookie key name that you would like to use for the cookie token.
|
*/
'cookie_key_name' => 'token',
/*
|--------------------------------------------------------------------------
| Providers
|--------------------------------------------------------------------------
|
| Specify the various providers used throughout the package.
|
*/
'providers' => [
/*
|--------------------------------------------------------------------------
| JWT Provider
|--------------------------------------------------------------------------
|
| Specify the provider that is used to create and decode the tokens.
|
*/
'jwt' => PHPOpenSourceSaver\JWTAuth\Providers\JWT\Lcobucci::class,
/*
|--------------------------------------------------------------------------
| Authentication Provider
|--------------------------------------------------------------------------
|
| Specify the provider that is used to authenticate users.
|
*/
'auth' => PHPOpenSourceSaver\JWTAuth\Providers\Auth\Illuminate::class,
/*
|--------------------------------------------------------------------------
| Storage Provider
|--------------------------------------------------------------------------
|
| Specify the provider that is used to store tokens in the blacklist.
|
*/
'storage' => PHPOpenSourceSaver\JWTAuth\Providers\Storage\Illuminate::class,
],
];
+240
View File
@@ -0,0 +1,240 @@
<?php
/*
* This file is part of jwt-auth.
*
* (c) 2014-2021 Sean Tymon <tymon148@gmail.com>
* (c) 2021 PHP Open Source Saver
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPOpenSourceSaver\JWTAuth;
use PHPOpenSourceSaver\JWTAuth\Contracts\Providers\Storage;
use PHPOpenSourceSaver\JWTAuth\Support\Utils;
class Blacklist
{
/**
* The storage.
*
* @var Storage
*/
protected $storage;
/**
* The grace period when a token is blacklisted. In seconds.
*
* @var int
*/
protected $gracePeriod = 0;
/**
* Number of minutes from issue date in which a JWT can be refreshed.
*
* @var int
*/
protected $refreshTTL = 20160;
/**
* The unique key held within the blacklist.
*
* @var string
*/
protected $key = 'jti';
/**
* Constructor.
*
* @return void
*/
public function __construct(Storage $storage)
{
$this->storage = $storage;
}
/**
* Add the token (jti claim) to the blacklist.
*
* @return bool
*/
public function add(Payload $payload)
{
// if there is no exp claim then add the jwt to
// the blacklist indefinitely
if (!$payload->hasKey('exp')) {
return $this->addForever($payload);
}
// if we have already added this token to the blacklist
if (!empty($this->storage->get($this->getKey($payload)))) {
return true;
}
$this->storage->add(
$this->getKey($payload),
['valid_until' => $this->getGraceTimestamp()],
$this->getMinutesUntilExpired($payload)
);
return true;
}
/**
* Get the number of minutes until the token expiry.
*
* @return int
*/
protected function getMinutesUntilExpired(Payload $payload)
{
$exp = Utils::timestamp($payload['exp']);
$iat = Utils::timestamp($payload['iat']);
// get the latter of the two expiration dates and find
// the number of minutes until the expiration date,
// plus 1 minute to avoid overlap
$intermediateResult = $exp->max($iat->addMinutes($this->refreshTTL))->addMinute();
// Handle Carbon 2 vs 3 deprecation of "Real" diff functions, see https://github.com/PHP-Open-Source-Saver/jwt-auth/issues/260
if (method_exists($intermediateResult, 'diffInRealMinutes')) {
return (int) round($intermediateResult->diffInRealMinutes(null, true));
} else {
return (int) round($intermediateResult->diffInMinutes(null, true));
}
}
/**
* Add the token (jti claim) to the blacklist indefinitely.
*
* @return bool
*/
public function addForever(Payload $payload)
{
$this->storage->forever($this->getKey($payload), 'forever');
return true;
}
/**
* Determine whether the token has been blacklisted.
*
* @return bool
*/
public function has(Payload $payload)
{
$val = $this->storage->get($this->getKey($payload));
// exit early if the token was blacklisted forever,
if ('forever' === $val) {
return true;
}
// check whether the expiry + grace has past
return !empty($val) && !Utils::isFuture($val['valid_until']);
}
/**
* Remove the token (jti claim) from the blacklist.
*
* @return bool
*/
public function remove(Payload $payload)
{
return $this->storage->destroy($this->getKey($payload));
}
/**
* Remove all tokens from the blacklist.
*
* @return bool
*/
public function clear()
{
$this->storage->flush();
return true;
}
/**
* Get the timestamp when the blacklist comes into effect
* This defaults to immediate (0 seconds).
*
* @return int
*/
protected function getGraceTimestamp()
{
return Utils::now()->addSeconds($this->gracePeriod)->getTimestamp();
}
/**
* Set the grace period.
*
* @param int $gracePeriod
*
* @return $this
*/
public function setGracePeriod($gracePeriod)
{
$this->gracePeriod = (int) $gracePeriod;
return $this;
}
/**
* Get the grace period.
*
* @return int
*/
public function getGracePeriod()
{
return $this->gracePeriod;
}
/**
* Get the unique key held within the blacklist.
*/
public function getKey(Payload $payload)
{
return $payload($this->key);
}
/**
* Set the unique key held within the blacklist.
*
* @param string $key
*
* @return $this
*/
public function setKey($key)
{
$this->key = value($key);
return $this;
}
/**
* Set the refresh time limit.
*
* @param int $ttl
*
* @return $this
*/
public function setRefreshTTL($ttl)
{
$this->refreshTTL = (int) $ttl;
return $this;
}
/**
* Get the refresh time limit.
*
* @return int
*/
public function getRefreshTTL()
{
return $this->refreshTTL;
}
}
@@ -0,0 +1,18 @@
<?php
/*
* This file is part of jwt-auth.
*
* (c) 2014-2021 Sean Tymon <tymon148@gmail.com>
* (c) 2021 PHP Open Source Saver
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPOpenSourceSaver\JWTAuth\Claims;
class Audience extends Claim
{
protected $name = 'aud';
}
@@ -0,0 +1,176 @@
<?php
/*
* This file is part of jwt-auth.
*
* (c) 2014-2021 Sean Tymon <tymon148@gmail.com>
* (c) 2021 PHP Open Source Saver
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPOpenSourceSaver\JWTAuth\Claims;
use Illuminate\Contracts\Support\Arrayable;
use Illuminate\Contracts\Support\Jsonable;
use PHPOpenSourceSaver\JWTAuth\Contracts\Claim as ClaimContract;
use PHPOpenSourceSaver\JWTAuth\Exceptions\InvalidClaimException;
abstract class Claim implements Arrayable, ClaimContract, Jsonable, \JsonSerializable
{
/**
* The claim name.
*
* @var string
*/
protected $name;
/**
* The claim value.
*/
private $value;
/**
* @return void
*
* @throws InvalidClaimException
*/
public function __construct($value)
{
$this->setValue($value);
}
/**
* Set the claim value, and call a validate method.
*
* @return $this
*
* @throws InvalidClaimException
*/
public function setValue($value)
{
$this->value = $this->validateCreate($value);
return $this;
}
/**
* Get the claim value.
*/
public function getValue()
{
return $this->value;
}
/**
* Set the claim name.
*
* @param string $name
*
* @return $this
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get the claim name.
*
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* Validate the claim in a standalone Claim context.
*
* @return bool
*/
public function validateCreate($value)
{
return $value;
}
/**
* Validate the Claim within a Payload context.
*
* @return bool
*/
public function validatePayload()
{
return $this->getValue();
}
/**
* Validate the Claim within a refresh context.
*
* @param int $refreshTTL
*
* @return bool
*/
public function validateRefresh($refreshTTL)
{
return $this->getValue();
}
/**
* Checks if the value matches the claim.
*
* @param bool $strict
*
* @return bool
*/
public function matches($value, $strict = true)
{
return $strict ? $this->value === $value : $this->value == $value;
}
/**
* Convert the object into something JSON serializable.
*
* @return array
*/
#[\ReturnTypeWillChange]
public function jsonSerialize()
{
return $this->toArray();
}
/**
* Build a key value array comprising of the claim name and value.
*
* @return array
*/
public function toArray()
{
return [$this->getName() => $this->getValue()];
}
/**
* Get the claim as JSON.
*
* @param int $options
*
* @return string
*/
public function toJson($options = JSON_UNESCAPED_SLASHES)
{
return json_encode($this->toArray(), $options);
}
/**
* Get the payload as a string.
*
* @return string
*/
public function __toString()
{
return $this->toJson();
}
}
@@ -0,0 +1,121 @@
<?php
/*
* This file is part of jwt-auth.
*
* (c) 2014-2021 Sean Tymon <tymon148@gmail.com>
* (c) 2021 PHP Open Source Saver
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPOpenSourceSaver\JWTAuth\Claims;
use Illuminate\Support\Collection as IlluminateCollection;
use Illuminate\Support\Str;
class Collection extends IlluminateCollection
{
/**
* Create a new collection.
*
* @return void
*/
public function __construct($items = [])
{
parent::__construct($this->getArrayableItems($items));
}
/**
* Get a Claim instance by it's unique name.
*
* @param string $name
*
* @return Claim
*/
public function getByClaimName($name, ?callable $callback = null, $default = null)
{
return $this->filter(function (Claim $claim) use ($name) {
return $claim->getName() === $name;
})->first($callback, $default);
}
/**
* Validate each claim under a given context.
*
* @param string $context
*
* @return $this
*/
public function validate($context = 'payload')
{
$args = func_get_args();
array_shift($args);
$this->each(function ($claim) use ($context, $args) {
call_user_func_array(
[$claim, 'validate'.Str::ucfirst($context)],
$args
);
});
return $this;
}
/**
* Determine if the Collection contains all of the given keys.
*
* @return bool
*/
public function hasAllClaims($claims)
{
if (!count($claims)) {
return false;
}
foreach ($claims as $claim) {
if (!$this->has($claim)) {
return false;
}
}
return true;
}
/**
* Get the claims as key/val array.
*
* @return array
*/
public function toPlainArray()
{
return $this->map(function (Claim $claim) {
return $claim->getValue();
})->toArray();
}
protected function getArrayableItems($items)
{
return $this->sanitizeClaims($items);
}
/**
* Ensure that the given claims array is keyed by the claim name.
*
* @return array
*/
private function sanitizeClaims($items)
{
$claims = [];
foreach ($items as $key => $value) {
if (!is_string($key) && $value instanceof Claim) {
$key = $value->getName();
}
$claims[$key] = $value;
}
return $claims;
}
}
@@ -0,0 +1,31 @@
<?php
/*
* This file is part of jwt-auth.
*
* (c) 2014-2021 Sean Tymon <tymon148@gmail.com>
* (c) 2021 PHP Open Source Saver
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPOpenSourceSaver\JWTAuth\Claims;
use PHPOpenSourceSaver\JWTAuth\Exceptions\InvalidClaimException;
class Custom extends Claim
{
/**
* @param string $name
*
* @return void
*
* @throws InvalidClaimException
*/
public function __construct($name, $value)
{
parent::__construct($value);
$this->setName($name);
}
}
@@ -0,0 +1,89 @@
<?php
/*
* This file is part of jwt-auth.
*
* (c) 2014-2021 Sean Tymon <tymon148@gmail.com>
* (c) 2021 PHP Open Source Saver
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPOpenSourceSaver\JWTAuth\Claims;
use PHPOpenSourceSaver\JWTAuth\Exceptions\InvalidClaimException;
use PHPOpenSourceSaver\JWTAuth\Support\Utils;
trait DatetimeTrait
{
/**
* Time leeway in seconds.
*
* @var int
*/
protected $leeway = 0;
/**
* Set the claim value, and call a validate method.
*
* @return $this
*
* @throws InvalidClaimException
*/
public function setValue($value)
{
if ($value instanceof \DateInterval) {
$value = Utils::now()->add($value);
}
if ($value instanceof \DateTimeInterface) {
$value = $value->getTimestamp();
}
return parent::setValue($value);
}
public function validateCreate($value)
{
if (!is_numeric($value)) {
throw new InvalidClaimException($this);
}
return $value;
}
/**
* Determine whether the value is in the future.
*
* @return bool
*/
protected function isFuture($value)
{
return Utils::isFuture($value, $this->leeway);
}
/**
* Determine whether the value is in the past.
*
* @return bool
*/
protected function isPast($value)
{
return Utils::isPast($value, $this->leeway);
}
/**
* Set the leeway in seconds.
*
* @param int $leeway
*
* @return $this
*/
public function setLeeway($leeway)
{
$this->leeway = $leeway;
return $this;
}
}
@@ -0,0 +1,31 @@
<?php
/*
* This file is part of jwt-auth.
*
* (c) 2014-2021 Sean Tymon <tymon148@gmail.com>
* (c) 2021 PHP Open Source Saver
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPOpenSourceSaver\JWTAuth\Claims;
use PHPOpenSourceSaver\JWTAuth\Exceptions\TokenExpiredException;
class Expiration extends Claim
{
use DatetimeTrait;
protected $name = 'exp';
public function validatePayload()
{
if ($this->isPast($this->getValue())) {
throw new TokenExpiredException('Token has expired');
}
return true;
}
}
@@ -0,0 +1,230 @@
<?php
/*
* This file is part of jwt-auth.
*
* (c) 2014-2021 Sean Tymon <tymon148@gmail.com>
* (c) 2021 PHP Open Source Saver
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPOpenSourceSaver\JWTAuth\Claims;
use Illuminate\Http\Request;
use Illuminate\Support\Str;
use PHPOpenSourceSaver\JWTAuth\Exceptions\InvalidClaimException;
use PHPOpenSourceSaver\JWTAuth\Support\Utils;
class Factory
{
/**
* The request.
*
* @var Request
*/
protected $request;
/**
* The TTL.
*
* @var int|null
*/
protected $ttl = 60;
/**
* Time leeway in seconds.
*
* @var int
*/
protected $leeway = 0;
/**
* The classes map.
*
* @var array
*/
private $classMap = [
'aud' => Audience::class,
'exp' => Expiration::class,
'iat' => IssuedAt::class,
'iss' => Issuer::class,
'jti' => JwtId::class,
'nbf' => NotBefore::class,
'sub' => Subject::class,
];
/**
* Constructor.
*
* @return void
*/
public function __construct(Request $request)
{
$this->request = $request;
}
/**
* Get the instance of the claim when passing the name and value.
*
* @param string $name
*
* @return Claim
*
* @throws InvalidClaimException
*/
public function get($name, $value)
{
if ($this->has($name)) {
$claim = new $this->classMap[$name]($value);
return method_exists($claim, 'setLeeway') ?
$claim->setLeeway($this->leeway) :
$claim;
}
return new Custom($name, $value);
}
/**
* Check whether the claim exists.
*
* @param string $name
*
* @return bool
*/
public function has($name)
{
return array_key_exists($name, $this->classMap);
}
/**
* Generate the initial value and return the Claim instance.
*
* @param string $name
*
* @return Claim
*
* @throws InvalidClaimException
*/
public function make($name)
{
return $this->get($name, $this->$name());
}
/**
* Get the Issuer (iss) claim.
*
* @return string
*/
public function iss()
{
return $this->request->url();
}
/**
* Get the Issued At (iat) claim.
*
* @return int
*/
public function iat()
{
return Utils::now()->getTimestamp();
}
/**
* Get the Expiration (exp) claim.
*
* @return int
*/
public function exp()
{
return Utils::now()->addMinutes($this->ttl)->getTimestamp();
}
/**
* Get the Not Before (nbf) claim.
*
* @return int
*/
public function nbf()
{
return Utils::now()->getTimestamp();
}
/**
* Get the JWT Id (jti) claim.
*
* @return string
*/
public function jti()
{
return Str::random();
}
/**
* Add a new claim mapping.
*
* @param string $name
* @param string $classPath
*
* @return $this
*/
public function extend($name, $classPath)
{
$this->classMap[$name] = $classPath;
return $this;
}
/**
* Set the request instance.
*
* @return $this
*/
public function setRequest(Request $request)
{
$this->request = $request;
return $this;
}
/**
* Set the token ttl (in minutes).
*
* @param int|null $ttl
*
* @return $this
*/
public function setTTL($ttl)
{
$this->ttl = $ttl ? (int) $ttl : $ttl;
return $this;
}
/**
* Get the token ttl.
*
* @return int|null
*/
public function getTTL()
{
return $this->ttl;
}
/**
* Set the leeway in seconds.
*
* @param int $leeway
*
* @return $this
*/
public function setLeeway($leeway)
{
$this->leeway = $leeway;
return $this;
}
}
@@ -0,0 +1,55 @@
<?php
/*
* This file is part of jwt-auth.
*
* (c) 2014-2021 Sean Tymon <tymon148@gmail.com>
* (c) 2021 PHP Open Source Saver
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPOpenSourceSaver\JWTAuth\Claims;
use PHPOpenSourceSaver\JWTAuth\Exceptions\InvalidClaimException;
use PHPOpenSourceSaver\JWTAuth\Exceptions\TokenExpiredException;
use PHPOpenSourceSaver\JWTAuth\Exceptions\TokenInvalidException;
class IssuedAt extends Claim
{
use DatetimeTrait {
validateCreate as commonValidateCreate;
}
protected $name = 'iat';
public function validateCreate($value)
{
$this->commonValidateCreate($value);
if ($this->isFuture($value)) {
throw new InvalidClaimException($this);
}
return $value;
}
public function validatePayload()
{
if ($this->isFuture($this->getValue())) {
throw new TokenInvalidException('Issued At (iat) timestamp cannot be in the future');
}
return true;
}
public function validateRefresh($refreshTTL)
{
if ($this->isPast($this->getValue() + $refreshTTL * 60)) {
throw new TokenExpiredException('Token has expired and can no longer be refreshed');
}
return true;
}
}
@@ -0,0 +1,18 @@
<?php
/*
* This file is part of jwt-auth.
*
* (c) 2014-2021 Sean Tymon <tymon148@gmail.com>
* (c) 2021 PHP Open Source Saver
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPOpenSourceSaver\JWTAuth\Claims;
class Issuer extends Claim
{
protected $name = 'iss';
}
@@ -0,0 +1,18 @@
<?php
/*
* This file is part of jwt-auth.
*
* (c) 2014-2021 Sean Tymon <tymon148@gmail.com>
* (c) 2021 PHP Open Source Saver
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPOpenSourceSaver\JWTAuth\Claims;
class JwtId extends Claim
{
protected $name = 'jti';
}
@@ -0,0 +1,34 @@
<?php
/*
* This file is part of jwt-auth.
*
* (c) 2014-2021 Sean Tymon <tymon148@gmail.com>
* (c) 2021 PHP Open Source Saver
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPOpenSourceSaver\JWTAuth\Claims;
use PHPOpenSourceSaver\JWTAuth\Exceptions\TokenInvalidException;
class NotBefore extends Claim
{
use DatetimeTrait;
protected $name = 'nbf';
/**
* @throws TokenInvalidException
*/
public function validatePayload()
{
if ($this->isFuture($this->getValue())) {
throw new TokenInvalidException('Not Before (nbf) timestamp cannot be in the future');
}
return true;
}
}
@@ -0,0 +1,18 @@
<?php
/*
* This file is part of jwt-auth.
*
* (c) 2014-2021 Sean Tymon <tymon148@gmail.com>
* (c) 2021 PHP Open Source Saver
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPOpenSourceSaver\JWTAuth\Claims;
class Subject extends Claim
{
protected $name = 'sub';
}
@@ -0,0 +1,86 @@
<?php
/*
* This file is part of jwt-auth.
*
* (c) 2014-2021 Sean Tymon <tymon148@gmail.com>
* (c) 2021 PHP Open Source Saver
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPOpenSourceSaver\JWTAuth\Console;
use Illuminate\Support\Str;
trait EnvHelperTrait
{
/**
* Checks if the env file exists.
*/
protected function envFileExists(): bool
{
return file_exists($this->envPath());
}
/**
* Update an env-file entry.
*
* @param string|int $value
*/
public function updateEnvEntry(string $key, $value, ?\Closure $confirmOnExisting = null): bool
{
$filepath = $this->envPath();
$filecontents = $this->getFileContents($filepath);
if (false === Str::contains($filecontents, $key)) {
// create new entry
$this->putFileContents(
$filepath,
$filecontents.PHP_EOL."{$key}={$value}".PHP_EOL
);
return true;
} else {
if (is_null($confirmOnExisting) || $confirmOnExisting()) {
// update existing entry
$this->putFileContents(
$filepath,
preg_replace(
"/{$key}=.*/",
"{$key}={$value}",
$filecontents
)
);
return true;
}
}
return false;
}
protected function getFileContents(string $filepath): string
{
return file_get_contents($filepath);
}
protected function putFileContents(string $filepath, string $data): void
{
file_put_contents($filepath, $data);
}
/**
* Get the .env file path.
*/
protected function envPath(): string
{
if (method_exists($this->laravel, 'environmentFilePath')) {
return $this->laravel->environmentFilePath();
}
return $this->laravel->basePath('.env');
}
}
@@ -0,0 +1,139 @@
<?php
/*
* This file is part of jwt-auth.
*
* (c) 2014-2021 Sean Tymon <tymon148@gmail.com>
* (c) 2021 PHP Open Source Saver
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPOpenSourceSaver\JWTAuth\Console;
use Illuminate\Console\Command;
class JWTGenerateCertCommand extends Command
{
use EnvHelperTrait;
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'jwt:generate-certs
{--force : Override certificates if existing}
{--algo= : Algorithm (rsa/ec)}
{--bits= : RSA-Key length (1024,2048,4096,8192}
{--sha= : SHA-variant (1,224,256,384,512)}
{--dir= : Directory where the certificates should be placed}
{--curve= : EC-Curvename (e.g. secp384r1, prime256v1 )}
{--passphrase= : Passphrase}
{--ask-passphrase : Enter passphrase instead passing as argument}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Generates a new cert pair';
/**
* Execute the console command.
*
* @return int
*/
public function handle()
{
$force = $this->option('force');
$directory = $this->option('dir') ? $this->option('dir') : 'storage/certs';
$algo = $this->option('algo') ? $this->option('algo') : 'rsa';
$bits = $this->option('bits') ? intval($this->option('bits')) : 4096;
$shaVariant = $this->option('sha') ? intval($this->option('sha')) : 512;
$curve = $this->option('curve') ? $this->option('curve') : 'prime256v1';
if ($this->option('ask-passphrase')) {
$passphrase = $this->secret('Passphrase');
} else {
$passphrase = $this->option('passphrase') ? $this->option('passphrase') : null;
}
$filenamePublic = sprintf('%s/jwt-%s-%d-public.pem', $directory, $algo, $bits);
$filenamePrivate = sprintf('%s/jwt-%s-%d-private.pem', $directory, $algo, $bits);
if (true === file_exists($filenamePrivate)) {
$this->warn('Private cert already exists');
if (!$force) {
$this->warn('Aborting');
return;
}
}
if (true === file_exists($filenamePublic)) {
$this->warn('Public cert already exists');
if (!$force) {
$this->warn('Aborting');
return;
}
}
switch ($algo) {
case 'rsa':
$keyType = OPENSSL_KEYTYPE_RSA;
$algoIdentifier = sprintf('RS%d', $shaVariant);
break;
case 'ec':
$keyType = OPENSSL_KEYTYPE_EC;
$algoIdentifier = sprintf('ES%d', $shaVariant);
break;
default:
$this->error('Unknown algorithm');
return -1;
}
// Create the private and public key
$res = openssl_pkey_new([
'digest_alg' => sprintf('sha%d', $shaVariant),
'private_key_bits' => $bits,
'private_key_type' => $keyType,
'curve_name' => $curve,
]);
// Extract the private key from $res to $privKey
openssl_pkey_export($res, $privKey, $passphrase);
// Extract the public key from $res to $pubKey
$pubKey = openssl_pkey_get_details($res);
$pubKey = $pubKey['key'];
// save certificates to disk
if (false === is_dir($directory)) {
mkdir($directory, 0777, true);
}
file_put_contents($filenamePrivate, $privKey);
file_put_contents($filenamePublic, $pubKey);
// Updated .env-file
if (!$this->envFileExists()) {
$this->error('.env file missing');
return -1;
}
$this->updateEnvEntry('JWT_ALGO', $algoIdentifier);
$this->updateEnvEntry('JWT_PRIVATE_KEY', sprintf('file://../%s', $filenamePrivate));
$this->updateEnvEntry('JWT_PUBLIC_KEY', sprintf('file://../%s', $filenamePublic));
$this->updateEnvEntry('JWT_PASSPHRASE', $passphrase ?? '');
return 0;
}
}
@@ -0,0 +1,107 @@
<?php
/*
* This file is part of jwt-auth.
*
* (c) 2014-2021 Sean Tymon <tymon148@gmail.com>
* (c) 2021 PHP Open Source Saver
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPOpenSourceSaver\JWTAuth\Console;
use Illuminate\Console\Command;
use Illuminate\Support\Str;
class JWTGenerateSecretCommand extends Command
{
use EnvHelperTrait;
/**
* The console command signature.
*
* @var string
*/
protected $signature = 'jwt:secret
{--s|show : Display the key instead of modifying files.}
{--always-no : Skip generating key if it already exists.}
{--f|force : Skip confirmation when overwriting an existing key.}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Set the JWTAuth secret key used to sign the tokens';
/**
* Execute the console command.
*
* @return void
*/
public function handle()
{
$key = Str::random(64);
if ($this->option('show')) {
$this->comment($key);
return;
}
if (!$this->envFileExists()) {
$this->displayKey($key);
return;
}
$updated = $this->updateEnvEntry('JWT_SECRET', $key, function () {
if ($this->option('always-no')) {
$this->comment('Secret key already exists. Skipping...');
return false;
}
if (false === $this->isConfirmed()) {
$this->comment('Phew... No changes were made to your secret key.');
return false;
}
return true;
});
if ($updated) {
$this->updateEnvEntry('JWT_ALGO', 'HS256');
$this->info('jwt-auth secret set successfully.');
}
}
/**
* Display the key.
*
* @param string $key
*
* @return void
*/
protected function displayKey($key)
{
$this->laravel['config']['jwt.secret'] = $key;
$this->info("jwt-auth secret [$key] set successfully.");
}
/**
* Check if the modification is confirmed.
*
* @return bool
*/
protected function isConfirmed()
{
return $this->option('force') ? true : $this->confirm(
'This will invalidate all existing tokens. Are you sure you want to override the secret key?'
);
}
}
@@ -0,0 +1,55 @@
<?php
/*
* This file is part of jwt-auth.
*
* (c) 2014-2021 Sean Tymon <tymon148@gmail.com>
* (c) 2021 PHP Open Source Saver
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPOpenSourceSaver\JWTAuth\Contracts;
use PHPOpenSourceSaver\JWTAuth\Exceptions\InvalidClaimException;
interface Claim
{
/**
* Set the claim value, and call a validate method.
*
* @return $this
*
* @throws InvalidClaimException
*/
public function setValue($value);
/**
* Get the claim value.
*/
public function getValue();
/**
* Set the claim name.
*
* @param string $name
*
* @return $this
*/
public function setName($name);
/**
* Get the claim name.
*
* @return string
*/
public function getName();
/**
* Validate the Claim value.
*
* @return bool
*/
public function validateCreate($value);
}
@@ -0,0 +1,25 @@
<?php
/*
* This file is part of jwt-auth.
*
* (c) 2014-2021 Sean Tymon <tymon148@gmail.com>
* (c) 2021 PHP Open Source Saver
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPOpenSourceSaver\JWTAuth\Contracts\Http;
use Illuminate\Http\Request;
interface Parser
{
/**
* Parse the request.
*
* @return string|null
*/
public function parse(Request $request);
}
@@ -0,0 +1,28 @@
<?php
/*
* This file is part of jwt-auth.
*
* (c) 2014-2021 Sean Tymon <tymon148@gmail.com>
* (c) 2021 PHP Open Source Saver
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPOpenSourceSaver\JWTAuth\Contracts;
interface JWTSubject
{
/**
* Get the identifier that will be stored in the subject claim of the JWT.
*/
public function getJWTIdentifier();
/**
* Return a key value array, containing any custom claims to be added to the JWT.
*
* @return array
*/
public function getJWTCustomClaims();
}
@@ -0,0 +1,31 @@
<?php
/*
* This file is part of jwt-auth.
*
* (c) 2014-2021 Sean Tymon <tymon148@gmail.com>
* (c) 2021 PHP Open Source Saver
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPOpenSourceSaver\JWTAuth\Contracts\Providers;
interface Auth
{
/**
* Check a user's credentials.
*/
public function byCredentials(array $credentials);
/**
* Authenticate a user via the id.
*/
public function byId($id);
/**
* Get the currently authenticated user.
*/
public function user();
}
@@ -0,0 +1,28 @@
<?php
/*
* This file is part of jwt-auth.
*
* (c) 2014-2021 Sean Tymon <tymon148@gmail.com>
* (c) 2021 PHP Open Source Saver
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPOpenSourceSaver\JWTAuth\Contracts\Providers;
interface JWT
{
/**
* @return string
*/
public function encode(array $payload);
/**
* @param string $token
*
* @return array
*/
public function decode($token);
}
@@ -0,0 +1,48 @@
<?php
/*
* This file is part of jwt-auth.
*
* (c) 2014-2021 Sean Tymon <tymon148@gmail.com>
* (c) 2021 PHP Open Source Saver
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPOpenSourceSaver\JWTAuth\Contracts\Providers;
interface Storage
{
/**
* @param string $key
* @param int $minutes
*
* @return void
*/
public function add($key, $value, $minutes);
/**
* @param string $key
*
* @return void
*/
public function forever($key, $value);
/**
* @param string $key
*/
public function get($key);
/**
* @param string $key
*
* @return bool
*/
public function destroy($key);
/**
* @return void
*/
public function flush();
}
@@ -0,0 +1,32 @@
<?php
/*
* This file is part of jwt-auth.
*
* (c) 2014-2021 Sean Tymon <tymon148@gmail.com>
* (c) 2021 PHP Open Source Saver
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPOpenSourceSaver\JWTAuth\Contracts;
interface Validator
{
/**
* Perform some checks on the value.
*
* @return void
*/
public function check($value);
/**
* Helper function to return a boolean.
*
* @param array $value
*
* @return bool
*/
public function isValid($value);
}
@@ -0,0 +1,30 @@
<?php
/*
* This file is part of jwt-auth.
*
* (c) 2014-2021 Sean Tymon <tymon148@gmail.com>
* (c) 2021 PHP Open Source Saver
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPOpenSourceSaver\JWTAuth\Exceptions;
use PHPOpenSourceSaver\JWTAuth\Claims\Claim;
class InvalidClaimException extends JWTException
{
/**
* Constructor.
*
* @param int $code
*
* @return void
*/
public function __construct(Claim $claim, $code = 0, ?\Exception $previous = null)
{
parent::__construct('Invalid value provided for claim ['.$claim->getName().']', $code, $previous);
}
}
@@ -0,0 +1,18 @@
<?php
/*
* This file is part of jwt-auth.
*
* (c) 2014-2021 Sean Tymon <tymon148@gmail.com>
* (c) 2021 PHP Open Source Saver
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPOpenSourceSaver\JWTAuth\Exceptions;
class JWTException extends \Exception
{
protected $message = 'An error occurred';
}
@@ -0,0 +1,17 @@
<?php
/*
* This file is part of jwt-auth.
*
* (c) 2014-2021 Sean Tymon <tymon148@gmail.com>
* (c) 2021 PHP Open Source Saver
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPOpenSourceSaver\JWTAuth\Exceptions;
class PayloadException extends JWTException
{
}
@@ -0,0 +1,17 @@
<?php
/*
* This file is part of jwt-auth.
*
* (c) 2014-2021 Sean Tymon <tymon148@gmail.com>
* (c) 2021 PHP Open Source Saver
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPOpenSourceSaver\JWTAuth\Exceptions;
class SecretMissingException extends JWTException
{
}
@@ -0,0 +1,17 @@
<?php
/*
* This file is part of jwt-auth.
*
* (c) 2014-2021 Sean Tymon <tymon148@gmail.com>
* (c) 2021 PHP Open Source Saver
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPOpenSourceSaver\JWTAuth\Exceptions;
class TokenBlacklistedException extends TokenInvalidException
{
}
@@ -0,0 +1,17 @@
<?php
/*
* This file is part of jwt-auth.
*
* (c) 2014-2021 Sean Tymon <tymon148@gmail.com>
* (c) 2021 PHP Open Source Saver
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPOpenSourceSaver\JWTAuth\Exceptions;
class TokenExpiredException extends JWTException
{
}
@@ -0,0 +1,17 @@
<?php
/*
* This file is part of jwt-auth.
*
* (c) 2014-2021 Sean Tymon <tymon148@gmail.com>
* (c) 2021 PHP Open Source Saver
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPOpenSourceSaver\JWTAuth\Exceptions;
class TokenInvalidException extends JWTException
{
}
@@ -0,0 +1,17 @@
<?php
/*
* This file is part of jwt-auth.
*
* (c) 2014-2021 Sean Tymon <tymon148@gmail.com>
* (c) 2021 PHP Open Source Saver
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPOpenSourceSaver\JWTAuth\Exceptions;
class UserNotDefinedException extends JWTException
{
}
@@ -0,0 +1,28 @@
<?php
/*
* This file is part of jwt-auth.
*
* (c) 2014-2021 Sean Tymon <tymon148@gmail.com>
* (c) 2021 PHP Open Source Saver
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPOpenSourceSaver\JWTAuth\Facades;
use Illuminate\Support\Facades\Facade;
class JWTAuth extends Facade
{
/**
* Get the registered name of the component.
*
* @return string
*/
protected static function getFacadeAccessor()
{
return 'tymon.jwt.auth';
}
}
@@ -0,0 +1,28 @@
<?php
/*
* This file is part of jwt-auth.
*
* (c) 2014-2021 Sean Tymon <tymon148@gmail.com>
* (c) 2021 PHP Open Source Saver
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPOpenSourceSaver\JWTAuth\Facades;
use Illuminate\Support\Facades\Facade;
class JWTFactory extends Facade
{
/**
* Get the registered name of the component.
*
* @return string
*/
protected static function getFacadeAccessor()
{
return 'tymon.jwt.payload.factory';
}
}
@@ -0,0 +1,28 @@
<?php
/*
* This file is part of jwt-auth.
*
* (c) 2014-2021 Sean Tymon <tymon148@gmail.com>
* (c) 2021 PHP Open Source Saver
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPOpenSourceSaver\JWTAuth\Facades;
use Illuminate\Support\Facades\Facade;
class JWTProvider extends Facade
{
/**
* Get the registered name of the component.
*
* @return string
*/
protected static function getFacadeAccessor()
{
return 'tymon.jwt.provider.jwt';
}
}
+252
View File
@@ -0,0 +1,252 @@
<?php
/*
* This file is part of jwt-auth.
*
* (c) 2014-2021 Sean Tymon <tymon148@gmail.com>
* (c) 2021 PHP Open Source Saver
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPOpenSourceSaver\JWTAuth;
use PHPOpenSourceSaver\JWTAuth\Claims\Claim;
use PHPOpenSourceSaver\JWTAuth\Claims\Collection;
use PHPOpenSourceSaver\JWTAuth\Claims\Factory as ClaimFactory;
use PHPOpenSourceSaver\JWTAuth\Support\CustomClaims;
use PHPOpenSourceSaver\JWTAuth\Support\RefreshFlow;
use PHPOpenSourceSaver\JWTAuth\Validators\PayloadValidator;
class Factory
{
use CustomClaims;
use RefreshFlow;
/**
* The claim factory.
*
* @var ClaimFactory
*/
protected $claimFactory;
/**
* The validator.
*
* @var PayloadValidator
*/
protected $validator;
/**
* The default claims.
*
* @var array
*/
protected $defaultClaims = [
'iss',
'iat',
'exp',
'nbf',
'jti',
];
/**
* The claims collection.
*
* @var Collection
*/
protected $claims;
/**
* Constructor.
*
* @return void
*/
public function __construct(ClaimFactory $claimFactory, PayloadValidator $validator)
{
$this->claimFactory = $claimFactory;
$this->validator = $validator;
$this->claims = new Collection();
}
/**
* Create the Payload instance.
*
* @param bool $resetClaims
*
* @return Payload
*/
public function make($resetClaims = false)
{
if ($resetClaims) {
$this->emptyClaims();
}
return $this->withClaims($this->buildClaimsCollection());
}
/**
* Empty the claims collection.
*
* @return $this
*/
public function emptyClaims()
{
$this->claims = new Collection();
return $this;
}
/**
* Add an array of claims to the Payload.
*
* @return $this
*/
protected function addClaims(array $claims)
{
foreach ($claims as $name => $value) {
$this->addClaim($name, $value);
}
return $this;
}
/**
* Add a claim to the Payload.
*
* @param string $name
*
* @return $this
*/
protected function addClaim($name, $value)
{
$this->claims->put($name, $value);
return $this;
}
/**
* Build the default claims.
*
* @return $this
*/
protected function buildClaims()
{
// remove the exp claim if it exists and the ttl is null
if (null === $this->claimFactory->getTTL() && $key = array_search('exp', $this->defaultClaims)) {
unset($this->defaultClaims[$key]);
}
// add the default claims
foreach ($this->defaultClaims as $claim) {
$this->addClaim($claim, $this->claimFactory->make($claim));
}
// add custom claims on top, allowing them to overwrite defaults
return $this->addClaims($this->getCustomClaims());
}
/**
* Build out the Claim DTO's.
*
* @return Collection
*/
protected function resolveClaims()
{
return $this->claims->map(function ($value, $name) {
return $value instanceof Claim ? $value : $this->claimFactory->get($name, $value);
});
}
/**
* Build and get the Claims Collection.
*
* @return Collection
*/
public function buildClaimsCollection()
{
return $this->buildClaims()->resolveClaims();
}
/**
* Get a Payload instance with a claims collection.
*
* @return Payload
*/
public function withClaims(Collection $claims)
{
return new Payload($claims, $this->validator, $this->refreshFlow);
}
/**
* Set the default claims to be added to the Payload.
*
* @return $this
*/
public function setDefaultClaims(array $claims)
{
$this->defaultClaims = $claims;
return $this;
}
/**
* Helper to set the ttl.
*
* @param int|null $ttl
*
* @return $this
*/
public function setTTL($ttl)
{
$this->claimFactory->setTTL($ttl);
return $this;
}
/**
* Helper to get the ttl.
*
* @return int|null
*/
public function getTTL()
{
return $this->claimFactory->getTTL();
}
/**
* Get the default claims.
*
* @return array
*/
public function getDefaultClaims()
{
return $this->defaultClaims;
}
/**
* Get the PayloadValidator instance.
*
* @return PayloadValidator
*/
public function validator()
{
return $this->validator;
}
/**
* Magically add a claim.
*
* @param string $method
* @param array $parameters
*
* @return $this
*/
public function __call($method, $parameters)
{
$this->addClaim($method, $parameters[0]);
return $this;
}
}
@@ -0,0 +1,33 @@
<?php
/*
* This file is part of jwt-auth.
*
* (c) 2014-2021 Sean Tymon <tymon148@gmail.com>
* (c) 2021 PHP Open Source Saver
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPOpenSourceSaver\JWTAuth\Http\Middleware;
use Illuminate\Http\Request;
use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException;
class Authenticate extends BaseMiddleware
{
/**
* Handle an incoming request.
*
* @param Request $request
*
* @throws UnauthorizedHttpException
*/
public function handle($request, \Closure $next)
{
$this->authenticate($request);
return $next($request);
}
}
@@ -0,0 +1,36 @@
<?php
/*
* This file is part of jwt-auth.
*
* (c) 2014-2021 Sean Tymon <tymon148@gmail.com>
* (c) 2021 PHP Open Source Saver
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPOpenSourceSaver\JWTAuth\Http\Middleware;
use Illuminate\Http\Request;
use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException;
class AuthenticateAndRenew extends BaseMiddleware
{
/**
* Handle an incoming request.
*
* @param Request $request
*
* @throws UnauthorizedHttpException
*/
public function handle($request, \Closure $next)
{
$this->authenticate($request);
$response = $next($request);
// Send the refreshed token back to the client.
return $this->setAuthenticationHeader($response);
}
}
@@ -0,0 +1,89 @@
<?php
/*
* This file is part of jwt-auth.
*
* (c) 2014-2021 Sean Tymon <tymon148@gmail.com>
* (c) 2021 PHP Open Source Saver
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPOpenSourceSaver\JWTAuth\Http\Middleware;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use PHPOpenSourceSaver\JWTAuth\Exceptions\JWTException;
use PHPOpenSourceSaver\JWTAuth\JWTAuth;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException;
abstract class BaseMiddleware
{
/**
* The JWT Authenticator.
*/
protected JWTAuth $auth;
/**
* Create a new BaseMiddleware instance.
*
* @return void
*/
public function __construct(JWTAuth $auth)
{
$this->auth = $auth;
}
/**
* Check the request for the presence of a token.
*
* @return void
*
* @throws BadRequestHttpException
*/
public function checkForToken(Request $request)
{
if (!$this->auth->parser()->setRequest($request)->hasToken()) {
throw new UnauthorizedHttpException('jwt-auth', 'Token not provided');
}
}
/**
* Attempt to authenticate a user via the token in the request.
*
* @return void
*
* @throws UnauthorizedHttpException
*/
public function authenticate(Request $request)
{
$this->checkForToken($request);
try {
if (!$this->auth->parseToken()->authenticate()) {
throw new UnauthorizedHttpException('jwt-auth', 'User not found');
}
} catch (JWTException $e) {
throw new UnauthorizedHttpException('jwt-auth', $e->getMessage(), $e, $e->getCode());
}
}
/**
* Set the authentication header.
*
* @param Response|JsonResponse $response
* @param string|null $token
*
* @return Response|JsonResponse
*/
protected function setAuthenticationHeader($response, $token = null)
{
$token = $token ?: $this->auth->refresh();
$response->headers->set('Authorization', 'Bearer '.$token);
return $response;
}
}
@@ -0,0 +1,35 @@
<?php
/*
* This file is part of jwt-auth.
*
* (c) 2014-2021 Sean Tymon <tymon148@gmail.com>
* (c) 2021 PHP Open Source Saver
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPOpenSourceSaver\JWTAuth\Http\Middleware;
use Illuminate\Http\Request;
class Check extends BaseMiddleware
{
/**
* Handle an incoming request.
*
* @param Request $request
*/
public function handle($request, \Closure $next)
{
if ($this->auth->parser()->setRequest($request)->hasToken()) {
try {
$this->auth->parseToken()->authenticate();
} catch (\Exception $e) {
}
}
return $next($request);
}
}
@@ -0,0 +1,43 @@
<?php
/*
* This file is part of jwt-auth.
*
* (c) 2014-2021 Sean Tymon <tymon148@gmail.com>
* (c) 2021 PHP Open Source Saver
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPOpenSourceSaver\JWTAuth\Http\Middleware;
use Illuminate\Http\Request;
use PHPOpenSourceSaver\JWTAuth\Exceptions\JWTException;
use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException;
class RefreshToken extends BaseMiddleware
{
/**
* Handle an incoming request.
*
* @param Request $request
*
* @throws UnauthorizedHttpException
*/
public function handle($request, \Closure $next)
{
$this->checkForToken($request);
try {
$token = $this->auth->parseToken()->refresh();
} catch (JWTException $e) {
throw new UnauthorizedHttpException('jwt-auth', $e->getMessage(), $e, $e->getCode());
}
$response = $next($request);
// Send the refreshed token back to the client.
return $this->setAuthenticationHeader($response, $token);
}
}
@@ -0,0 +1,95 @@
<?php
/*
* This file is part of jwt-auth.
*
* (c) 2014-2021 Sean Tymon <tymon148@gmail.com>
* (c) 2021 PHP Open Source Saver
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPOpenSourceSaver\JWTAuth\Http\Parser;
use Illuminate\Http\Request;
use PHPOpenSourceSaver\JWTAuth\Contracts\Http\Parser as ParserContract;
class AuthHeaders implements ParserContract
{
/**
* The header name.
*
* @var string
*/
protected $header = 'authorization';
/**
* The header prefix.
*
* @var string
*/
protected $prefix = 'bearer';
/**
* Attempt to parse the token from some other possible headers.
*
* @return string|null
*/
protected function fromAltHeaders(Request $request)
{
return $request->server->get('HTTP_AUTHORIZATION') ?: $request->server->get('REDIRECT_HTTP_AUTHORIZATION');
}
/**
* Try to parse the token from the request header.
*
* @return string|null
*/
public function parse(Request $request)
{
$header = $request->headers->get($this->header) ?: $this->fromAltHeaders($request);
if (null !== $header) {
$position = strripos($header, $this->prefix);
if (false !== $position) {
$header = substr($header, $position + strlen($this->prefix));
return trim(
false !== strpos($header, ',') ? strstr($header, ',', true) : $header
);
}
}
return null;
}
/**
* Set the header name.
*
* @param string $headerName
*
* @return $this
*/
public function setHeaderName($headerName)
{
$this->header = $headerName;
return $this;
}
/**
* Set the header prefix.
*
* @param string $headerPrefix
*
* @return $this
*/
public function setHeaderPrefix($headerPrefix)
{
$this->prefix = $headerPrefix;
return $this;
}
}
@@ -0,0 +1,56 @@
<?php
/*
* This file is part of jwt-auth.
*
* (c) 2014-2021 Sean Tymon <tymon148@gmail.com>
* (c) 2021 PHP Open Source Saver
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPOpenSourceSaver\JWTAuth\Http\Parser;
use Illuminate\Contracts\Encryption\DecryptException;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Crypt;
use PHPOpenSourceSaver\JWTAuth\Contracts\Http\Parser as ParserContract;
use PHPOpenSourceSaver\JWTAuth\Exceptions\TokenInvalidException;
class Cookies implements ParserContract
{
use KeyTrait;
/**
* Decrypt or not the cookie while parsing.
*
* @var bool
*/
private $decrypt;
public function __construct($decrypt = true)
{
$this->decrypt = $decrypt;
}
/**
* Try to parse the token from the request cookies.
*
* @return string|null
*
* @throws TokenInvalidException
*/
public function parse(Request $request)
{
if ($this->decrypt && $request->hasCookie($this->key)) {
try {
return Crypt::decrypt($request->cookie($this->key));
} catch (DecryptException $ex) {
throw new TokenInvalidException('Token has not decrypted successfully.');
}
}
return $request->cookie($this->key);
}
}
@@ -0,0 +1,31 @@
<?php
/*
* This file is part of jwt-auth.
*
* (c) 2014-2021 Sean Tymon <tymon148@gmail.com>
* (c) 2021 PHP Open Source Saver
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPOpenSourceSaver\JWTAuth\Http\Parser;
use Illuminate\Http\Request;
use PHPOpenSourceSaver\JWTAuth\Contracts\Http\Parser as ParserContract;
class InputSource implements ParserContract
{
use KeyTrait;
/**
* Try to parse the token from the request input source.
*
* @return string|null
*/
public function parse(Request $request)
{
return $request->input($this->key);
}
}
@@ -0,0 +1,47 @@
<?php
/*
* This file is part of jwt-auth.
*
* (c) 2014-2021 Sean Tymon <tymon148@gmail.com>
* (c) 2021 PHP Open Source Saver
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPOpenSourceSaver\JWTAuth\Http\Parser;
trait KeyTrait
{
/**
* The key.
*
* @var string
*/
protected $key = 'token';
/**
* Set the key.
*
* @param string $key
*
* @return $this
*/
public function setKey($key)
{
$this->key = $key;
return $this;
}
/**
* Get the key.
*
* @return string
*/
public function getKey()
{
return $this->key;
}
}
@@ -0,0 +1,32 @@
<?php
/*
* This file is part of jwt-auth.
*
* (c) 2014-2021 Sean Tymon <tymon148@gmail.com>
* (c) 2021 PHP Open Source Saver
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPOpenSourceSaver\JWTAuth\Http\Parser;
use Illuminate\Http\Request;
use Illuminate\Support\Arr;
class LumenRouteParams extends RouteParams
{
/**
* Try to get the token from the route parameters.
*
* @return string|null
*/
public function parse(Request $request)
{
// WARNING: Only use this parser if you know what you're doing!
// It will only work with poorly-specified aspects of certain Lumen releases.
// Route is the expected kind of array, and has a parameter with the key we want.
return Arr::get($request->route(), '2.'.$this->key);
}
}
@@ -0,0 +1,122 @@
<?php
/*
* This file is part of jwt-auth.
*
* (c) 2014-2021 Sean Tymon <tymon148@gmail.com>
* (c) 2021 PHP Open Source Saver
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPOpenSourceSaver\JWTAuth\Http\Parser;
use Illuminate\Http\Request;
class Parser
{
/**
* The chain.
*/
private array $chain;
/**
* The request.
*/
protected Request $request;
/**
* Constructor.
*
* @return void
*/
public function __construct(Request $request, array $chain = [])
{
$this->request = $request;
$this->chain = $chain;
}
/**
* Get the parser chain.
*
* @return array
*/
public function getChain()
{
return $this->chain;
}
/**
* Add a new parser to the chain.
*
* @param array|\PHPOpenSourceSaver\JWTAuth\Contracts\Http\Parser $parsers
*
* @return $this
*/
public function addParser($parsers)
{
$this->chain = array_merge($this->chain, is_array($parsers) ? $parsers : [$parsers]);
return $this;
}
/**
* Set the order of the parser chain.
*
* @return $this
*/
public function setChain(array $chain)
{
$this->chain = $chain;
return $this;
}
/**
* Alias for setting the order of the chain.
*
* @return $this
*/
public function setChainOrder(array $chain)
{
return $this->setChain($chain);
}
/**
* Iterate through the parsers and attempt to retrieve
* a value, otherwise return null.
*
* @return string|null
*/
public function parseToken()
{
foreach ($this->chain as $parser) {
if ($response = $parser->parse($this->request)) {
return $response;
}
}
}
/**
* Check whether a token exists in the chain.
*
* @return bool
*/
public function hasToken()
{
return null !== $this->parseToken();
}
/**
* Set the request instance.
*
* @return $this
*/
public function setRequest(Request $request)
{
$this->request = $request;
return $this;
}
}
@@ -0,0 +1,31 @@
<?php
/*
* This file is part of jwt-auth.
*
* (c) 2014-2021 Sean Tymon <tymon148@gmail.com>
* (c) 2021 PHP Open Source Saver
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPOpenSourceSaver\JWTAuth\Http\Parser;
use Illuminate\Http\Request;
use PHPOpenSourceSaver\JWTAuth\Contracts\Http\Parser as ParserContract;
class QueryString implements ParserContract
{
use KeyTrait;
/**
* Try to parse the token from the request query string.
*
* @return string|null
*/
public function parse(Request $request)
{
return $request->query($this->key);
}
}
@@ -0,0 +1,38 @@
<?php
/*
* This file is part of jwt-auth.
*
* (c) 2014-2021 Sean Tymon <tymon148@gmail.com>
* (c) 2021 PHP Open Source Saver
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPOpenSourceSaver\JWTAuth\Http\Parser;
use Illuminate\Http\Request;
use PHPOpenSourceSaver\JWTAuth\Contracts\Http\Parser as ParserContract;
class RouteParams implements ParserContract
{
use KeyTrait;
/**
* Try to get the token from the route parameters.
*
* @return string|null
*/
public function parse(Request $request)
{
$route = $request->route();
// Route may not be an instance of Illuminate\Routing\Route
// (it's an array in Lumen <5.2) or not exist at all
// (if the request was never dispatched)
if (is_callable([$route, 'parameter'])) {
return $route->parameter($this->key);
}
}
}
+402
View File
@@ -0,0 +1,402 @@
<?php
/*
* This file is part of jwt-auth.
*
* (c) 2014-2021 Sean Tymon <tymon148@gmail.com>
* (c) 2021 PHP Open Source Saver
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPOpenSourceSaver\JWTAuth;
use Illuminate\Http\Request;
use PHPOpenSourceSaver\JWTAuth\Contracts\JWTSubject;
use PHPOpenSourceSaver\JWTAuth\Exceptions\JWTException;
use PHPOpenSourceSaver\JWTAuth\Http\Parser\Parser;
use PHPOpenSourceSaver\JWTAuth\Support\CustomClaims;
class JWT
{
use CustomClaims;
/**
* The authentication manager.
*
* @var Manager
*/
protected $manager;
/**
* The HTTP parser.
*
* @var Parser
*/
protected $parser;
/**
* The token.
*
* @var Token|null
*/
protected $token;
/**
* Lock the subject.
*
* @var bool
*/
protected $lockSubject = true;
/**
* JWT constructor.
*
* @return void
*/
public function __construct(Manager $manager, Parser $parser)
{
$this->manager = $manager;
$this->parser = $parser;
}
/**
* Generate a token for a given subject.
*
* @return string
*/
public function fromSubject(JWTSubject $subject)
{
$payload = $this->makePayload($subject);
return $this->manager->encode($payload)->get();
}
/**
* Alias to generate a token for a given user.
*
* @return string
*/
public function fromUser(JWTSubject $user)
{
return $this->fromSubject($user);
}
/**
* Refresh an expired token.
*
* @param bool $forceForever
* @param bool $resetClaims
*
* @return string
*/
public function refresh($forceForever = false, $resetClaims = false)
{
$this->requireToken();
return $this->manager->customClaims($this->getCustomClaims())
->refresh($this->token, $forceForever, $resetClaims)
->get();
}
/**
* Invalidate a token (add it to the blacklist).
*
* @param bool $forceForever
*
* @return $this
*/
public function invalidate($forceForever = false)
{
$this->requireToken();
$this->manager->invalidate($this->token, $forceForever);
return $this;
}
/**
* Alias to get the payload, and as a result checks that
* the token is valid i.e. not expired or blacklisted.
*
* @return Payload
*
* @throws JWTException
*/
public function checkOrFail()
{
return $this->getPayload();
}
/**
* Check that the token is valid.
*
* @param bool $getPayload
*
* @return Payload|bool
*/
public function check($getPayload = false)
{
try {
$payload = $this->checkOrFail();
} catch (JWTException $e) {
return false;
}
return $getPayload ? $payload : true;
}
/**
* Get the token.
*
* @return Token|null
*/
public function getToken()
{
if (null === $this->token) {
try {
$this->parseToken();
} catch (JWTException $e) {
$this->token = null;
}
}
return $this->token;
}
/**
* Parse the token from the request.
*
* @return $this
*
* @throws JWTException
*/
public function parseToken()
{
if (!$token = $this->parser->parseToken()) {
throw new JWTException('The token could not be parsed from the request');
}
return $this->setToken($token);
}
/**
* Get the raw Payload instance.
*
* @return Payload
*/
public function getPayload()
{
$this->requireToken();
return $this->manager->decode($this->token);
}
/**
* Alias for getPayload().
*
* @return Payload
*/
public function payload()
{
return $this->getPayload();
}
/**
* Convenience method to get a claim value.
*
* @param string $claim
*/
public function getClaim($claim)
{
return $this->payload()->get($claim);
}
/**
* Create a Payload instance.
*
* @return Payload
*/
public function makePayload(JWTSubject $subject)
{
return $this->factory()->customClaims($this->getClaimsArray($subject))->make();
}
/**
* Build the claims array and return it.
*
* @return array
*/
protected function getClaimsArray(JWTSubject $subject)
{
return array_merge(
$this->getClaimsForSubject($subject),
$subject->getJWTCustomClaims(), // custom claims from JWTSubject method
$this->customClaims // custom claims from inline setter
);
}
/**
* Get the claims associated with a given subject.
*
* @return array
*/
protected function getClaimsForSubject(JWTSubject $subject)
{
return array_merge([
'sub' => $subject->getJWTIdentifier(),
], $this->lockSubject ? ['prv' => $this->hashSubjectModel($subject)] : []);
}
/**
* Hash the subject model and return it.
*
* @param string|object $model
*
* @return string
*/
protected function hashSubjectModel($model)
{
return sha1(is_object($model) ? get_class($model) : $model);
}
/**
* Check if the subject model matches the one saved in the token.
*
* @param string|object $model
*
* @return bool
*/
public function checkSubjectModel($model)
{
if (($prv = $this->payload()->get('prv')) === null) {
return true;
}
return $this->hashSubjectModel($model) === $prv;
}
/**
* Set the token.
*
* @param Token|string $token
*
* @return $this
*/
public function setToken($token)
{
$this->token = $token instanceof Token ? $token : new Token($token);
return $this;
}
/**
* Unset the current token.
*
* @return $this
*/
public function unsetToken()
{
$this->token = null;
return $this;
}
/**
* Ensure that a token is available.
*
* @return void
*
* @throws JWTException
*/
protected function requireToken()
{
if (!$this->token) {
throw new JWTException('A token is required');
}
}
/**
* Set the request instance.
*
* @return $this
*/
public function setRequest(Request $request)
{
$this->parser->setRequest($request);
return $this;
}
/**
* Set whether the subject should be "locked".
*
* @param bool $lock
*
* @return $this
*/
public function lockSubject($lock)
{
$this->lockSubject = $lock;
return $this;
}
/**
* Get the Manager instance.
*
* @return Manager
*/
public function manager()
{
return $this->manager;
}
/**
* Get the Parser instance.
*
* @return Parser
*/
public function parser()
{
return $this->parser;
}
/**
* Get the Payload Factory.
*
* @return Factory
*/
public function factory()
{
return $this->manager->getPayloadFactory();
}
/**
* Get the Blacklist.
*
* @return Blacklist
*/
public function blacklist()
{
return $this->manager->getBlacklist();
}
/**
* Magically call the JWT Manager.
*
* @param string $method
* @param array $parameters
*
* @throws \BadMethodCallException
*/
public function __call($method, $parameters)
{
if (method_exists($this->manager, $method)) {
return call_user_func_array([$this->manager, $method], $parameters);
}
throw new \BadMethodCallException("Method [$method] does not exist.");
}
}
+88
View File
@@ -0,0 +1,88 @@
<?php
/*
* This file is part of jwt-auth.
*
* (c) 2014-2021 Sean Tymon <tymon148@gmail.com>
* (c) 2021 PHP Open Source Saver
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPOpenSourceSaver\JWTAuth;
use PHPOpenSourceSaver\JWTAuth\Contracts\JWTSubject;
use PHPOpenSourceSaver\JWTAuth\Contracts\Providers\Auth;
use PHPOpenSourceSaver\JWTAuth\Http\Parser\Parser;
class JWTAuth extends JWT
{
/**
* The authentication provider.
*
* @var Auth
*/
protected $auth;
/**
* Constructor.
*
* @return void
*/
public function __construct(Manager $manager, Auth $auth, Parser $parser)
{
parent::__construct($manager, $parser);
$this->auth = $auth;
}
/**
* Attempt to authenticate the user and return the token.
*
* @return false|string
*/
public function attempt(array $credentials)
{
if (!$this->auth->byCredentials($credentials)) {
return false;
}
return $this->fromUser($this->user());
}
/**
* Authenticate a user via a token.
*
* @return JWTSubject|false
*/
public function authenticate()
{
$id = $this->getPayload()->get('sub') ?: $this->getPayload()->get('id');
if (!$this->auth->byId($id)) {
return false;
}
return $this->user();
}
/**
* Alias for authenticate().
*
* @return JWTSubject|false
*/
public function toUser()
{
return $this->authenticate();
}
/**
* Get the authenticated user.
*
* @return JWTSubject
*/
public function user()
{
return $this->auth->user();
}
}
+619
View File
@@ -0,0 +1,619 @@
<?php
/*
* This file is part of jwt-auth.
*
* (c) 2014-2021 Sean Tymon <tymon148@gmail.com>
* (c) 2021 PHP Open Source Saver
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPOpenSourceSaver\JWTAuth;
use Illuminate\Auth\Events\Attempting;
use Illuminate\Auth\Events\Authenticated;
use Illuminate\Auth\Events\Failed;
use Illuminate\Auth\Events\Login;
use Illuminate\Auth\Events\Logout;
use Illuminate\Auth\GuardHelpers;
use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Contracts\Auth\Guard;
use Illuminate\Contracts\Auth\UserProvider;
use Illuminate\Contracts\Events\Dispatcher;
use Illuminate\Http\Request;
use Illuminate\Support\Traits\Macroable;
use PHPOpenSourceSaver\JWTAuth\Contracts\JWTSubject;
use PHPOpenSourceSaver\JWTAuth\Exceptions\JWTException;
use PHPOpenSourceSaver\JWTAuth\Exceptions\UserNotDefinedException;
/**
* @mixin JWT
*/
class JWTGuard implements Guard
{
use GuardHelpers {
setUser as guardHelperSetUser;
}
use Macroable {
__call as macroCall;
}
/**
* The user we last attempted to retrieve.
*
* @var Authenticatable
*/
protected $lastAttempted;
/**
* The JWT instance.
*
* @var JWT
*/
protected $jwt;
/**
* The request instance.
*
* @var Request
*/
protected $request;
/**
* The event dispatcher instance.
*
* @var Dispatcher
*/
protected $events;
/**
* The name of the Guard.
*
* @var string
*/
protected $name = 'tymon.jwt';
/**
* Instantiate the class.
*
* @return void
*/
public function __construct(JWT $jwt, UserProvider $provider, Request $request, Dispatcher $eventDispatcher)
{
$this->jwt = $jwt;
$this->provider = $provider;
$this->request = $request;
$this->events = $eventDispatcher;
}
/**
* Get the currently authenticated user.
*
* @return Authenticatable|null
*/
public function user()
{
if (null !== $this->user) {
return $this->user;
}
if (
$this->jwt->setRequest($this->request)->getToken()
&& ($payload = $this->jwt->check(true))
&& $this->validateSubject()
) {
return $this->user = $this->provider->retrieveById($payload['sub']);
}
}
/**
* @return int|string|null
*/
public function getUserId()
{
if (null !== $this->user) {
return $this->user->getAuthIdentifier();
}
if (
$this->jwt->setRequest($this->request)->getToken()
&& ($payload = $this->jwt->check(true))
&& $this->validateSubject()
) {
return $payload['sub'];
}
return null;
}
/**
* Get the ID for the currently authenticated user.
*
* @return int|string|null
*/
public function id()
{
return $this->getUserId();
}
/**
* Get the currently authenticated user or throws an exception.
*
* @return Authenticatable
*
* @throws UserNotDefinedException
*/
public function userOrFail()
{
if (!$user = $this->user()) {
throw new UserNotDefinedException();
}
return $user;
}
/**
* Validate a user's credentials.
*
* @return bool
*/
public function validate(array $credentials = [])
{
return (bool) $this->attempt($credentials, false);
}
/**
* Attempt to authenticate the user using the given credentials and return the token.
*
* @param bool $login
*
* @return bool|string
*/
public function attempt(array $credentials = [], $login = true)
{
$this->lastAttempted = $user = $this->provider->retrieveByCredentials($credentials);
$this->fireAttemptEvent($credentials);
if ($this->hasValidCredentials($user, $credentials)) {
return $login ? $this->login($user) : true;
}
$this->fireFailedEvent($user, $credentials);
return false;
}
/**
* Create a token for a user.
*
* @return string
*/
public function login(JWTSubject $user)
{
$token = $this->jwt->fromUser($user);
$this->setToken($token)->setUser($user);
$this->fireLoginEvent($user);
return $token;
}
/**
* Logout the user, thus invalidating the token.
*
* @param bool $forceForever
*
* @return void
*/
public function logout($forceForever = false)
{
try {
$this->requireToken()->invalidate($forceForever);
} catch (JWTException $e) {
// Proceed with the logout as normal if we can't invalidate the token
}
$this->fireLogoutEvent($this->user);
$this->user = null;
$this->jwt->unsetToken();
}
/**
* Refresh the token.
*
* @param bool $forceForever
* @param bool $resetClaims
*
* @return string
*/
public function refresh($forceForever = false, $resetClaims = false)
{
return $this->requireToken()->refresh($forceForever, $resetClaims);
}
/**
* Invalidate the token.
*
* @param bool $forceForever
*
* @return JWT
*/
public function invalidate($forceForever = false)
{
return $this->requireToken()->invalidate($forceForever);
}
/**
* Create a new token by User id.
*
* @return string|null
*/
public function tokenById($id)
{
if ($user = $this->provider->retrieveById($id)) {
return $this->jwt->fromUser($user);
}
}
/**
* Log a user into the application using their credentials.
*
* @return bool
*/
public function once(array $credentials = [])
{
if ($this->validate($credentials)) {
$this->setUser($this->lastAttempted);
return true;
}
return false;
}
/**
* Log the given User into the application.
*
* @return bool
*/
public function onceUsingId($id)
{
if ($user = $this->provider->retrieveById($id)) {
$this->setUser($user);
return true;
}
return false;
}
/**
* Alias for onceUsingId.
*
* @return bool
*/
public function byId($id)
{
return $this->onceUsingId($id);
}
/**
* Add any custom claims.
*
* @return $this
*/
public function claims(array $claims)
{
$this->jwt->claims($claims);
return $this;
}
/**
* Get the raw Payload instance.
*
* @return Payload
*/
public function getPayload()
{
return $this->requireToken()->getPayload();
}
/**
* Alias for getPayload().
*
* @return Payload
*/
public function payload()
{
return $this->getPayload();
}
/**
* Set the token.
*
* @param Token|string $token
*
* @return $this
*/
public function setToken($token)
{
$this->jwt->setToken($token);
return $this;
}
/**
* Get the token ttl.
*
* @return int|null
*/
public function getTTL()
{
return $this->jwt->factory()->getTTL();
}
/**
* Set the token ttl.
*
* @param int|null $ttl
*
* @return $this
*/
public function setTTL($ttl)
{
$this->jwt->factory()->setTTL($ttl);
return $this;
}
/**
* Get the user provider used by the guard.
*
* @return UserProvider
*/
public function getProvider()
{
return $this->provider;
}
/**
* Set the user provider used by the guard.
*
* @return $this
*/
public function setProvider(UserProvider $provider)
{
$this->provider = $provider;
return $this;
}
/**
* Return the currently cached user.
*
* @return Authenticatable|null
*/
public function getUser()
{
return $this->user;
}
/**
* Set the current user.
*
* @return $this
*/
public function setUser(Authenticatable $user)
{
$result = $this->guardHelperSetUser($user);
$this->fireAuthenticatedEvent($user);
return $result;
}
/**
* Get the current request instance.
*
* @return Request
*/
public function getRequest()
{
return $this->request ?: Request::createFromGlobals();
}
/**
* Set the current request instance.
*
* @return $this
*/
public function setRequest(Request $request)
{
$this->request = $request;
return $this;
}
/**
* Get the last user we attempted to authenticate.
*
* @return Authenticatable
*/
public function getLastAttempted()
{
return $this->lastAttempted;
}
/**
* Determine if the user matches the credentials.
*
* @param array $credentials
*
* @return bool
*/
protected function hasValidCredentials($user, $credentials)
{
$validated = null !== $user && $this->provider->validateCredentials($user, $credentials);
if ($validated) {
$this->fireValidatedEvent($user);
}
return $validated;
}
/**
* Ensure the JWTSubject matches what is in the token.
*
* @return bool
*/
protected function validateSubject()
{
// If the provider doesn't have the necessary method
// to get the underlying model name then allow.
if (!method_exists($this->provider, 'getModel')) {
return true;
}
return $this->jwt->checkSubjectModel($this->provider->getModel());
}
/**
* Ensure that a token is available in the request.
*
* @return JWT
*
* @throws JWTException
*/
protected function requireToken()
{
if (!$this->jwt->setRequest($this->getRequest())->getToken()) {
throw new JWTException('Token could not be parsed from the request.');
}
return $this->jwt;
}
/**
* Fire the attempt event.
*
* @return void
*/
protected function fireAttemptEvent(array $credentials)
{
$this->events->dispatch(new Attempting(
$this->name,
$credentials,
false
));
}
/**
* Fires the validated event.
*
* @param Authenticatable $user
*
* @return void
*/
protected function fireValidatedEvent($user)
{
if (class_exists('Illuminate\Auth\Events\Validated')) {
$this->events->dispatch(
new \Illuminate\Auth\Events\Validated(
$this->name,
$user
)
);
}
}
/**
* Fire the failed authentication attempt event.
*
* @param Authenticatable|null $user
*
* @return void
*/
protected function fireFailedEvent($user, array $credentials)
{
$this->events->dispatch(new Failed(
$this->name,
$user,
$credentials
));
}
/**
* Fire the authenticated event.
*
* @param Authenticatable $user
*
* @return void
*/
protected function fireAuthenticatedEvent($user)
{
$this->events->dispatch(new Authenticated(
$this->name,
$user
));
}
/**
* Fire the login event.
*
* @param Authenticatable $user
* @param bool $remember
*
* @return void
*/
protected function fireLoginEvent($user, $remember = false)
{
$this->events->dispatch(new Login(
$this->name,
$user,
$remember
));
}
/**
* Fire the logout event.
*
* @param Authenticatable $user
* @param bool $remember
*
* @return void
*/
protected function fireLogoutEvent($user, $remember = false)
{
$this->events->dispatch(new Logout(
$this->name,
$user
));
}
/**
* Magically call the JWT instance.
*
* @param string $method
* @param array $parameters
*
* @throws \BadMethodCallException
*/
public function __call($method, $parameters)
{
if (method_exists($this->jwt, $method)) {
return call_user_func_array([$this->jwt, $method], $parameters);
}
if (static::hasMacro($method)) {
return $this->macroCall($method, $parameters);
}
throw new \BadMethodCallException("Method [$method] does not exist.");
}
}
+291
View File
@@ -0,0 +1,291 @@
<?php
/*
* This file is part of jwt-auth.
*
* (c) 2014-2021 Sean Tymon <tymon148@gmail.com>
* (c) 2021 PHP Open Source Saver
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPOpenSourceSaver\JWTAuth;
use PHPOpenSourceSaver\JWTAuth\Contracts\Providers\JWT as JWTContract;
use PHPOpenSourceSaver\JWTAuth\Exceptions\JWTException;
use PHPOpenSourceSaver\JWTAuth\Exceptions\TokenBlacklistedException;
use PHPOpenSourceSaver\JWTAuth\Support\CustomClaims;
use PHPOpenSourceSaver\JWTAuth\Support\RefreshFlow;
use PHPOpenSourceSaver\JWTAuth\Support\Utils;
class Manager
{
use CustomClaims;
use RefreshFlow;
/**
* The provider.
*
* @var JWTContract
*/
protected $provider;
/**
* The blacklist.
*
* @var Blacklist
*/
protected $blacklist;
/**
* the payload factory.
*
* @var Factory
*/
protected $payloadFactory;
/**
* The blacklist flag.
*
* @var bool
*/
protected $blacklistEnabled = true;
/**
* The refresh iat flag.
*
* @var bool
*/
protected $refreshIat = false;
/**
* the persistent claims.
*
* @var array
*/
protected $persistentClaims = [];
/**
* @var bool
*/
protected $showBlackListException = true;
/**
* Constructor.
*
* @return void
*/
public function __construct(JWTContract $provider, Blacklist $blacklist, Factory $payloadFactory)
{
$this->provider = $provider;
$this->blacklist = $blacklist;
$this->payloadFactory = $payloadFactory;
}
/**
* Encode a Payload and return the Token.
*
* @return Token
*/
public function encode(Payload $payload)
{
$token = $this->provider->encode($payload->get());
return new Token($token);
}
/**
* Decode a Token and return the Payload.
*
* @param bool $checkBlacklist
*
* @return Payload
*
* @throws TokenBlacklistedException
*/
public function decode(Token $token, $checkBlacklist = true)
{
$payloadArray = $this->provider->decode($token->get());
$payload = $this->payloadFactory
->setRefreshFlow($this->refreshFlow)
->customClaims($payloadArray)
->make();
if (
$checkBlacklist
&& $this->blacklistEnabled
&& $this->getBlackListExceptionEnabled()
&& $this->blacklist->has($payload)
) {
throw new TokenBlacklistedException('The token has been blacklisted');
}
return $payload;
}
/**
* Refresh a Token and return a new Token.
*
* @param bool $forceForever
* @param bool $resetClaims
*
* @return Token
*/
public function refresh(Token $token, $forceForever = false, $resetClaims = false)
{
$this->setRefreshFlow();
$claims = $this->buildRefreshClaims($this->decode($token));
if ($this->blacklistEnabled) {
// Invalidate old token
$this->invalidate($token, $forceForever);
}
// Return the new token
return $this->encode(
$this->payloadFactory->customClaims($claims)->make($resetClaims)
);
}
/**
* Invalidate a Token by adding it to the blacklist.
*
* @param bool $forceForever
*
* @return bool
*
* @throws JWTException
*/
public function invalidate(Token $token, $forceForever = false)
{
if (!$this->blacklistEnabled) {
throw new JWTException('You must have the blacklist enabled to invalidate a token.');
}
return call_user_func(
[$this->blacklist, $forceForever ? 'addForever' : 'add'],
$this->decode($token, false)
);
}
/**
* Build the claims to go into the refreshed token.
*
* @return array
*/
protected function buildRefreshClaims(Payload $payload)
{
// Get the claims to be persisted from the payload
$persistentClaims = collect($payload->toArray())
->only($this->persistentClaims)
->toArray();
// persist the relevant claims
return array_merge(
$this->customClaims,
$persistentClaims,
[
'sub' => $payload['sub'],
'iat' => $this->refreshIat ? Utils::now()->timestamp : $payload['iat'],
]
);
}
/**
* Get the Payload Factory instance.
*
* @return Factory
*/
public function getPayloadFactory()
{
return $this->payloadFactory;
}
/**
* Get the JWTProvider instance.
*
* @return JWTContract
*/
public function getJWTProvider()
{
return $this->provider;
}
/**
* Get the Blacklist instance.
*
* @return Blacklist
*/
public function getBlacklist()
{
return $this->blacklist;
}
/**
* Set whether the blacklist is enabled.
*
* @param bool $enabled
*
* @return $this
*/
public function setBlacklistEnabled($enabled)
{
$this->blacklistEnabled = $enabled;
return $this;
}
/**
* Configuration to set up if show the TokenBlacklistedException
* can be throwable or not.
*
* @param bool $showBlackListException
*
* @removed this
*/
public function setBlackListExceptionEnabled($showBlackListException = true)
{
$this->showBlackListException = $showBlackListException;
return $this;
}
/**
* Get if the blacklist instance is enabled.
*
* @return bool
*/
public function getBlackListExceptionEnabled()
{
return $this->showBlackListException;
}
/**
* Set the claims to be persisted when refreshing a token.
*
* @return $this
*/
public function setPersistentClaims(array $claims)
{
$this->persistentClaims = $claims;
return $this;
}
/**
* Set whether the refresh iat is enabled.
*
* @param bool $enabled
*
* @return $this
*/
public function setRefreshIat($refreshIat)
{
$this->refreshIat = $refreshIat;
return $this;
}
}
+269
View File
@@ -0,0 +1,269 @@
<?php
/*
* This file is part of jwt-auth.
*
* (c) 2014-2021 Sean Tymon <tymon148@gmail.com>
* (c) 2021 PHP Open Source Saver
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPOpenSourceSaver\JWTAuth;
use Illuminate\Contracts\Support\Arrayable;
use Illuminate\Contracts\Support\Jsonable;
use Illuminate\Support\Arr;
use Illuminate\Support\Str;
use PHPOpenSourceSaver\JWTAuth\Claims\Claim;
use PHPOpenSourceSaver\JWTAuth\Claims\Collection;
use PHPOpenSourceSaver\JWTAuth\Exceptions\PayloadException;
use PHPOpenSourceSaver\JWTAuth\Validators\PayloadValidator;
class Payload implements \ArrayAccess, Arrayable, \Countable, Jsonable, \JsonSerializable
{
/**
* The collection of claims.
*
* @var Collection
*/
private $claims;
/**
* Build the Payload.
*
* @param bool $refreshFlow
*
* @return void
*/
public function __construct(Collection $claims, PayloadValidator $validator, $refreshFlow = false)
{
$this->claims = $validator->setRefreshFlow($refreshFlow)->check($claims);
}
/**
* Get the array of claim instances.
*
* @return Collection
*/
public function getClaims()
{
return $this->claims;
}
/**
* Checks if a payload matches some expected values.
*
* @param bool $strict
*
* @return bool
*/
public function matches(array $values, $strict = false)
{
if (empty($values)) {
return false;
}
$claims = $this->getClaims();
foreach ($values as $key => $value) {
if (!$claims->has($key) || !$claims->get($key)->matches($value, $strict)) {
return false;
}
}
return true;
}
/**
* Checks if a payload strictly matches some expected values.
*
* @return bool
*/
public function matchesStrict(array $values)
{
return $this->matches($values, true);
}
/**
* Get the payload.
*/
public function get($claim = null)
{
$claim = value($claim);
if (null !== $claim) {
if (is_array($claim)) {
return array_map([$this, 'get'], $claim);
}
return Arr::get($this->toArray(), $claim);
}
return $this->toArray();
}
/**
* Get the underlying Claim instance.
*
* @param string $claim
*
* @return Claim
*/
public function getInternal($claim)
{
return $this->claims->getByClaimName($claim);
}
/**
* Determine whether the payload has the claim (by instance).
*
* @return bool
*/
public function has(Claim $claim)
{
return $this->claims->has($claim->getName());
}
/**
* Determine whether the payload has the claim (by key).
*
* @param string $claim
*
* @return bool
*/
public function hasKey($claim)
{
return $this->offsetExists($claim);
}
/**
* Get the array of claims.
*
* @return array
*/
public function toArray()
{
return $this->claims->toPlainArray();
}
/**
* Convert the object into something JSON serializable.
*
* @return array
*/
#[\ReturnTypeWillChange]
public function jsonSerialize()
{
return $this->toArray();
}
/**
* Get the payload as JSON.
*
* @param int $options
*
* @return string
*/
public function toJson($options = JSON_UNESCAPED_SLASHES)
{
return json_encode($this->toArray(), $options);
}
/**
* Get the payload as a string.
*
* @return string
*/
public function __toString()
{
return $this->toJson();
}
/**
* Determine if an item exists at an offset.
*
* @return bool
*/
#[\ReturnTypeWillChange]
public function offsetExists($key)
{
return Arr::has($this->toArray(), $key);
}
/**
* Get an item at a given offset.
*/
#[\ReturnTypeWillChange]
public function offsetGet($key)
{
return Arr::get($this->toArray(), $key);
}
/**
* Don't allow changing the payload as it should be immutable.
*
* @throws PayloadException
*/
#[\ReturnTypeWillChange]
public function offsetSet($key, $value)
{
throw new PayloadException('The payload is immutable');
}
/**
* Don't allow changing the payload as it should be immutable.
*
* @param string $key
*
* @return void
*
* @throws PayloadException
*/
#[\ReturnTypeWillChange]
public function offsetUnset($key)
{
throw new PayloadException('The payload is immutable');
}
/**
* Count the number of claims.
*
* @return int
*/
#[\ReturnTypeWillChange]
public function count()
{
return count($this->toArray());
}
/**
* Invoke the Payload as a callable function.
*/
public function __invoke($claim = null)
{
return $this->get($claim);
}
/**
* Magically get a claim value.
*
* @param string $method
* @param array $parameters
*
* @throws \BadMethodCallException
*/
public function __call($method, $parameters)
{
if (preg_match('/get(.+)\b/i', $method, $matches)) {
foreach ($this->claims as $claim) {
if (get_class($claim) === 'PHPOpenSourceSaver\\JWTAuth\\Claims\\'.$matches[1]) {
return $claim->getValue();
}
}
}
throw new \BadMethodCallException(sprintf('The claim [%s] does not exist on the payload.', Str::after($method, 'get')));
}
}
@@ -0,0 +1,355 @@
<?php
/*
* This file is part of jwt-auth.
*
* (c) 2014-2021 Sean Tymon <tymon148@gmail.com>
* (c) 2021 PHP Open Source Saver
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPOpenSourceSaver\JWTAuth\Providers;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Support\Arr;
use Illuminate\Support\ServiceProvider;
use Namshi\JOSE\JWS;
use PHPOpenSourceSaver\JWTAuth\Blacklist;
use PHPOpenSourceSaver\JWTAuth\Claims\Factory as ClaimFactory;
use PHPOpenSourceSaver\JWTAuth\Console\JWTGenerateCertCommand;
use PHPOpenSourceSaver\JWTAuth\Console\JWTGenerateSecretCommand;
use PHPOpenSourceSaver\JWTAuth\Contracts\Providers\Auth;
use PHPOpenSourceSaver\JWTAuth\Contracts\Providers\JWT as JWTContract;
use PHPOpenSourceSaver\JWTAuth\Contracts\Providers\Storage;
use PHPOpenSourceSaver\JWTAuth\Factory;
use PHPOpenSourceSaver\JWTAuth\Http\Middleware\Authenticate;
use PHPOpenSourceSaver\JWTAuth\Http\Middleware\AuthenticateAndRenew;
use PHPOpenSourceSaver\JWTAuth\Http\Middleware\Check;
use PHPOpenSourceSaver\JWTAuth\Http\Middleware\RefreshToken;
use PHPOpenSourceSaver\JWTAuth\Http\Parser\AuthHeaders;
use PHPOpenSourceSaver\JWTAuth\Http\Parser\InputSource;
use PHPOpenSourceSaver\JWTAuth\Http\Parser\Parser;
use PHPOpenSourceSaver\JWTAuth\Http\Parser\QueryString;
use PHPOpenSourceSaver\JWTAuth\JWT;
use PHPOpenSourceSaver\JWTAuth\JWTAuth;
use PHPOpenSourceSaver\JWTAuth\JWTGuard;
use PHPOpenSourceSaver\JWTAuth\Manager;
use PHPOpenSourceSaver\JWTAuth\Providers\JWT\Lcobucci;
use PHPOpenSourceSaver\JWTAuth\Providers\JWT\Namshi;
use PHPOpenSourceSaver\JWTAuth\Validators\PayloadValidator;
abstract class AbstractServiceProvider extends ServiceProvider
{
/**
* The middleware aliases.
*/
protected array $middlewareAliases = [
'jwt.auth' => Authenticate::class,
'jwt.check' => Check::class,
'jwt.refresh' => RefreshToken::class,
'jwt.renew' => AuthenticateAndRenew::class,
];
/**
* Boot the service provider.
*
* @return void
*/
abstract public function boot();
/**
* Register the service provider.
*
* @return void
*/
public function register()
{
$this->registerAliases();
$this->registerJWTProvider();
$this->registerAuthProvider();
$this->registerStorageProvider();
$this->registerJWTBlacklist();
$this->registerManager();
$this->registerTokenParser();
$this->registerJWT();
$this->registerJWTAuth();
$this->registerPayloadValidator();
$this->registerClaimFactory();
$this->registerPayloadFactory();
$this->registerJWTCommands();
$this->commands([
'tymon.jwt.secret',
'tymon.jwt.cert',
]);
}
/**
* Extend Laravel's Auth.
*
* @return void
*/
protected function extendAuthGuard()
{
$this->app['auth']->extend('jwt', function ($app, $name, array $config) {
$guard = new JWTGuard(
$app['tymon.jwt'],
$app['auth']->createUserProvider($config['provider']),
$app['request'],
$app['events']
);
if (Arr::has($config, 'ttl')) {
$guard->setTTL(Arr::get($config, 'ttl'));
} else {
$guard->setTTL($app->make('config')->get('jwt.ttl'));
}
$app->refresh('request', $guard, 'setRequest');
return $guard;
});
}
/**
* Bind some aliases.
*
* @return void
*/
protected function registerAliases()
{
$this->app->alias('tymon.jwt', JWT::class);
$this->app->alias('tymon.jwt.auth', JWTAuth::class);
$this->app->alias('tymon.jwt.provider.jwt', JWTContract::class);
$this->app->alias('tymon.jwt.provider.jwt.namshi', Namshi::class);
$this->app->alias('tymon.jwt.provider.jwt.lcobucci', Lcobucci::class);
$this->app->alias('tymon.jwt.provider.auth', Auth::class);
$this->app->alias('tymon.jwt.provider.storage', Storage::class);
$this->app->alias('tymon.jwt.manager', Manager::class);
$this->app->alias('tymon.jwt.blacklist', Blacklist::class);
$this->app->alias('tymon.jwt.payload.factory', Factory::class);
$this->app->alias('tymon.jwt.validators.payload', PayloadValidator::class);
}
/**
* Register the bindings for the JSON Web Token provider.
*
* @return void
*/
protected function registerJWTProvider()
{
$this->registerNamshiProvider();
$this->registerLcobucciProvider();
$this->app->singleton('tymon.jwt.provider.jwt', fn ($app) => $this->getConfigInstance($app, 'providers.jwt'));
}
/**
* Register the bindings for the Namshi JWT provider.
*
* @return void
*/
protected function registerNamshiProvider()
{
$this->app->singleton('tymon.jwt.provider.jwt.namshi', fn ($app) => new Namshi(
new JWS(['typ' => 'JWT', 'alg' => $app->make('config')->get('jwt.algo')]),
$app->make('config')->get('jwt.secret'),
$app->make('config')->get('jwt.algo'),
$app->make('config')->get('jwt.keys')
));
}
/**
* Register the bindings for the Lcobucci JWT provider.
*
* @return void
*/
protected function registerLcobucciProvider()
{
$this->app->singleton('tymon.jwt.provider.jwt.lcobucci', fn ($app) => new Lcobucci(
$app->make('config')->get('jwt.secret'),
$app->make('config')->get('jwt.algo'),
$app->make('config')->get('jwt.keys')
));
}
/**
* Register the bindings for the Auth provider.
*
* @return void
*/
protected function registerAuthProvider()
{
$this->app->singleton('tymon.jwt.provider.auth', fn ($app) => $this->getConfigInstance($app, 'providers.auth'));
}
/**
* Register the bindings for the Storage provider.
*
* @return void
*/
protected function registerStorageProvider()
{
$this->app->singleton('tymon.jwt.provider.storage', fn ($app) => $this->getConfigInstance($app, 'providers.storage'));
}
/**
* Register the bindings for the JWT Manager.
*
* @return void
*/
protected function registerManager()
{
$this->app->singleton('tymon.jwt.manager', function ($app) {
$instance = new Manager(
$app['tymon.jwt.provider.jwt'],
$app['tymon.jwt.blacklist'],
$app['tymon.jwt.payload.factory']
);
return $instance->setBlacklistEnabled((bool) $app->make('config')->get('jwt.blacklist_enabled'))
->setRefreshIat((bool) $app->make('config')->get('jwt.refresh_iat', false))
->setPersistentClaims($app->make('config')->get('jwt.persistent_claims'))
->setBlackListExceptionEnabled((bool) $app->make('config')->get('jwt.show_black_list_exception', 0));
});
}
/**
* Register the bindings for the Token Parser.
*
* @return void
*/
protected function registerTokenParser()
{
$this->app->singleton('tymon.jwt.parser', function ($app) {
$parser = new Parser(
$app['request'],
[
new AuthHeaders(),
new QueryString(),
new InputSource(),
]
);
$app->refresh('request', $parser, 'setRequest');
return $parser;
});
}
/**
* Register the bindings for the main JWT class.
*
* @return void
*/
protected function registerJWT()
{
$this->app->singleton('tymon.jwt', fn ($app) => (new JWT(
$app['tymon.jwt.manager'],
$app['tymon.jwt.parser']
))->lockSubject($app->make('config')->get('jwt.lock_subject')));
}
/**
* Register the bindings for the main JWTAuth class.
*
* @return void
*/
protected function registerJWTAuth()
{
$this->app->singleton('tymon.jwt.auth', fn ($app) => (new JWTAuth(
$app['tymon.jwt.manager'],
$app['tymon.jwt.provider.auth'],
$app['tymon.jwt.parser']
))->lockSubject($app->make('config')->get('jwt.lock_subject')));
}
/**
* Register the bindings for the Blacklist.
*
* @return void
*/
protected function registerJWTBlacklist()
{
$this->app->singleton('tymon.jwt.blacklist', function ($app) {
$instance = new Blacklist($app['tymon.jwt.provider.storage']);
return $instance->setGracePeriod($app->make('config')->get('jwt.blacklist_grace_period'))
->setRefreshTTL($app->make('config')->get('jwt.refresh_ttl'));
});
}
/**
* Register the bindings for the payload validator.
*
* @return void
*/
protected function registerPayloadValidator()
{
$this->app->singleton('tymon.jwt.validators.payload', fn ($app) => (new PayloadValidator())
->setRefreshTTL($app->make('config')->get('jwt.refresh_ttl'))
->setRequiredClaims($app->make('config')->get('jwt.required_claims')));
}
/**
* Register the bindings for the Claim Factory.
*
* @return void
*/
protected function registerClaimFactory()
{
$this->app->singleton('tymon.jwt.claim.factory', function ($app) {
$factory = new ClaimFactory($app['request']);
$app->refresh('request', $factory, 'setRequest');
return $factory->setTTL($app->make('config')->get('jwt.ttl'))
->setLeeway($app->make('config')->get('jwt.leeway'));
});
}
/**
* Register the bindings for the Payload Factory.
*
* @return void
*/
protected function registerPayloadFactory()
{
$this->app->singleton('tymon.jwt.payload.factory', fn ($app) => new Factory(
$app['tymon.jwt.claim.factory'],
$app['tymon.jwt.validators.payload']
));
}
/**
* Register the Artisan command.
*
* @return void
*/
protected function registerJWTCommands()
{
$this->app->singleton('tymon.jwt.secret', fn () => new JWTGenerateSecretCommand());
$this->app->singleton('tymon.jwt.cert', fn () => new JWTGenerateCertCommand());
}
/**
* Get an instantiable configuration instance.
*
* @param Application $app
* @param string $key
*/
protected function getConfigInstance($app, $key)
{
$instance = $app->make('config')->get('jwt.'.$key);
if (is_string($instance)) {
return $this->app->make($instance);
}
return $instance;
}
}
@@ -0,0 +1,64 @@
<?php
/*
* This file is part of jwt-auth.
*
* (c) 2014-2021 Sean Tymon <tymon148@gmail.com>
* (c) 2021 PHP Open Source Saver
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPOpenSourceSaver\JWTAuth\Providers\Auth;
use Illuminate\Contracts\Auth\Guard as GuardContract;
use PHPOpenSourceSaver\JWTAuth\Contracts\Providers\Auth;
class Illuminate implements Auth
{
/**
* The authentication guard.
*
* @var GuardContract
*/
protected $auth;
/**
* Constructor.
*
* @return void
*/
public function __construct(GuardContract $auth)
{
$this->auth = $auth;
}
/**
* Check a user's credentials.
*
* @return bool
*/
public function byCredentials(array $credentials)
{
return $this->auth->once($credentials);
}
/**
* Authenticate a user via the id.
*
* @return bool
*/
public function byId($id)
{
return $this->auth->onceUsingId($id);
}
/**
* Get the currently authenticated user.
*/
public function user()
{
return $this->auth->user();
}
}
@@ -0,0 +1,274 @@
<?php
/*
* This file is part of jwt-auth.
*
* (c) 2014-2021 Sean Tymon <tymon148@gmail.com>
* (c) 2021 PHP Open Source Saver
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPOpenSourceSaver\JWTAuth\Providers\JWT;
use Illuminate\Support\Collection;
use Lcobucci\JWT\Builder;
use Lcobucci\JWT\Configuration;
use Lcobucci\JWT\Signer;
use Lcobucci\JWT\Signer\Ecdsa;
use Lcobucci\JWT\Signer\Ecdsa\Sha256 as ES256;
use Lcobucci\JWT\Signer\Ecdsa\Sha384 as ES384;
use Lcobucci\JWT\Signer\Ecdsa\Sha512 as ES512;
use Lcobucci\JWT\Signer\Hmac\Sha256 as HS256;
use Lcobucci\JWT\Signer\Hmac\Sha384 as HS384;
use Lcobucci\JWT\Signer\Hmac\Sha512 as HS512;
use Lcobucci\JWT\Signer\Key;
use Lcobucci\JWT\Signer\Key\InMemory;
use Lcobucci\JWT\Signer\Rsa;
use Lcobucci\JWT\Signer\Rsa\Sha256 as RS256;
use Lcobucci\JWT\Signer\Rsa\Sha384 as RS384;
use Lcobucci\JWT\Signer\Rsa\Sha512 as RS512;
use Lcobucci\JWT\Token\RegisteredClaims;
use Lcobucci\JWT\Validation\Constraint\SignedWith;
use PHPOpenSourceSaver\JWTAuth\Contracts\Providers\JWT;
use PHPOpenSourceSaver\JWTAuth\Exceptions\JWTException;
use PHPOpenSourceSaver\JWTAuth\Exceptions\TokenInvalidException;
class Lcobucci extends Provider implements JWT
{
/**
* The builder instance.
*
* @var Builder
*/
protected $builder;
/**
* The configuration instance.
*
* @var Configuration
*/
protected $config;
/**
* The Signer instance.
*
* @var Signer
*/
protected $signer;
/**
* Create the Lcobucci provider.
*
* @param string $secret
* @param string $algo
* @param Configuration $config optional, to pass an existing configuration to be used
*
* @return void
*/
public function __construct(
$secret,
$algo,
array $keys,
$config = null,
) {
parent::__construct($secret, $algo, $keys);
$this->generateConfig($config);
}
/**
* Generate the config.
*
* @param Configuration $config optional, to pass an existing configuration to be used
*
* @return void
*/
private function generateConfig($config = null)
{
$this->signer = $this->getSigner();
if (!is_null($config)) {
$this->config = $config;
} elseif ($this->isAsymmetric()) {
$this->config = Configuration::forAsymmetricSigner($this->signer, $this->getSigningKey(), $this->getVerificationKey());
} else {
$this->config = Configuration::forSymmetricSigner($this->signer, InMemory::plainText($this->getSecret()));
}
if (!count($this->config->validationConstraints())) {
$this->config->setValidationConstraints(
new SignedWith($this->signer, $this->getVerificationKey()),
);
}
}
/**
* Set the secret used to sign the token and regenerate the config using the secret.
*
* @param string $secret
*
* @return $this
*/
public function setSecret($secret)
{
$this->secret = $secret;
$this->generateConfig();
return $this;
}
/**
* Gets the {@see $config} attribute.
*
* @return Configuration
*/
public function getConfig()
{
return $this->config;
}
/**
* Signers that this provider supports.
*
* @var array
*/
protected $signers = [
'HS256' => HS256::class,
'HS384' => HS384::class,
'HS512' => HS512::class,
'RS256' => RS256::class,
'RS384' => RS384::class,
'RS512' => RS512::class,
'ES256' => ES256::class,
'ES384' => ES384::class,
'ES512' => ES512::class,
];
/**
* Create a JSON Web Token.
*
* @return string
*
* @throws JWTException
*/
public function encode(array $payload)
{
$this->builder = null;
$this->builder = $this->config->builder();
try {
foreach ($payload as $key => $value) {
$this->builder = $this->addClaim($key, $value);
}
return $this->builder->getToken($this->config->signer(), $this->config->signingKey())->toString();
} catch (\Exception $e) {
throw new JWTException('Could not create token: '.$e->getMessage(), $e->getCode(), $e);
}
}
/**
* Decode a JSON Web Token.
*
* @param string $token
*
* @return array
*
* @throws JWTException
*/
public function decode($token)
{
try {
$jwt = $this->config->parser()->parse($token);
} catch (\Exception $e) {
throw new TokenInvalidException('Could not decode token: '.$e->getMessage(), $e->getCode(), $e);
}
if (!$this->config->validator()->validate($jwt, ...$this->config->validationConstraints())) {
throw new TokenInvalidException('Token Signature could not be verified.');
}
return (new Collection($jwt->claims()->all()))->map(function ($claim) {
if (is_a($claim, \DateTimeImmutable::class)) {
return $claim->getTimestamp();
}
if (is_object($claim) && method_exists($claim, 'getValue')) {
return $claim->getValue();
}
return $claim;
})->toArray();
}
/**
* Adds a claim to the {@see $config}.
*
* @param string $key
*/
protected function addClaim($key, $value): Builder
{
if (!isset($this->builder)) {
$this->builder = $this->config->builder();
}
return match ($key) {
RegisteredClaims::ID => $this->builder->identifiedBy($value),
RegisteredClaims::EXPIRATION_TIME => $this->builder->expiresAt(\DateTimeImmutable::createFromFormat('U', $value)),
RegisteredClaims::NOT_BEFORE => $this->builder->canOnlyBeUsedAfter(\DateTimeImmutable::createFromFormat('U', $value)),
RegisteredClaims::ISSUED_AT => $this->builder->issuedAt(\DateTimeImmutable::createFromFormat('U', $value)),
RegisteredClaims::ISSUER => $this->builder->issuedBy($value),
RegisteredClaims::AUDIENCE => is_array($value) ? $this->builder->permittedFor(...$value) : $this->builder->permittedFor($value),
RegisteredClaims::SUBJECT => $this->builder->relatedTo($value),
default => $this->builder->withClaim($key, $value),
};
}
/**
* Get the signer instance.
*
* @return Signer
*
* @throws JWTException
*/
protected function getSigner()
{
if (!array_key_exists($this->algo, $this->signers)) {
throw new JWTException('The given algorithm could not be found');
}
$signer = $this->signers[$this->algo];
return new $signer();
}
protected function isAsymmetric()
{
$reflect = new \ReflectionClass($this->signer);
return $reflect->isSubclassOf(Rsa::class) || $reflect->isSubclassOf(Ecdsa::class);
}
/**
* Get the key used to sign the tokens.
*
* @return Key|string
*/
protected function getSigningKey()
{
return $this->isAsymmetric() ?
InMemory::plainText($this->getPrivateKey(), $this->getPassphrase() ?? '') :
InMemory::plainText($this->getSecret());
}
/**
* Get the key used to verify the tokens.
*
* @return Key|string
*/
protected function getVerificationKey()
{
return $this->isAsymmetric() ?
InMemory::plainText($this->getPublicKey()) :
InMemory::plainText($this->getSecret());
}
}
@@ -0,0 +1,96 @@
<?php
/*
* This file is part of jwt-auth.
*
* (c) 2014-2021 Sean Tymon <tymon148@gmail.com>
* (c) 2021 PHP Open Source Saver
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPOpenSourceSaver\JWTAuth\Providers\JWT;
use Namshi\JOSE\JWS;
use Namshi\JOSE\Signer\OpenSSL\PublicKey;
use PHPOpenSourceSaver\JWTAuth\Contracts\Providers\JWT;
use PHPOpenSourceSaver\JWTAuth\Exceptions\JWTException;
use PHPOpenSourceSaver\JWTAuth\Exceptions\TokenInvalidException;
class Namshi extends Provider implements JWT
{
/**
* The JWS.
*
* @var JWS
*/
protected $jws;
/**
* Constructor.
*
* @param string $secret
* @param string $algo
*
* @return void
*/
public function __construct(JWS $jws, $secret, $algo, array $keys)
{
parent::__construct($secret, $algo, $keys);
$this->jws = $jws;
}
/**
* Create a JSON Web Token.
*
* @return string
*
* @throws JWTException
*/
public function encode(array $payload)
{
try {
$this->jws->setPayload($payload)->sign($this->getSigningKey(), $this->getPassphrase());
return (string) $this->jws->getTokenString();
} catch (\Exception $e) {
throw new JWTException('Could not create token: '.$e->getMessage(), $e->getCode(), $e);
}
}
/**
* Decode a JSON Web Token.
*
* @param string $token
*
* @return array
*
* @throws JWTException
*/
public function decode($token)
{
try {
// Let's never allow insecure tokens
$jws = $this->jws->load($token, false);
} catch (\InvalidArgumentException $e) {
throw new TokenInvalidException('Could not decode token: '.$e->getMessage(), $e->getCode(), $e);
}
if (!$jws->verify($this->getVerificationKey(), $this->getAlgo())) {
throw new TokenInvalidException('Token Signature could not be verified.');
}
return (array) $jws->getPayload();
}
protected function isAsymmetric()
{
try {
return (new \ReflectionClass(sprintf('Namshi\\JOSE\\Signer\\OpenSSL\\%s', $this->getAlgo())))->isSubclassOf(PublicKey::class);
} catch (\ReflectionException $e) {
throw new JWTException('The given algorithm could not be found', $e->getCode(), $e);
}
}
}
@@ -0,0 +1,188 @@
<?php
/*
* This file is part of jwt-auth.
*
* (c) 2014-2021 Sean Tymon <tymon148@gmail.com>
* (c) 2021 PHP Open Source Saver
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPOpenSourceSaver\JWTAuth\Providers\JWT;
use Illuminate\Support\Arr;
use PHPOpenSourceSaver\JWTAuth\Exceptions\JWTException;
use PHPOpenSourceSaver\JWTAuth\Exceptions\SecretMissingException;
abstract class Provider
{
/**
* The secret.
*/
protected ?string $secret;
/**
* The array of keys.
*/
protected array $keys;
/**
* The used algorithm.
*/
protected string $algo;
/**
* Constructor.
*
* @param string $secret
* @param string $algo
*
* @return void
*/
public function __construct($secret, $algo, array $keys)
{
if (is_null($secret) && (is_null($keys['public']) || is_null($keys['private']))) {
throw new SecretMissingException();
}
$this->secret = $secret;
$this->algo = $algo;
$this->keys = $keys;
}
/**
* Set the algorithm used to sign the token.
*
* @param string $algo
*
* @return $this
*/
public function setAlgo($algo)
{
$this->algo = $algo;
return $this;
}
/**
* Get the algorithm used to sign the token.
*
* @return string
*/
public function getAlgo()
{
return $this->algo;
}
/**
* Set the secret used to sign the token.
*
* @param string $secret
*
* @return $this
*/
public function setSecret($secret)
{
$this->secret = $secret;
return $this;
}
/**
* Get the secret used to sign the token.
*
* @return string
*/
public function getSecret()
{
return $this->secret;
}
/**
* Set the keys used to sign the token.
*
* @return $this
*/
public function setKeys(array $keys)
{
$this->keys = $keys;
return $this;
}
/**
* Get the array of keys used to sign tokens
* with an asymmetric algorithm.
*
* @return array
*/
public function getKeys()
{
return $this->keys;
}
/**
* Get the public key used to sign tokens
* with an asymmetric algorithm.
*
* @return resource|string
*/
public function getPublicKey()
{
return Arr::get($this->keys, 'public');
}
/**
* Get the private key used to sign tokens
* with an asymmetric algorithm.
*
* @return resource|string
*/
public function getPrivateKey()
{
return Arr::get($this->keys, 'private');
}
/**
* Get the passphrase used to sign tokens
* with an asymmetric algorithm.
*
* @return string
*/
public function getPassphrase()
{
return Arr::get($this->keys, 'passphrase');
}
/**
* Get the key used to sign the tokens.
*
* @return resource|string
*/
protected function getSigningKey()
{
return $this->isAsymmetric() ? $this->getPrivateKey() : $this->getSecret();
}
/**
* Get the key used to verify the tokens.
*
* @return resource|string
*/
protected function getVerificationKey()
{
return $this->isAsymmetric() ? $this->getPublicKey() : $this->getSecret();
}
/**
* Determine if the algorithm is asymmetric, and thus
* requires a public/private key combo.
*
* @return bool
*
* @throws JWTException
*/
abstract protected function isAsymmetric();
}
@@ -0,0 +1,87 @@
<?php
/*
* This file is part of jwt-auth.
*
* (c) 2014-2021 Sean Tymon <tymon148@gmail.com>
* (c) 2021 PHP Open Source Saver
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPOpenSourceSaver\JWTAuth\Providers;
use Laravel\Octane\Events\RequestReceived;
use Laravel\Octane\Events\TaskReceived;
use Laravel\Octane\Events\TickReceived;
use PHPOpenSourceSaver\JWTAuth\Facades\JWTAuth;
use PHPOpenSourceSaver\JWTAuth\Facades\JWTFactory;
use PHPOpenSourceSaver\JWTAuth\Facades\JWTProvider;
use PHPOpenSourceSaver\JWTAuth\Http\Parser\Cookies;
use PHPOpenSourceSaver\JWTAuth\Http\Parser\RouteParams;
class LaravelServiceProvider extends AbstractServiceProvider
{
public function boot()
{
$path = realpath(__DIR__.'/../../config/config.php');
$this->publishes([$path => $this->app->configPath('jwt.php')], 'config');
$this->mergeConfigFrom($path, 'jwt');
$this->aliasMiddleware();
$this->extendAuthGuard();
$config = $this->app->make('config');
$this->app['tymon.jwt.parser']->addParser([
new RouteParams(),
(new Cookies(
$config->get('jwt.decrypt_cookies'),
))->setKey($config->get('jwt.cookie_key_name', 'token')),
]);
if (isset($_SERVER['LARAVEL_OCTANE'])) {
$clear = function () {
JWTAuth::clearResolvedInstances();
JWTFactory::clearResolvedInstances();
JWTProvider::clearResolvedInstances();
};
$this->app['events']->listen(RequestReceived::class, $clear);
$this->app['events']->listen(TaskReceived::class, $clear);
$this->app['events']->listen(TickReceived::class, $clear);
}
}
protected function registerStorageProvider()
{
$this->app->singleton('tymon.jwt.provider.storage', function ($app) {
$instance = $this->getConfigInstance($app, 'providers.storage');
if (method_exists($instance, 'setLaravelVersion')) {
$instance->setLaravelVersion($this->app->version());
}
return $instance;
});
}
/**
* Alias the middleware.
*
* @return void
*/
protected function aliasMiddleware()
{
$router = $this->app['router'];
$method = method_exists($router, 'aliasMiddleware') ? 'aliasMiddleware' : 'middleware';
foreach ($this->middlewareAliases as $alias => $middleware) {
$router->$method($alias, $middleware);
}
}
}
@@ -0,0 +1,32 @@
<?php
/*
* This file is part of jwt-auth.
*
* (c) 2014-2021 Sean Tymon <tymon148@gmail.com>
* (c) 2021 PHP Open Source Saver
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPOpenSourceSaver\JWTAuth\Providers;
use PHPOpenSourceSaver\JWTAuth\Http\Parser\LumenRouteParams;
class LumenServiceProvider extends AbstractServiceProvider
{
public function boot()
{
$this->app->configure('jwt');
$path = realpath(__DIR__.'/../../config/config.php');
$this->mergeConfigFrom($path, 'jwt');
$this->app->routeMiddleware($this->middlewareAliases);
$this->extendAuthGuard();
$this->app['tymon.jwt.parser']->addParser(new LumenRouteParams());
}
}
@@ -0,0 +1,177 @@
<?php
/*
* This file is part of jwt-auth.
*
* (c) 2014-2021 Sean Tymon <tymon148@gmail.com>
* (c) 2021 PHP Open Source Saver
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPOpenSourceSaver\JWTAuth\Providers\Storage;
use Illuminate\Contracts\Cache\Repository as CacheContract;
use PHPOpenSourceSaver\JWTAuth\Contracts\Providers\Storage;
use Psr\SimpleCache\CacheInterface as PsrCacheInterface;
class Illuminate implements Storage
{
/**
* The cache repository contract.
*
* @var CacheContract
*/
protected $cache;
/**
* The used cache tag.
*
* @var string
*/
protected $tag = 'tymon.jwt';
/**
* @var bool
*/
protected $supportsTags;
/**
* @var string|null
*/
protected $laravelVersion;
/**
* Constructor.
*
* @return void
*/
public function __construct(CacheContract $cache)
{
$this->cache = $cache;
}
/**
* Add a new item into storage.
*
* @param string $key
* @param int $minutes
*
* @return void
*/
public function add($key, $value, $minutes)
{
// If the laravel version is 5.8 or higher then convert minutes to seconds.
if (null !== $this->laravelVersion
&& is_int($minutes)
&& version_compare($this->laravelVersion, '5.8', '>=')
) {
$minutes = $minutes * 60;
}
$this->cache()->put($key, $value, $minutes);
}
/**
* Add a new item into storage forever.
*
* @param string $key
*
* @return void
*/
public function forever($key, $value)
{
$this->cache()->forever($key, $value);
}
/**
* Get an item from storage.
*
* @param string $key
*/
public function get($key)
{
return $this->cache()->get($key);
}
/**
* Remove an item from storage.
*
* @param string $key
*
* @return bool
*/
public function destroy($key)
{
return $this->cache()->forget($key);
}
/**
* Remove all items associated with the tag.
*
* @return void
*/
public function flush()
{
$this->cache()->flush();
}
/**
* Return the cache instance with tags attached.
*
* @return CacheContract
*/
protected function cache()
{
if (null === $this->supportsTags) {
$this->determineTagSupport();
}
if ($this->supportsTags) {
return $this->cache->tags($this->tag);
}
return $this->cache;
}
/**
* Set the laravel version.
*/
public function setLaravelVersion($version)
{
$this->laravelVersion = $version;
return $this;
}
/**
* Detect as best we can whether tags are supported with this repository & store,
* and save our result on the $supportsTags flag.
*
* @return void
*/
protected function determineTagSupport()
{
// Laravel >= 5.1.28
if (method_exists($this->cache, 'tags') || $this->cache instanceof PsrCacheInterface) {
try {
// Attempt the repository tags command, which throws exceptions when unsupported
$this->cache->tags($this->tag);
$this->supportsTags = true;
} catch (\BadMethodCallException $ex) {
$this->supportsTags = false;
}
} else {
// Laravel <= 5.1.27
if (method_exists($this->cache, 'getStore')) {
// Check for the tags function directly on the store
$this->supportsTags = method_exists($this->cache->getStore(), 'tags');
} else {
// Must be using custom cache repository without getStore(), and all bets are off,
// or we are mocking the cache contract (in testing), which will not create a getStore method
$this->supportsTags = false;
}
}
}
}
@@ -0,0 +1,55 @@
<?php
/*
* This file is part of jwt-auth.
*
* (c) 2014-2021 Sean Tymon <tymon148@gmail.com>
* (c) 2021 PHP Open Source Saver
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPOpenSourceSaver\JWTAuth\Support;
trait CustomClaims
{
/**
* Custom claims.
*
* @var array
*/
protected $customClaims = [];
/**
* Set the custom claims.
*
* @return $this
*/
public function customClaims(array $customClaims)
{
$this->customClaims = $customClaims;
return $this;
}
/**
* Alias to set the custom claims.
*
* @return $this
*/
public function claims(array $customClaims)
{
return $this->customClaims($customClaims);
}
/**
* Get the custom claims.
*
* @return array
*/
public function getCustomClaims()
{
return $this->customClaims;
}
}
@@ -0,0 +1,37 @@
<?php
/*
* This file is part of jwt-auth.
*
* (c) 2014-2021 Sean Tymon <tymon148@gmail.com>
* (c) 2021 PHP Open Source Saver
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPOpenSourceSaver\JWTAuth\Support;
trait RefreshFlow
{
/**
* The refresh flow flag.
*
* @var bool
*/
protected $refreshFlow = false;
/**
* Set the refresh flow flag.
*
* @param bool $refreshFlow
*
* @return $this
*/
public function setRefreshFlow($refreshFlow = true)
{
$this->refreshFlow = $refreshFlow;
return $this;
}
}
@@ -0,0 +1,74 @@
<?php
/*
* This file is part of jwt-auth.
*
* (c) 2014-2021 Sean Tymon <tymon148@gmail.com>
* (c) 2021 PHP Open Source Saver
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPOpenSourceSaver\JWTAuth\Support;
use Carbon\Carbon;
class Utils
{
/**
* Get the Carbon instance for the current time.
*
* @return Carbon
*/
public static function now()
{
return Carbon::now('UTC');
}
/**
* Get the Carbon instance for the timestamp.
*
* @param int $timestamp
*
* @return Carbon
*/
public static function timestamp($timestamp)
{
return Carbon::createFromTimestampUTC($timestamp)->timezone('UTC');
}
/**
* Checks if a timestamp is in the past.
*
* @param int $timestamp
* @param int $leeway
*
* @return bool
*/
public static function isPast($timestamp, $leeway = 0)
{
$timestamp = static::timestamp($timestamp);
return $leeway > 0
? $timestamp->addSeconds($leeway)->isPast()
: $timestamp->isPast();
}
/**
* Checks if a timestamp is in the future.
*
* @param int $timestamp
* @param int $leeway
*
* @return bool
*/
public static function isFuture($timestamp, $leeway = 0)
{
$timestamp = static::timestamp($timestamp);
return $leeway > 0
? $timestamp->subSeconds($leeway)->isFuture()
: $timestamp->isFuture();
}
}
+54
View File
@@ -0,0 +1,54 @@
<?php
/*
* This file is part of jwt-auth.
*
* (c) 2014-2021 Sean Tymon <tymon148@gmail.com>
* (c) 2021 PHP Open Source Saver
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPOpenSourceSaver\JWTAuth;
use PHPOpenSourceSaver\JWTAuth\Validators\TokenValidator;
class Token
{
private string $value;
/**
* Create a new JSON Web Token.
*
* @param string $value
*
* @return void
*
* @throws Exceptions\TokenInvalidException
*/
public function __construct($value)
{
$this->value = (string) (new TokenValidator())->check($value);
}
/**
* Get the token.
*
* @return string
*/
public function get()
{
return $this->value;
}
/**
* Get the token when casting to string.
*
* @return string
*/
public function __toString()
{
return $this->get();
}
}
@@ -0,0 +1,120 @@
<?php
/*
* This file is part of jwt-auth.
*
* (c) 2014-2021 Sean Tymon <tymon148@gmail.com>
* (c) 2021 PHP Open Source Saver
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPOpenSourceSaver\JWTAuth\Validators;
use PHPOpenSourceSaver\JWTAuth\Claims\Collection;
use PHPOpenSourceSaver\JWTAuth\Exceptions\TokenInvalidException;
class PayloadValidator extends Validator
{
/**
* The required claims.
*
* @var array
*/
protected $requiredClaims = [
'iss',
'iat',
'exp',
'nbf',
'sub',
'jti',
];
/**
* The refresh TTL.
*
* @var int
*/
protected $refreshTTL = 20160;
/**
* Run the validations on the payload array.
*
* @param Collection $value
*
* @return Collection
*/
public function check($value)
{
$this->validateStructure($value);
return $this->refreshFlow ? $this->validateRefresh($value) : $this->validatePayload($value);
}
/**
* Ensure the payload contains the required claims and
* the claims have the relevant type.
*
* @return void
*
* @throws TokenInvalidException
*/
protected function validateStructure(Collection $claims)
{
if ($this->requiredClaims && !$claims->hasAllClaims($this->requiredClaims)) {
throw new TokenInvalidException('JWT payload does not contain the required claims');
}
}
/**
* Validate the payload timestamps.
*
* @return Collection
*
* @throws TokenInvalidException
* @throws \PHPOpenSourceSaver\JWTAuth\Exceptions\TokenExpiredException
*/
protected function validatePayload(Collection $claims)
{
return $claims->validate('payload');
}
/**
* Check the token in the refresh flow context.
*
* @return Collection
*
* @throws \PHPOpenSourceSaver\JWTAuth\Exceptions\TokenExpiredException
*/
protected function validateRefresh(Collection $claims)
{
return null === $this->refreshTTL ? $claims : $claims->validate('refresh', $this->refreshTTL);
}
/**
* Set the required claims.
*
* @return $this
*/
public function setRequiredClaims(array $claims)
{
$this->requiredClaims = $claims;
return $this;
}
/**
* Set the refresh ttl.
*
* @param int $ttl
*
* @return $this
*/
public function setRefreshTTL($ttl)
{
$this->refreshTTL = $ttl;
return $this;
}
}
@@ -0,0 +1,56 @@
<?php
/*
* This file is part of jwt-auth.
*
* (c) 2014-2021 Sean Tymon <tymon148@gmail.com>
* (c) 2021 PHP Open Source Saver
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPOpenSourceSaver\JWTAuth\Validators;
use PHPOpenSourceSaver\JWTAuth\Exceptions\TokenInvalidException;
class TokenValidator extends Validator
{
/**
* Check the structure of the token.
*
* @param string $value
*
* @return string
*
* @throws TokenInvalidException
*/
public function check($value)
{
return $this->validateStructure($value);
}
/**
* @param string $token
*
* @return string
*
* @throws TokenInvalidException
*/
protected function validateStructure($token)
{
$parts = explode('.', $token);
if (3 !== count($parts)) {
throw new TokenInvalidException('Wrong number of segments');
}
$parts = array_filter(array_map('trim', $parts));
if (3 !== count($parts) || implode('.', $parts) !== $token) {
throw new TokenInvalidException('Malformed token');
}
return $token;
}
}
@@ -0,0 +1,49 @@
<?php
/*
* This file is part of jwt-auth.
*
* (c) 2014-2021 Sean Tymon <tymon148@gmail.com>
* (c) 2021 PHP Open Source Saver
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPOpenSourceSaver\JWTAuth\Validators;
use PHPOpenSourceSaver\JWTAuth\Contracts\Validator as ValidatorContract;
use PHPOpenSourceSaver\JWTAuth\Exceptions\JWTException;
use PHPOpenSourceSaver\JWTAuth\Support\RefreshFlow;
abstract class Validator implements ValidatorContract
{
use RefreshFlow;
/**
* Helper function to return a boolean.
*
* @param array $value
*
* @return bool
*/
public function isValid($value)
{
try {
$this->check($value);
} catch (JWTException $e) {
return false;
}
return true;
}
/**
* Run the validation.
*
* @param array $value
*
* @return void
*/
abstract public function check($value);
}