diff --git a/.gitattributes b/.gitattributes
index ebcadc98d..8f6a4572a 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -1,13 +1,13 @@
* text=auto
/tests export-ignore
+/docs export-ignore
/.codecov.yml export-ignore
/.editorconfig export-ignore
/.gitattributes export-ignore
/.gitignore export-ignore
/.styleci.yml export-ignore
/.travis.yml export-ignore
-/CODE_OF_CONDUCT.md export-ignore
-/CONTRIBUTING.md export-ignore
+/.github export-ignore
/phpunit.xml.dist export-ignore
/README.md export-ignore
diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml
new file mode 100644
index 000000000..228097987
--- /dev/null
+++ b/.github/workflows/run-tests.yml
@@ -0,0 +1,44 @@
+name: Run tests
+
+on:
+ push:
+
+jobs:
+ php-tests:
+ runs-on: ${{ matrix.os }}
+
+ strategy:
+ matrix:
+ php: [7.4]
+ laravel: [6.*, 7.*]
+ os: [ubuntu-latest, windows-latest]
+ coverage: ['none', 'xdebug']
+
+ name: '[P${{ matrix.php }}] [L${{ matrix.laravel }}] [${{ matrix.os }}] [${{ matrix.coverage }}]'
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v1
+
+ - name: Setup PHP
+ uses: shivammathur/setup-php@v1
+ with:
+ php-version: ${{ matrix.php }}
+ extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, bcmath, soap, intl, gd, gmp, exif, iconv, imagick
+ coverage: ${{ matrix.coverage }}
+
+ - name: Install dependencies
+ run: |
+ composer require "illuminate/contracts:${{ matrix.laravel }}" --no-interaction --no-update
+ composer update --prefer-stable --prefer-dist --no-interaction --no-suggest
+
+ - name: Execute tests
+ run: composer test:ci
+
+ - name: Upload coverage
+ uses: codecov/codecov-action@v1
+ if: matrix.coverage == 'xdebug'
+ with:
+ token: ${{ secrets.CODECOV_TOKEN }}
+ file: ./coverage.xml
+ yml: ./codecov.yml
diff --git a/.gitignore b/.gitignore
index 8830b4835..7335f3b2a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,3 +2,4 @@
composer.lock
build
coverage.xml
+/.phpunit.*
diff --git a/.styleci.yml b/.styleci.yml
index 8d6af11ab..3377c4db4 100644
--- a/.styleci.yml
+++ b/.styleci.yml
@@ -6,5 +6,4 @@ enabled:
- phpdoc_order
- phpdoc_separation
- unalign_double_arrow
-
-linting: true
+ - php_unit_no_expectation_annotation
diff --git a/.travis.yml b/.travis.yml
index c200bd9d9..69aeaf7f1 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -15,64 +15,14 @@ env:
matrix:
include:
- - php: hhvm-3.18
- sudo: required
- dist: trusty
- env: LARAVEL='5.4.*' XDEBUG=1
- group: edge
- - php: 5.5.9
- env: LARAVEL='5.1.*'
- - php: 5.5.9
- env: LARAVEL='5.2.*'
- - php: 5.5
- env: LARAVEL='5.1.*'
- - php: 5.5
- env: LARAVEL='5.2.*'
- - php: 5.6
- env: LARAVEL='5.1.*'
- - php: 5.6
- env: LARAVEL='5.2.*'
- - php: 5.6
- env: LARAVEL='5.3.*'
- - php: 5.6
- env: LARAVEL='5.4.*'
- - php: 7.0
- env: LARAVEL='5.1.*'
- - php: 7.0
- env: LARAVEL='5.2.*'
- - php: 7.0
- env: LARAVEL='5.3.*'
- - php: 7.0
- env: LARAVEL='5.4.*'
- - php: 7.0
- env: LARAVEL='5.5.*'
- - php: 7.1
- env: LARAVEL='5.1.*'
- - php: 7.1
- env: LARAVEL='5.2.*'
- - php: 7.1
- env: LARAVEL='5.3.*'
- - php: 7.1
- env: LARAVEL='5.4.*'
- - php: 7.1
- env: LARAVEL='5.5.*'
- - php: 7.1
- env: LARAVEL='5.6.*'
- - php: 7.2
- env: LARAVEL='5.1.*' XDEBUG=1
- - php: 7.2
- env: LARAVEL='5.2.*' XDEBUG=1
- - php: 7.2
- env: LARAVEL='5.3.*' XDEBUG=1
- - php: 7.2
- env: LARAVEL='5.4.*' XDEBUG=1
- - php: 7.2
- env: LARAVEL='5.5.*' XDEBUG=1
- - php: 7.2
- env: COVERAGE=1 LARAVEL='5.6.*' XDEBUG=1
+ - php: 7.4
+ env: LARAVEL='5.7.*' XDEBUG=1
+ - php: 7.4
+ env: LARAVEL='5.8.*' XDEBUG=1
+ - php: 7.4
+ env: COVERAGE=1 LARAVEL='6.*' XDEBUG=1
fast_finish: true
-
before_install:
- if [[ $XDEBUG = 0 ]]; then phpenv config-rm xdebug.ini; fi
- travis_retry composer self-update
@@ -82,8 +32,7 @@ install:
- travis_retry composer install --prefer-dist --no-interaction --no-suggest
script:
- - if [[ $LARAVEL = '5.1.*' ]]; then composer test:ci -- --exclude-group laravel-5.2; fi
- - if [[ $LARAVEL != '5.1.*' ]]; then composer test:ci; fi
+ - composer test:ci
after_success:
- if [[ $COVERAGE = 1 ]]; then bash <(curl -s https://codecov.io/bash); fi
diff --git a/LICENSE b/LICENSE
index ebd746db8..90712983c 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,6 @@
The MIT License (MIT)
-Copyright (c) 2018 Sean Tymon
+Copyright (c) Sean Tymon
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/README.md b/README.md
index 776e31529..14539f734 100644
--- a/README.md
+++ b/README.md
@@ -1,13 +1,11 @@
![jwt-auth-banner](https://cloud.githubusercontent.com/assets/1801923/9915273/119b9350-5cae-11e5-850b-c941cac60b32.png)
-[![Build Status](http://img.shields.io/travis/tymondesigns/jwt-auth/master.svg?style=flat-square)](https://travis-ci.org/tymondesigns/jwt-auth)
-[![Codecov branch](https://img.shields.io/codecov/c/github/tymondesigns/jwt-auth/develop.svg?style=flat-square)](https://codecov.io/github/tymondesigns/jwt-auth)
-[![StyleCI](https://styleci.io/repos/23680678/shield?style=flat-square)](https://styleci.io/repos/23680678)
-[![Latest Version](http://img.shields.io/packagist/v/tymon/jwt-auth.svg?style=flat-square)](https://packagist.org/packages/tymon/jwt-auth)
-[![Latest Dev Version](https://img.shields.io/packagist/vpre/tymon/jwt-auth.svg?style=flat-square)](https://packagist.org/packages/tymon/jwt-auth#dev-develop)
-[![Monthly Downloads](https://img.shields.io/packagist/dm/tymon/jwt-auth.svg?style=flat-square)](https://packagist.org/packages/tymon/jwt-auth)
-[![Dependency Status](https://www.versioneye.com/php/tymon:jwt-auth/dev-develop/badge?style=flat-square)](https://www.versioneye.com/php/tymon:jwt-auth/dev-develop)
-[![PHP-Eye](https://php-eye.com/badge/tymon/jwt-auth/tested.svg?style=flat-square)](https://php-eye.com/package/tymon/jwt-auth)
+[![Build Status](http://img.shields.io/travis/tymondesigns/jwt-auth/master.svg?style=flat-square&logo=travis)](https://travis-ci.org/tymondesigns/jwt-auth)
+[![Codecov branch](https://img.shields.io/codecov/c/github/tymondesigns/jwt-auth/develop.svg?style=flat-square&logo=codecov)](https://codecov.io/github/tymondesigns/jwt-auth)
+[![StyleCI](https://styleci.io/repos/23680678/shield?style=flat-square&logo=styleci)](https://styleci.io/repos/23680678)
+[![Latest Version](http://img.shields.io/packagist/v/tymon/jwt-auth.svg?style=flat-square&logo=packagist)](https://packagist.org/packages/tymon/jwt-auth)
+[![Latest Dev Version](https://img.shields.io/packagist/vpre/tymon/jwt-auth.svg?style=flat-square&logo=packagist)](https://packagist.org/packages/tymon/jwt-auth#dev-develop)
+[![Monthly Downloads](https://img.shields.io/packagist/dm/tymon/jwt-auth.svg?style=flat-square&logo=packagist)](https://packagist.org/packages/tymon/jwt-auth)
## Documentation
diff --git a/composer.json b/composer.json
index f40982825..3ae104869 100644
--- a/composer.json
+++ b/composer.json
@@ -23,24 +23,32 @@
}
],
"require": {
- "php": "^5.5.9 || ^7.0",
- "illuminate/auth": "5.1.* || 5.2.* || 5.3.* || 5.4.* || 5.5.* || 5.6.*",
- "illuminate/contracts": "5.1.* || 5.2.* || 5.3.* || 5.4.* || 5.5.* || 5.6.*",
- "illuminate/http": "5.1.* || 5.2.* || 5.3.* || 5.4.* || 5.5.* || 5.6.*",
- "illuminate/support": "5.1.* || 5.2.* || 5.3.* || 5.4.* || 5.5.* || 5.6.*",
+ "php": "^7.4|^8.0.0",
+ "illuminate/contracts": "^6|^7",
+ "illuminate/http": "^6|^7",
+ "illuminate/support": "^6|^7",
"lcobucci/jwt": "^3.2",
- "namshi/jose": "^7.0",
- "nesbot/carbon": "^1.0"
+ "nesbot/carbon": "^2.0",
+ "web-token/jwt-checker": "^2.1",
+ "web-token/jwt-core": "^2.1",
+ "web-token/jwt-key-mgmt": "^2.1",
+ "web-token/jwt-signature": "^2.1",
+ "web-token/jwt-signature-algorithm-ecdsa": "^2.1",
+ "web-token/jwt-signature-algorithm-hmac": "^2.1",
+ "web-token/jwt-signature-algorithm-rsa": "^2.1"
},
"require-dev": {
- "cartalyst/sentinel": "2.0.*",
- "illuminate/console": "5.1.* || 5.2.* || 5.3.* || 5.4.* || 5.5.* || 5.6.*",
- "illuminate/database": "5.1.* || 5.2.* || 5.3.* || 5.4.* || 5.5.* || 5.6.*",
- "illuminate/routing": "5.1.* || 5.2.* || 5.3.* || 5.4.* || 5.5.* || 5.6.*",
- "mockery/mockery": ">=0.9.9",
- "phpunit/phpunit": "~4.8 || ~6.0"
+ "illuminate/auth": "^6|^7",
+ "illuminate/console": "^6|^7",
+ "illuminate/database": "^6|^7",
+ "illuminate/routing": "^6|^7",
+ "mockery/mockery": "^1.0",
+ "phpunit/phpunit": "^8.0"
},
"autoload": {
+ "files": [
+ "src/Support/helpers.php"
+ ],
"psr-4": {
"Tymon\\JWTAuth\\": "src/"
}
@@ -52,12 +60,12 @@
},
"extra": {
"branch-alias": {
- "dev-develop": "1.0-dev"
+ "2.0": "2.0-dev"
},
"laravel": {
"aliases": {
- "JWTAuth": "Tymon\\JWTAuth\\Facades\\JWTAuth",
- "JWTFactory": "Tymon\\JWTAuth\\Facades\\JWTFactory"
+ "JWTManager": "Tymon\\JWTAuth\\Facades\\JWTManager",
+ "JWTProvider": "Tymon\\JWTAuth\\Facades\\JWTProvider"
},
"providers": [
"Tymon\\JWTAuth\\Providers\\LaravelServiceProvider"
diff --git a/config/config.php b/config/config.php
index c4ea92db1..6525071b4 100644
--- a/config/config.php
+++ b/config/config.php
@@ -9,6 +9,8 @@
* file that was distributed with this source code.
*/
+use Tymon\JWTAuth\Claims;
+
return [
/*
@@ -91,7 +93,7 @@
|--------------------------------------------------------------------------
|
| Specify the length of time (in minutes) that the token will be valid for.
- | Defaults to 1 hour.
+ | Defaults to 30 minutes.
|
| 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.
@@ -100,26 +102,21 @@
|
*/
- 'ttl' => env('JWT_TTL', 60),
+ 'ttl' => env('JWT_TTL', 30),
/*
|--------------------------------------------------------------------------
- | Refresh time to live
+ | Max refresh period
|--------------------------------------------------------------------------
|
- | Specify the length of time (in minutes) that the token can be refreshed
- | within. I.E. The user can refresh their token within a 2 week window of
- | the original token being created until they must re-authenticate.
- | Defaults to 2 weeks.
+ | Specify the length of time (in minutes) that the token will be
+ | refreshable for.
|
- | 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.
+ | Defaults to null, which will allow tokens to be refreshable forever.
|
*/
- 'refresh_ttl' => env('JWT_REFRESH_TTL', 20160),
+ 'max_refresh_period' => env('JWT_MAX_REFRESH_PERIOD'),
/*
|--------------------------------------------------------------------------
@@ -128,8 +125,11 @@
|
| 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.
+ | Possible values:
+ |
+ | 'HS256', 'HS384', 'HS512',
+ | 'RS256', 'RS384', 'RS512',
+ | 'ES256', 'ES384', 'ES512'
|
*/
@@ -147,30 +147,11 @@
*/
'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',
+ Claims\Issuer::NAME,
+ Claims\IssuedAt::NAME,
+ Claims\Expiration::NAME,
+ Claims\Subject::NAME,
+ Claims\JwtId::NAME,
],
/*
@@ -178,11 +159,12 @@
| 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.
+ | This will determine whether a HashedSubject (hsu) 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
@@ -276,17 +258,6 @@
'jwt' => Tymon\JWTAuth\Providers\JWT\Lcobucci::class,
- /*
- |--------------------------------------------------------------------------
- | Authentication Provider
- |--------------------------------------------------------------------------
- |
- | Specify the provider that is used to authenticate users.
- |
- */
-
- 'auth' => Tymon\JWTAuth\Providers\Auth\Illuminate::class,
-
/*
|--------------------------------------------------------------------------
| Storage Provider
diff --git a/docs/.nojekyll b/docs/.nojekyll
new file mode 100644
index 000000000..e69de29bb
diff --git a/docs/_coverpage.md b/docs/_coverpage.md
new file mode 100644
index 000000000..c97a55358
--- /dev/null
+++ b/docs/_coverpage.md
@@ -0,0 +1,11 @@
+# jwt-auth
+
+> JSON Web Token Authentication for Laravel & Lumen
+
+- Simple and lightweight (~21kB gzipped)
+- No statically built html files
+- Multiple themes
+
+[Get Started](laravel-installation.md)
+[GitHub](https://github.com/tymondesigns/jwt-auth)
+
diff --git a/docs/_navbar.md b/docs/_navbar.md
new file mode 100644
index 000000000..911b87045
--- /dev/null
+++ b/docs/_navbar.md
@@ -0,0 +1,9 @@
+- **Getting started**
+ - [Laravel Installation](laravel-installation.md)
+ - [Lumen Installation](lumen-installation.md)
+ - [Quick Start](quick-start.md)
+ - [Configuration](configuration.md)
+ - [Exception handling](exception-handling.md)
+
+- **Versions**
+ - [1.*](#/1.0)
diff --git a/docs/_sidebar.md b/docs/_sidebar.md
new file mode 100644
index 000000000..be8fd8941
--- /dev/null
+++ b/docs/_sidebar.md
@@ -0,0 +1,15 @@
+- **Getting started**
+ - [Laravel Installation](laravel-installation.md)
+ - [Lumen Installation](lumen-installation.md)
+ - [Quick Start](quick-start.md)
+ - [Configuration](configuration.md)
+
+- **Usage**
+ - [Auth Guard](auth-guard.md)
+ - [Exception handling](exception-handling.md)
+
+- [Resources](resources.md)
+
+- **Links**
+ - [![Github](https://icongr.am/simple/github.svg?color=808080&size=16)Github](https://github.com/tymondesigns/jwt-auth)
+ - [![Twitter](https://icongr.am/simple/twitter.svg?colored&size=16)@tymondesigns](http://twitter.com/tymondesigns)
diff --git a/docs/auth-guard.md b/docs/auth-guard.md
index 98f0668dc..3f335bdb5 100644
--- a/docs/auth-guard.md
+++ b/docs/auth-guard.md
@@ -7,8 +7,12 @@ The following methods are available on the Auth guard instance.
Attempt to authenticate a user via some credentials.
```php
+$credentials = request(['email', 'password']);
+
// Generate a token for the user if the credentials are valid
-$token = auth()->attempt($credentials);
+if ($token = auth()->attempt($credentials)) {
+ // Credentials are good
+}
```
This will return either a jwt or `null`
@@ -102,9 +106,21 @@ $payload = auth()->payload();
// then you can access the claims directly e.g.
$payload->get('sub'); // = 123
+
+// array access to claims
$payload['jti']; // = 'asfe4fq434asdf'
+
+// invokable access to claims
$payload('exp') // = 123456
-$payload->toArray(); // = ['sub' => 123, 'exp' => 123456, 'jti' => 'asfe4fq434asdf'] etc
+
+$payload->toArray(); // = ['sub' => 123, 'exp' => 123456, 'jti' => 'asfe4...'] etc
+```
+
+?> Payloads are immutable and cannot be altered after they have been created.
+
+```php
+// This will throw a \Tymon\JWTAuth\Exceptions\PayloadException
+$payload['sub'] = 1;
```
### validate()
diff --git a/docs/configuration.md b/docs/configuration.md
index d58e9a10b..510e9c988 100644
--- a/docs/configuration.md
+++ b/docs/configuration.md
@@ -1,3 +1,5 @@
+# Configuration
+
Let's review some of the options in the `config/jwt.php` file that we published earlier.
I won't go through all of the options here since [the file itself](https://github.com/tymondesigns/jwt-auth/blob/1.0.0-beta.2/config/config.php) is pretty well documented.
diff --git a/docs/index.html b/docs/index.html
new file mode 100644
index 000000000..c7d9bedb2
--- /dev/null
+++ b/docs/index.html
@@ -0,0 +1,72 @@
+
+
+
+
+ JSON Web Token Authentication for Laravel & Lumen
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/docs/index.md b/docs/index.md
index 9486484f4..48f65ad6b 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -1,3 +1,4 @@
-JSON Web Token Authentication for Laravel & Lumen
+
+
![jwt-auth-banner](https://cloud.githubusercontent.com/assets/1801923/9915273/119b9350-5cae-11e5-850b-c941cac60b32.png)
diff --git a/docs/laravel-installation.md b/docs/laravel-installation.md
index 2d473cdbe..4b6ba1e6e 100644
--- a/docs/laravel-installation.md
+++ b/docs/laravel-installation.md
@@ -1,4 +1,6 @@
-### Install via composer
+# Laravel Installation
+
+## Install via composer
Run the following command to pull in the latest version:
@@ -8,13 +10,12 @@ composer require tymon/jwt-auth
-------------------------------------------------------------------------------
-### Add service provider ( Laravel 5.4 or below )
+## Add service provider ( Laravel 5.4 or below )
Add the service provider to the `providers` array in the `config/app.php` config file as follows:
```php
'providers' => [
-
...
Tymon\JWTAuth\Providers\LaravelServiceProvider::class,
@@ -23,7 +24,7 @@ Add the service provider to the `providers` array in the `config/app.php` config
-------------------------------------------------------------------------------
-### Publish the config
+## Publish the config
Run the following command to publish the package config file:
@@ -35,7 +36,7 @@ You should now have a `config/jwt.php` file that allows you to configure the bas
-------------------------------------------------------------------------------
-### Generate secret key
+## Generate secret key
I have included a helper command to generate a key for you:
diff --git a/docs/lumen-installation.md b/docs/lumen-installation.md
index 039abc521..b41f5758c 100644
--- a/docs/lumen-installation.md
+++ b/docs/lumen-installation.md
@@ -1,4 +1,6 @@
-### Install via composer
+# Lumen Installation
+
+## Install via composer
Run the following command to pull in the latest version:
@@ -8,7 +10,7 @@ composer require tymon/jwt-auth
-------------------------------------------------------------------------------
-### Bootstrap file changes.
+## Bootstrap file changes.
Add the following snippet to the `bootstrap/app.php` file under the providers section as follows:
@@ -30,7 +32,7 @@ $app->routeMiddleware([
-------------------------------------------------------------------------------
-### Generate secret key
+## Generate secret key
I have included a helper command to generate a key for you:
diff --git a/docs/quick-start.md b/docs/quick-start.md
index 9a7b3f11e..056c5f81b 100644
--- a/docs/quick-start.md
+++ b/docs/quick-start.md
@@ -1,7 +1,9 @@
+# Quick Start
+
Before continuing, make sure you have installed the package as per the installation instructions for
[Laravel](laravel-installation) or [Lumen](lumen-installation).
-### Update your User model
+## Update your User model
Firstly you need to implement the `Tymon\JWTAuth\Contracts\JWTSubject` contract on your User model,
which requires that you implement the 2 methods `getJWTIdentifier()` and `getJWTCustomClaims()`.
@@ -46,9 +48,9 @@ class User extends Authenticatable implements JWTSubject
}
```
-### Configure Auth guard
+## Configure Auth guard
-*Note: This will only work if you are using Laravel 5.2 and above.*
+!> This will only work if you are using Laravel 5.2 and above.*
Inside the `config/auth.php` file you will need to make a few changes to configure Laravel
to use the `jwt` guard to power your application authentication.
@@ -76,27 +78,23 @@ as the default.
We can now use Laravel's built in Auth system, with jwt-auth doing the work behind the scenes!
-### Add some basic authentication routes
+## Add some basic authentication routes
First let's add some routes in `routes/api.php` as follows:
```php
Route::group([
-
'middleware' => 'api',
'prefix' => 'auth'
-
], function ($router) {
-
Route::post('login', 'AuthController@login');
Route::post('logout', 'AuthController@logout');
Route::post('refresh', 'AuthController@refresh');
Route::post('me', 'AuthController@me');
-
});
```
-### Create the AuthController
+## Create the AuthController
Then create the `AuthController`, either manually or by running the artisan command:
@@ -123,7 +121,7 @@ class AuthController extends Controller
*/
public function __construct()
{
- $this->middleware('auth:api', ['except' => ['login']]);
+ $this->middleware('auth:api')->except('login');
}
/**
@@ -205,7 +203,7 @@ credentials and see a response like:
This token can then be used to make authenticated requests to your application.
-### Authenticated requests
+## Authenticated requests
There are a number of ways to send the token via http:
diff --git a/mkdocs.yml b/mkdocs.yml
deleted file mode 100644
index 9cedaccd8..000000000
--- a/mkdocs.yml
+++ /dev/null
@@ -1,11 +0,0 @@
-site_name: jwt-auth
-pages:
- - Home: index.md
- - Laravel Installation: laravel-installation.md
- - Lumen Installation (incomplete): lumen-installation.md
- - Quick start: quick-start.md
- - Auth guard: auth-guard.md
- - Configuration: configuration.md
- - Exception Handling: exception-handling.md
- - Resources: resources.md
-theme: readthedocs
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
index ee9ce0773..01a0291ba 100644
--- a/phpunit.xml.dist
+++ b/phpunit.xml.dist
@@ -34,7 +34,7 @@
-
+
diff --git a/src/Blacklist.php b/src/Blacklist.php
index 78bc273e4..f387702d5 100644
--- a/src/Blacklist.php
+++ b/src/Blacklist.php
@@ -1,5 +1,7 @@
hasKey('exp')) {
- return $this->addForever($payload);
+ if (! $payload->hasKey(Expiration::NAME)) {
+ $this->addForever($payload);
+
+ return;
+ }
+
+ // if we have already added this token to the blacklist
+ if (! empty($this->storage->get($this->getKey($payload)))) {
+ return;
}
$this->storage->add(
$this->getKey($payload),
- ['valid_until' => $this->getGraceTimestamp()],
+ [static::VALID_UNTIL => $this->getGraceTimestamp()],
$this->getMinutesUntilExpired($payload)
);
-
- return true;
}
/**
* Get the number of minutes until the token expiry.
- *
- * @param \Tymon\JWTAuth\Payload $payload
- *
- * @return int
*/
- protected function getMinutesUntilExpired(Payload $payload)
+ protected function getMinutesUntilExpired(Payload $payload): int
{
- $exp = Utils::timestamp($payload['exp']);
- $iat = Utils::timestamp($payload['iat']);
+ $exp = timestamp($payload[Expiration::NAME]);
- // get the latter of the two expiration dates and find
- // the number of minutes until the expiration date,
+ // find the number of minutes until the expiration date,
// plus 1 minute to avoid overlap
- return $exp->max($iat->addMinutes($this->refreshTTL))->addMinute()->diffInMinutes();
+ return now()
+ ->subMinute()
+ ->diffInRealMinutes($exp);
}
/**
* Add the token (jti claim) to the blacklist indefinitely.
- *
- * @param \Tymon\JWTAuth\Payload $payload
- *
- * @return bool
*/
- public function addForever(Payload $payload)
+ public function addForever(Payload $payload): void
{
- $this->storage->forever($this->getKey($payload), 'forever');
-
- return true;
+ $this->storage->forever($this->getKey($payload), static::FOREVER);
}
/**
* Determine whether the token has been blacklisted.
- *
- * @param \Tymon\JWTAuth\Payload $payload
- *
- * @return bool
*/
- public function has(Payload $payload)
+ public function has(Payload $payload): bool
{
$val = $this->storage->get($this->getKey($payload));
// exit early if the token was blacklisted forever,
- if ($val === 'forever') {
+ if ($val === static::FOREVER) {
return true;
}
// check whether the expiry + grace has past
- return ! empty($val) && ! Utils::isFuture($val['valid_until']);
+ return ! empty($val) && ! is_future($val[static::VALID_UNTIL]);
}
/**
- * Remove the token (jti claim) from the blacklist.
- *
- * @param \Tymon\JWTAuth\Payload $payload
- *
- * @return bool
+ * Remove the token from the blacklist.
*/
- public function remove(Payload $payload)
+ public function remove(Payload $payload): void
{
- return $this->storage->destroy($this->getKey($payload));
+ $this->storage->destroy($this->getKey($payload));
}
/**
* Remove all tokens from the blacklist.
- *
- * @return bool
*/
- public function clear()
+ public function clear(): void
{
$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()
+ protected function getGraceTimestamp(): int
{
- return Utils::now()->addSeconds($this->gracePeriod)->getTimestamp();
+ return now()
+ ->addSeconds($this->gracePeriod)
+ ->getTimestamp();
}
/**
* Set the grace period.
- *
- * @param int $gracePeriod
- *
- * @return $this
*/
- public function setGracePeriod($gracePeriod)
+ public function setGracePeriod(int $gracePeriod): self
{
- $this->gracePeriod = (int) $gracePeriod;
+ $this->gracePeriod = $gracePeriod;
return $this;
}
/**
* Get the grace period.
- *
- * @return int
*/
- public function getGracePeriod()
+ public function getGracePeriod(): int
{
return $this->gracePeriod;
}
/**
* Get the unique key held within the blacklist.
- *
- * @param \Tymon\JWTAuth\Payload $payload
- *
- * @return mixed
*/
- public function getKey(Payload $payload)
+ public function getKey(Payload $payload): string
{
- return $payload($this->key);
+ return (string) $payload[$this->key];
}
/**
* Set the unique key held within the blacklist.
- *
- * @param string $key
- *
- * @return $this
*/
- public function setKey($key)
+ public function setKey(string $key): self
{
- $this->key = value($key);
+ $this->key = $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;
- }
}
diff --git a/src/Builder.php b/src/Builder.php
new file mode 100644
index 000000000..d54755fc6
--- /dev/null
+++ b/src/Builder.php
@@ -0,0 +1,283 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Tymon\JWTAuth;
+
+use Illuminate\Http\Request;
+use Illuminate\Support\Arr;
+use Tymon\JWTAuth\Claims\Factory as ClaimFactory;
+use Tymon\JWTAuth\Contracts\JWTSubject;
+use function Tymon\JWTAuth\Support\now;
+use function Tymon\JWTAuth\Support\timestamp;
+
+class Builder
+{
+ /**
+ * The request.
+ */
+ protected Request $request;
+
+ /**
+ * The TTL in minutes.
+ */
+ protected int $ttl = 30;
+
+ /**
+ * Lock the subject.
+ */
+ protected bool $lockSubject = true;
+
+ /**
+ * Time leeway in seconds.
+ */
+ protected int $leeway = 0;
+
+ /**
+ * Max refresh period in minutes.
+ */
+ protected ?int $maxRefreshPeriod = null;
+
+ /**
+ * The required claims.
+ */
+ protected array $requiredClaims = [];
+
+ /**
+ * The default claims to add.
+ */
+ protected array $defaultClaims = [
+ Claims\IssuedAt::NAME,
+ Claims\JwtId::NAME,
+ Claims\Issuer::NAME,
+ ];
+
+ /**
+ * Any custom validators.
+ */
+ protected array $customValidators = [];
+
+ /**
+ * Constructor.
+ */
+ public function __construct(Request $request)
+ {
+ $this->request = $request;
+ }
+
+ /**
+ * Create a Payload instance for a given array of claims.
+ */
+ public function make($claims = []): Payload
+ {
+ return Factory::make($claims, $this->getOptions());
+ }
+
+ /**
+ * Create a Payload instance for a given subject.
+ */
+ public function makeForSubject(JWTSubject $subject, array $claims = []): Payload
+ {
+ return $this->make(array_merge(
+ $this->getDefaultClaims(),
+ $this->getClaimsForSubject($subject),
+ $subject->getJWTCustomClaims(), // custom claims from JWTSubject method
+ $claims // custom claims from inline setter
+ ));
+ }
+
+ /**
+ * Build the claims to go into the refreshed token.
+ */
+ public function buildRefreshClaims(Payload $payload): array
+ {
+ return array_merge($payload->toArray(), [
+ Claims\JwtId::NAME => ClaimFactory::get(Claims\JwtId::NAME),
+ Claims\Expiration::NAME => timestamp($payload[Claims\Expiration::NAME])
+ ->addMinutes($this->getTTL())
+ ->getTimestamp(),
+ ]);
+ }
+
+ /**
+ * Get the builder options.
+ */
+ public function getOptions(): Options
+ {
+ return new Options([
+ Options::LEEWAY => $this->leeway,
+ Options::REQUIRED_CLAIMS => $this->requiredClaims,
+ Options::MAX_REFRESH_PERIOD => $this->maxRefreshPeriod,
+ Options::VALIDATORS => $this->customValidators,
+ ]);
+ }
+
+ /**
+ * Get the default claims to add.
+ */
+ protected function getDefaultClaims(): array
+ {
+ if ($key = array_search(Claims\Issuer::NAME, $this->defaultClaims)) {
+ $iss = Arr::pull($this->defaultClaims, $key);
+ }
+
+ return array_merge(
+ $this->defaultClaims,
+ // only add the iss claim if it exists in the default claims.
+ isset($iss) ? [$this->issClaim()] : [],
+ // only add exp claim if the ttl is not null
+ $this->getTTL() !== null ? [$this->expClaim()] : []
+ );
+ }
+
+ /**
+ * Get the issuer (iss) claim.
+ */
+ protected function issClaim(): Claims\Issuer
+ {
+ return ClaimFactory::get(
+ Claims\Issuer::NAME,
+ $this->request->getHost(),
+ $this->getOptions()
+ );
+ }
+
+ /**
+ * Get the expiration (exp) claim.
+ */
+ protected function expClaim(): Claims\Expiration
+ {
+ return ClaimFactory::get(
+ Claims\Expiration::NAME,
+ now()->addMinutes($this->getTTL())->getTimestamp(),
+ $this->getOptions()
+ );
+ }
+
+ /**
+ * Get the claims associated with a given subject.
+ */
+ protected function getClaimsForSubject(JWTSubject $subject): array
+ {
+ return array_merge([
+ Claims\Subject::NAME => $subject->getJWTIdentifier(),
+ ], $this->lockSubject ? [
+ Claims\HashedSubject::NAME => $this->hashSubjectModel($subject),
+ ] : []);
+ }
+
+ /**
+ * Hash the subject model and return it.
+ *
+ * @param string|object $model
+ */
+ public function hashSubjectModel($model): string
+ {
+ return sha1(is_object($model) ? get_class($model) : $model);
+ }
+
+ /**
+ * Set the request instance.
+ */
+ public function setRequest(Request $request): self
+ {
+ $this->request = $request;
+
+ return $this;
+ }
+
+ /**
+ * Get the request instance.
+ */
+ public function getRequest(): Request
+ {
+ return $this->request;
+ }
+
+ /**
+ * Set whether the subject should be "locked".
+ */
+ public function lockSubject(bool $lock): self
+ {
+ $this->lockSubject = $lock;
+
+ return $this;
+ }
+
+ /**
+ * Set the token ttl (in minutes).
+ */
+ public function setTTL(?int $ttl): self
+ {
+ $this->ttl = $ttl;
+
+ return $this;
+ }
+
+ /**
+ * Get the token ttl.
+ */
+ public function getTTL(): ?int
+ {
+ return $this->ttl;
+ }
+
+ /**
+ * Set the default claims.
+ */
+ public function setDefaultClaims(array $claims = []): self
+ {
+ $this->defaultClaims = $claims;
+
+ return $this;
+ }
+
+ /**
+ * Set the default claims.
+ */
+ public function setRequiredClaims(array $claims = []): self
+ {
+ $this->requiredClaims = $claims;
+
+ return $this;
+ }
+
+ /**
+ * Set the leeway in seconds.
+ */
+ public function setLeeway(int $leeway): self
+ {
+ $this->leeway = $leeway;
+
+ return $this;
+ }
+
+ /**
+ * Set the max refresh period in minutes.
+ */
+ public function setMaxRefreshPeriod(?int $period): self
+ {
+ $this->maxRefreshPeriod = $period;
+
+ return $this;
+ }
+
+ /**
+ * Add a custom validator.
+ */
+ public function setCustomValidator(string $key, callable $validator): self
+ {
+ $this->customValidators[$key] = $validator;
+
+ return $this;
+ }
+}
diff --git a/src/Claims/Audience.php b/src/Claims/Audience.php
index b34477558..d9fdc76ef 100644
--- a/src/Claims/Audience.php
+++ b/src/Claims/Audience.php
@@ -14,7 +14,7 @@
class Audience extends Claim
{
/**
- * {@inheritdoc}
+ * @var string
*/
- protected $name = 'aud';
+ const NAME = 'aud';
}
diff --git a/src/Claims/Claim.php b/src/Claims/Claim.php
index cb75f1d0d..514d8ec7a 100644
--- a/src/Claims/Claim.php
+++ b/src/Claims/Claim.php
@@ -1,5 +1,7 @@
value = $this->validateCreate($value);
@@ -70,12 +66,8 @@ public function getValue()
/**
* Set the claim name.
- *
- * @param string $name
- *
- * @return $this
*/
- public function setName($name)
+ public function setName(string $name): ClaimContract
{
$this->name = $name;
@@ -84,20 +76,18 @@ public function setName($name)
/**
* Get the claim name.
- *
- * @return string
*/
- public function getName()
+ public function getName(): string
{
- return $this->name;
+ return $this->name ?? static::NAME;
}
/**
- * Validate the claim in a standalone Claim context.
+ * Validate the claim for creation.
*
* @param mixed $value
*
- * @return bool
+ * @return mixed
*/
public function validateCreate($value)
{
@@ -105,56 +95,53 @@ public function validateCreate($value)
}
/**
- * Validate the Claim within a Payload context.
- *
- * @return bool
+ * Check the claim when verifying the validity of the payload.
*/
- public function validatePayload()
+ public function verify(): void
{
- return $this->getValue();
+ //
}
/**
- * Validate the Claim within a refresh context.
- *
- * @param int $refreshTTL
- *
- * @return bool
+ * Create an instance of the claim.
*/
- public function validateRefresh($refreshTTL)
+ public static function make($value = null): ClaimContract
{
- return $this->getValue();
+ return new static($value);
}
/**
* Checks if the value matches the claim.
*
* @param mixed $value
- * @param bool $strict
- *
- * @return bool
*/
- public function matches($value, $strict = true)
+ public function matches($value, bool $strict = true): bool
{
- return $strict ? $this->value === $value : $this->value == $value;
+ return $strict
+ ? $this->value === $value
+ : $this->value == $value;
+ }
+
+ /**
+ * Checks if the name matches the claim.
+ */
+ public function matchesName(string $name): bool
+ {
+ return $this->getName() === $name;
}
/**
* Convert the object into something JSON serializable.
- *
- * @return array
*/
- public function jsonSerialize()
+ public function jsonSerialize(): array
{
return $this->toArray();
}
/**
* Build a key value array comprising of the claim name and value.
- *
- * @return array
*/
- public function toArray()
+ public function toArray(): array
{
return [$this->getName() => $this->getValue()];
}
@@ -163,20 +150,16 @@ public function toArray()
* Get the claim as JSON.
*
* @param int $options
- *
- * @return string
*/
- public function toJson($options = JSON_UNESCAPED_SLASHES)
+ public function toJson($options = JSON_UNESCAPED_SLASHES): string
{
return json_encode($this->toArray(), $options);
}
/**
* Get the payload as a string.
- *
- * @return string
*/
- public function __toString()
+ public function __toString(): string
{
return $this->toJson();
}
diff --git a/src/Claims/Collection.php b/src/Claims/Collection.php
index 7ec2da156..aee1c6c00 100644
--- a/src/Claims/Collection.php
+++ b/src/Claims/Collection.php
@@ -1,5 +1,7 @@
getArrayableItems($items));
- }
-
/**
* Get a Claim instance by it's unique name.
- *
- * @param string $name
- * @param callable $callback
- * @param mixed $default
- *
- * @return \Tymon\JWTAuth\Claims\Claim
*/
- public function getByClaimName($name, callable $callback = null, $default = null)
+ public function getByClaimName(string $name, ...$args): ?ClaimContract
{
- return $this->filter(function (Claim $claim) use ($name) {
- return $claim->getName() === $name;
- })->first($callback, $default);
+ return $this->filter->matchesName($name)
+ ->first(...$args);
}
/**
- * Validate each claim under a given context.
- *
- * @param string $context
- *
- * @return $this
+ * Verify the validity of each claim.
*/
- public function validate($context = 'payload')
+ public function verify(): self
{
- $args = func_get_args();
- array_shift($args);
-
- $this->each(function ($claim) use ($context, $args) {
- call_user_func_array(
- [$claim, 'validate'.Str::ucfirst($context)],
- $args
- );
- });
+ $this->each->verify();
return $this;
}
@@ -70,42 +41,31 @@ public function validate($context = 'payload')
* Determine if the Collection contains all of the given keys.
*
* @param mixed $claims
- *
- * @return bool
*/
- public function hasAllClaims($claims)
+ public function hasAllClaims($claims): bool
{
- return count($claims) && (new static($claims))->diff($this->keys())->isEmpty();
+ if (empty($claims)) {
+ return true;
+ }
+
+ return (new static($claims))
+ ->diff($this->keys())
+ ->isEmpty();
}
/**
* Get the claims as key/val array.
- *
- * @return array
*/
- public function toPlainArray()
+ public function toPlainArray(): array
{
- return $this->map(function (Claim $claim) {
- return $claim->getValue();
- })->toArray();
+ return $this->map->getValue()
+ ->toArray();
}
/**
* {@inheritdoc}
*/
- protected function getArrayableItems($items)
- {
- return $this->sanitizeClaims($items);
- }
-
- /**
- * Ensure that the given claims array is keyed by the claim name.
- *
- * @param mixed $items
- *
- * @return array
- */
- private function sanitizeClaims($items)
+ protected function getArrayableItems($items): array
{
$claims = [];
foreach ($items as $key => $value) {
diff --git a/src/Claims/Custom.php b/src/Claims/Custom.php
index 0e43de54f..15e0d42d8 100644
--- a/src/Claims/Custom.php
+++ b/src/Claims/Custom.php
@@ -1,5 +1,7 @@
setName($name);
diff --git a/src/Claims/DatetimeTrait.php b/src/Claims/DatetimeTrait.php
index dd2b77d5a..9617f5975 100644
--- a/src/Claims/DatetimeTrait.php
+++ b/src/Claims/DatetimeTrait.php
@@ -1,5 +1,7 @@
add($value);
+ $value = now()->add($value);
}
if ($value instanceof DateTimeInterface) {
@@ -61,39 +70,71 @@ public function validateCreate($value)
/**
* Determine whether the value is in the future.
- *
- * @param mixed $value
- *
- * @return bool
*/
- protected function isFuture($value)
+ protected function isFuture(int $value): bool
{
- return Utils::isFuture($value, $this->leeway);
+ return is_future($value, $this->leeway);
}
/**
* Determine whether the value is in the past.
- *
- * @param mixed $value
- *
- * @return bool
*/
- protected function isPast($value)
+ protected function isPast(int $value): bool
{
- return Utils::isPast($value, $this->leeway);
+ return is_past($value, $this->leeway);
}
/**
* Set the leeway in seconds.
- *
- * @param int $leeway
- *
- * @return $this
*/
- public function setLeeway($leeway)
+ public function setLeeway(int $leeway): self
{
$this->leeway = $leeway;
return $this;
}
+
+ /**
+ * Get the leeway.
+ */
+ public function getLeeway(): int
+ {
+ return $this->leeway;
+ }
+
+ /**
+ * Set the max refresh period in minutes.
+ */
+ public function setMaxRefreshPeriod(?int $period): self
+ {
+ $this->maxRefreshPeriod = $period;
+
+ return $this;
+ }
+
+ /**
+ * Get the max refresh period.
+ */
+ public function getMaxRefreshPeriod(): ?int
+ {
+ return $this->maxRefreshPeriod;
+ }
+
+ /**
+ * Get the claim value as a Carbon instance.
+ */
+ public function asCarbon(): Carbon
+ {
+ return timestamp($this->getValue());
+ }
+
+ /**
+ * Get the claim value as a CarbonInterval instance.
+ */
+ public function asCarbonInterval(): CarbonInterval
+ {
+ return now()
+ ->diffAsCarbonInterval($this->asCarbon()->endOfSecond())
+ ->microseconds(0);
+ }
}
diff --git a/src/Claims/Expiration.php b/src/Claims/Expiration.php
index d15d4b24e..bbdac5f9b 100644
--- a/src/Claims/Expiration.php
+++ b/src/Claims/Expiration.php
@@ -18,14 +18,14 @@ class Expiration extends Claim
use DatetimeTrait;
/**
- * {@inheritdoc}
+ * @var string
*/
- protected $name = 'exp';
+ const NAME = 'exp';
/**
* {@inheritdoc}
*/
- public function validatePayload()
+ public function verify(): void
{
if ($this->isPast($this->getValue())) {
throw new TokenExpiredException('Token has expired');
diff --git a/src/Claims/Factory.php b/src/Claims/Factory.php
index 084732b48..6dc292ab2 100644
--- a/src/Claims/Factory.php
+++ b/src/Claims/Factory.php
@@ -1,5 +1,7 @@
Audience::class,
- 'exp' => Expiration::class,
- 'iat' => IssuedAt::class,
- 'iss' => Issuer::class,
- 'jti' => JwtId::class,
- 'nbf' => NotBefore::class,
- 'sub' => Subject::class,
+ * The class map.
+ */
+ private static array $classMap = [
+ Audience::NAME => Audience::class,
+ Expiration::NAME => Expiration::class,
+ IssuedAt::NAME => IssuedAt::class,
+ Issuer::NAME => Issuer::class,
+ JwtId::NAME => JwtId::class,
+ NotBefore::NAME => NotBefore::class,
+ Subject::NAME => Subject::class,
+ HashedSubject::NAME => HashedSubject::class,
];
- /**
- * Constructor.
- *
- * @param \Illuminate\Http\Request $request
- *
- * @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
- * @param mixed $value
- *
- * @return \Tymon\JWTAuth\Claims\Claim
*/
- public function get($name, $value)
+ public static function get(string $name, $value = null, ?Options $options = null): ClaimContract
{
- if ($this->has($name)) {
- $claim = new $this->classMap[$name]($value);
+ $options ??= new Options();
- return method_exists($claim, 'setLeeway') ?
- $claim->setLeeway($this->leeway) :
- $claim;
- }
+ $claim = static::has($name)
+ ? call_user_func([static::$classMap[$name], 'make'], $value)
+ : new Custom($name, $value);
- return new Custom($name, $value);
+ return static::applyClaimMethods($claim, [
+ 'setLeeway' => $options->leeway(),
+ 'setMaxRefreshPeriod' => $options->maxRefreshPeriod(),
+ ]);
}
/**
* 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 \Tymon\JWTAuth\Claims\Claim
- */
- public function make($name)
- {
- return $this->get($name, $this->$name());
- }
-
- /**
- * Get the Issuer (iss) claim.
- *
- * @return string
*/
- public function iss()
+ public static function has(string $name): bool
{
- return $this->request->url();
+ return array_key_exists($name, static::$classMap);
}
/**
- * Get the Issued At (iat) claim.
- *
- * @return int
+ * Apply a multiple methods to the given claim if they exist.
*/
- public function iat()
+ protected static function applyClaimMethods(ClaimContract $claim, array $data): ClaimContract
{
- 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.
- *
- * @param \Illuminate\Http\Request $request
- *
- * @return $this
- */
- public function setRequest(Request $request)
- {
- $this->request = $request;
-
- return $this;
- }
-
- /**
- * Set the token ttl (in minutes).
- *
- * @param int $ttl
- *
- * @return $this
- */
- public function setTTL($ttl)
- {
- $this->ttl = $ttl;
-
- return $this;
- }
-
- /**
- * Get the token ttl.
- *
- * @return int
- */
- public function getTTL()
- {
- return $this->ttl;
- }
-
- /**
- * Set the leeway in seconds.
- *
- * @param int $leeway
- *
- * @return $this
- */
- public function setLeeway($leeway)
- {
- $this->leeway = $leeway;
+ foreach ($data as $method => $value) {
+ $claim = method_exists($claim, $method)
+ ? $claim->{$method}($value)
+ : $claim;
+ }
- return $this;
+ return $claim;
}
}
diff --git a/src/Claims/HashedSubject.php b/src/Claims/HashedSubject.php
new file mode 100644
index 000000000..6bc2e2046
--- /dev/null
+++ b/src/Claims/HashedSubject.php
@@ -0,0 +1,20 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Tymon\JWTAuth\Claims;
+
+class HashedSubject extends Claim
+{
+ /**
+ * @var string
+ */
+ const NAME = 'hsu';
+}
diff --git a/src/Claims/IssuedAt.php b/src/Claims/IssuedAt.php
index 6253fe88d..3453eb58b 100644
--- a/src/Claims/IssuedAt.php
+++ b/src/Claims/IssuedAt.php
@@ -11,9 +11,12 @@
namespace Tymon\JWTAuth\Claims;
+use Tymon\JWTAuth\Contracts\Claim as ClaimContract;
use Tymon\JWTAuth\Exceptions\InvalidClaimException;
use Tymon\JWTAuth\Exceptions\TokenExpiredException;
use Tymon\JWTAuth\Exceptions\TokenInvalidException;
+use function Tymon\JWTAuth\Support\now;
+use function Tymon\JWTAuth\Support\timestamp;
class IssuedAt extends Claim
{
@@ -22,9 +25,9 @@ class IssuedAt extends Claim
}
/**
- * {@inheritdoc}
+ * @var string
*/
- protected $name = 'iat';
+ const NAME = 'iat';
/**
* {@inheritdoc}
@@ -43,20 +46,24 @@ public function validateCreate($value)
/**
* {@inheritdoc}
*/
- public function validatePayload()
+ public function verify(): void
{
if ($this->isFuture($this->getValue())) {
throw new TokenInvalidException('Issued At (iat) timestamp cannot be in the future');
}
+
+ if ($this->maxRefreshPeriod !== null) {
+ if (timestamp($this->getValue())->addMinutes($this->maxRefreshPeriod)->isFuture()) {
+ throw new TokenExpiredException('Token has expired');
+ }
+ }
}
/**
* {@inheritdoc}
*/
- public function validateRefresh($refreshTTL)
+ public static function make($value = null): ClaimContract
{
- if ($this->isPast($this->getValue() + $refreshTTL * 60)) {
- throw new TokenExpiredException('Token has expired and can no longer be refreshed');
- }
+ return new static($value ?? now()->getTimestamp());
}
}
diff --git a/src/Claims/Issuer.php b/src/Claims/Issuer.php
index d1d68cde4..8451f7ec6 100644
--- a/src/Claims/Issuer.php
+++ b/src/Claims/Issuer.php
@@ -14,7 +14,7 @@
class Issuer extends Claim
{
/**
- * {@inheritdoc}
+ * @var string
*/
- protected $name = 'iss';
+ const NAME = 'iss';
}
diff --git a/src/Claims/JwtId.php b/src/Claims/JwtId.php
index e1b93fc9f..a63d0a2c4 100644
--- a/src/Claims/JwtId.php
+++ b/src/Claims/JwtId.php
@@ -11,10 +11,21 @@
namespace Tymon\JWTAuth\Claims;
+use Illuminate\Support\Str;
+use Tymon\JWTAuth\Contracts\Claim as ClaimContract;
+
class JwtId extends Claim
{
+ /**
+ * @var string
+ */
+ const NAME = 'jti';
+
/**
* {@inheritdoc}
*/
- protected $name = 'jti';
+ public static function make($value = null): ClaimContract
+ {
+ return new static($value ?? Str::random(16));
+ }
}
diff --git a/src/Claims/NotBefore.php b/src/Claims/NotBefore.php
index 47fba3de7..d90b43107 100644
--- a/src/Claims/NotBefore.php
+++ b/src/Claims/NotBefore.php
@@ -11,41 +11,34 @@
namespace Tymon\JWTAuth\Claims;
-use Tymon\JWTAuth\Exceptions\InvalidClaimException;
+use Tymon\JWTAuth\Contracts\Claim as ClaimContract;
use Tymon\JWTAuth\Exceptions\TokenInvalidException;
+use function Tymon\JWTAuth\Support\now;
class NotBefore extends Claim
{
- use DatetimeTrait {
- validateCreate as commonValidateCreate;
- }
+ use DatetimeTrait;
/**
- * {@inheritdoc}
+ * @var string
*/
- protected $name = 'nbf';
+ const NAME = 'nbf';
/**
* {@inheritdoc}
*/
- public function validateCreate($value)
+ public function verify(): void
{
- $this->commonValidateCreate($value);
-
- if ($this->isFuture($value)) {
- throw new InvalidClaimException($this);
+ if ($this->isFuture($this->getValue())) {
+ throw new TokenInvalidException('Not Before (nbf) timestamp cannot be in the future');
}
-
- return $value;
}
/**
* {@inheritdoc}
*/
- public function validatePayload()
+ public static function make($value = null): ClaimContract
{
- if ($this->isFuture($this->getValue())) {
- throw new TokenInvalidException('Not Before (nbf) timestamp cannot be in the future');
- }
+ return new static($value ?? now()->getTimestamp());
}
}
diff --git a/src/Claims/Subject.php b/src/Claims/Subject.php
index 8ecb93d86..0c21bd43e 100644
--- a/src/Claims/Subject.php
+++ b/src/Claims/Subject.php
@@ -14,7 +14,7 @@
class Subject extends Claim
{
/**
- * {@inheritdoc}
+ * @var string
*/
- protected $name = 'sub';
+ const NAME = 'sub';
}
diff --git a/src/Console/JWTGenerateSecretCommand.php b/src/Console/JWTGenerateSecretCommand.php
index 24dd7fe04..ba953be2b 100644
--- a/src/Console/JWTGenerateSecretCommand.php
+++ b/src/Console/JWTGenerateSecretCommand.php
@@ -11,8 +11,8 @@
namespace Tymon\JWTAuth\Console;
-use Illuminate\Support\Str;
use Illuminate\Console\Command;
+use Illuminate\Support\Str;
class JWTGenerateSecretCommand extends Command
{
@@ -30,7 +30,7 @@ class JWTGenerateSecretCommand extends Command
*
* @var string
*/
- protected $description = 'Set the JWTAuth secret key used to sign the tokens';
+ protected $description = 'Set the secret key used to sign the tokens';
/**
* Execute the console command.
@@ -39,7 +39,7 @@ class JWTGenerateSecretCommand extends Command
*/
public function handle()
{
- $key = Str::random(32);
+ $key = Str::random(64);
if ($this->option('show')) {
$this->comment($key);
@@ -47,7 +47,7 @@ public function handle()
return;
}
- if (file_exists($path = $this->envPath()) === false) {
+ if (file_exists($path = $this->laravel->environmentFilePath()) === false) {
return $this->displayKey($key);
}
@@ -73,12 +73,8 @@ public function handle()
/**
* Display the key.
- *
- * @param string $key
- *
- * @return void
*/
- protected function displayKey($key)
+ protected function displayKey(string $key): void
{
$this->laravel['config']['jwt.secret'] = $key;
@@ -87,27 +83,11 @@ protected function displayKey($key)
/**
* Check if the modification is confirmed.
- *
- * @return bool
*/
- protected function isConfirmed()
+ protected function isConfirmed(): bool
{
return $this->option('force') ? true : $this->confirm(
'This will invalidate all existing tokens. Are you sure you want to override the secret key?'
);
}
-
- /**
- * Get the .env file path.
- *
- * @return string
- */
- protected function envPath()
- {
- if (method_exists($this->laravel, 'environmentFilePath')) {
- return $this->laravel->environmentFilePath();
- }
-
- return $this->laravel->basePath('.env');
- }
}
diff --git a/src/Contracts/Claim.php b/src/Contracts/Claim.php
index 9d6e9a94e..15b3f657f 100644
--- a/src/Contracts/Claim.php
+++ b/src/Contracts/Claim.php
@@ -1,5 +1,7 @@
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Tymon\JWTAuth\Contracts\Providers;
-
-interface Auth
-{
- /**
- * Check a user's credentials.
- *
- * @param array $credentials
- *
- * @return mixed
- */
- public function byCredentials(array $credentials);
-
- /**
- * Authenticate a user via the id.
- *
- * @param mixed $id
- *
- * @return mixed
- */
- public function byId($id);
-
- /**
- * Get the currently authenticated user.
- *
- * @return mixed
- */
- public function user();
-}
diff --git a/src/Contracts/Providers/JWT.php b/src/Contracts/Providers/JWT.php
index 7065a8791..3ac18e1af 100644
--- a/src/Contracts/Providers/JWT.php
+++ b/src/Contracts/Providers/JWT.php
@@ -1,5 +1,7 @@
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Tymon\JWTAuth\Events;
+
+class JWTAttempt extends JWTEvent
+{
+}
diff --git a/src/Events/JWTEvent.php b/src/Events/JWTEvent.php
new file mode 100644
index 000000000..052f4cf67
--- /dev/null
+++ b/src/Events/JWTEvent.php
@@ -0,0 +1,41 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Tymon\JWTAuth\Events;
+
+abstract class JWTEvent
+{
+ /**
+ * The authenticated user.
+ *
+ * @var \Illuminate\Contracts\Auth\Authenticatable
+ */
+ public $user;
+
+ /**
+ * @var \Tymon\JWTAuth\Token|string|null
+ */
+ public $token;
+
+ /**
+ * Constructor.
+ *
+ * @param \Illuminate\Contracts\Auth\Authenticatable $user
+ * @param \Tymon\JWTAuth\Token|string|null $token
+ *
+ * @return void
+ */
+ public function __construct($user, $token = null)
+ {
+ $this->token = $token;
+ $this->user = $user;
+ }
+}
diff --git a/src/Events/JWTInvalidate.php b/src/Events/JWTInvalidate.php
new file mode 100644
index 000000000..9a1ebd0f8
--- /dev/null
+++ b/src/Events/JWTInvalidate.php
@@ -0,0 +1,16 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Tymon\JWTAuth\Events;
+
+class JWTInvalidate extends JWTEvent
+{
+}
diff --git a/src/Events/JWTLogin.php b/src/Events/JWTLogin.php
new file mode 100644
index 000000000..aae06a07c
--- /dev/null
+++ b/src/Events/JWTLogin.php
@@ -0,0 +1,16 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Tymon\JWTAuth\Events;
+
+class JWTLogin extends JWTEvent
+{
+}
diff --git a/src/Events/JWTLogout.php b/src/Events/JWTLogout.php
new file mode 100644
index 000000000..ed801827f
--- /dev/null
+++ b/src/Events/JWTLogout.php
@@ -0,0 +1,16 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Tymon\JWTAuth\Events;
+
+class JWTLogout extends JWTEvent
+{
+}
diff --git a/src/Events/JWTRefresh.php b/src/Events/JWTRefresh.php
new file mode 100644
index 000000000..09c6a3223
--- /dev/null
+++ b/src/Events/JWTRefresh.php
@@ -0,0 +1,16 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Tymon\JWTAuth\Events;
+
+class JWTRefresh extends JWTEvent
+{
+}
diff --git a/src/Exceptions/InvalidClaimException.php b/src/Exceptions/InvalidClaimException.php
index 7a17feb72..f5580736f 100644
--- a/src/Exceptions/InvalidClaimException.php
+++ b/src/Exceptions/InvalidClaimException.php
@@ -18,14 +18,8 @@ class InvalidClaimException extends JWTException
{
/**
* Constructor.
- *
- * @param \Tymon\JWTAuth\Claims\Claim $claim
- * @param int $code
- * @param \Exception|null $previous
- *
- * @return void
*/
- public function __construct(Claim $claim, $code = 0, Exception $previous = null)
+ public function __construct(Claim $claim, int $code = 0, ?Exception $previous = null)
{
parent::__construct('Invalid value provided for claim ['.$claim->getName().']', $code, $previous);
}
diff --git a/src/Exceptions/PayloadException.php b/src/Exceptions/PayloadException.php
index dfcc69f24..18a6234fe 100644
--- a/src/Exceptions/PayloadException.php
+++ b/src/Exceptions/PayloadException.php
@@ -13,5 +13,8 @@
class PayloadException extends JWTException
{
- //
+ /**
+ * {@inheritdoc}
+ */
+ protected $message = 'The payload is immutable';
}
diff --git a/src/Exceptions/TokenBlacklistedException.php b/src/Exceptions/TokenBlacklistedException.php
index f53de8a1c..f4a7ed61e 100644
--- a/src/Exceptions/TokenBlacklistedException.php
+++ b/src/Exceptions/TokenBlacklistedException.php
@@ -13,5 +13,8 @@
class TokenBlacklistedException extends TokenInvalidException
{
- //
+ /**
+ * {@inheritdoc}
+ */
+ protected $message = 'The token has been blacklisted';
}
diff --git a/src/Exceptions/TokenExpiredException.php b/src/Exceptions/TokenExpiredException.php
index 99d466c06..8f243156c 100644
--- a/src/Exceptions/TokenExpiredException.php
+++ b/src/Exceptions/TokenExpiredException.php
@@ -13,5 +13,8 @@
class TokenExpiredException extends JWTException
{
- //
+ /**
+ * {@inheritdoc}
+ */
+ protected $message = 'The token has expired';
}
diff --git a/src/Exceptions/TokenInvalidException.php b/src/Exceptions/TokenInvalidException.php
index 8c6ced478..816dd3316 100644
--- a/src/Exceptions/TokenInvalidException.php
+++ b/src/Exceptions/TokenInvalidException.php
@@ -13,5 +13,8 @@
class TokenInvalidException extends JWTException
{
- //
+ /**
+ * {@inheritdoc}
+ */
+ protected $message = 'The token is invalid';
}
diff --git a/src/Exceptions/UserNotDefinedException.php b/src/Exceptions/UserNotDefinedException.php
index 658713c9b..965cf579b 100644
--- a/src/Exceptions/UserNotDefinedException.php
+++ b/src/Exceptions/UserNotDefinedException.php
@@ -13,5 +13,8 @@
class UserNotDefinedException extends JWTException
{
- //
+ /**
+ * {@inheritdoc}
+ */
+ protected $message = 'User not defined';
}
diff --git a/src/Facades/JWTFactory.php b/src/Facades/JWTFactory.php
deleted file mode 100644
index f43ff4695..000000000
--- a/src/Facades/JWTFactory.php
+++ /dev/null
@@ -1,27 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Tymon\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';
- }
-}
diff --git a/src/Facades/JWTAuth.php b/src/Facades/JWTManager.php
similarity index 86%
rename from src/Facades/JWTAuth.php
rename to src/Facades/JWTManager.php
index 419b5903f..a8d3d427f 100644
--- a/src/Facades/JWTAuth.php
+++ b/src/Facades/JWTManager.php
@@ -13,7 +13,7 @@
use Illuminate\Support\Facades\Facade;
-class JWTAuth extends Facade
+class JWTManager extends Facade
{
/**
* Get the registered name of the component.
@@ -22,6 +22,6 @@ class JWTAuth extends Facade
*/
protected static function getFacadeAccessor()
{
- return 'tymon.jwt.auth';
+ return 'tymon.jwt.manager';
}
}
diff --git a/src/Factory.php b/src/Factory.php
index 5f5f90d47..ec546c655 100644
--- a/src/Factory.php
+++ b/src/Factory.php
@@ -1,5 +1,7 @@
claimFactory = $claimFactory;
- $this->validator = $validator;
- $this->claims = new Collection;
- }
-
/**
- * Create the Payload instance.
- *
- * @param bool $resetClaims
- *
- * @return \Tymon\JWTAuth\Payload
+ * Create a Payload instance.
*/
- public function make($resetClaims = false)
+ public static function make($claims = [], ?Options $options = null): Payload
{
- $payload = $this->withClaims($this->buildClaimsCollection());
+ $claims = Collection::make($claims)
+ ->map(function ($value, $key) use ($options) {
+ if ($value instanceof Claim) {
+ return $value;
+ }
- if ($resetClaims) {
- $this->emptyClaims();
- }
+ if (! is_string($key)) {
+ $key = $value;
+ $value = null;
+ }
- return $payload;
- }
-
- /**
- * Empty the claims collection.
- *
- * @return $this
- */
- public function emptyClaims()
- {
- $this->claims = new Collection;
-
- return $this;
- }
-
- /**
- * Add an array of claims to the Payload.
- *
- * @param array $claims
- *
- * @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
- * @param mixed $value
- *
- * @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 ($this->claimFactory->getTTL() === null && $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 \Tymon\JWTAuth\Claims\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 \Tymon\JWTAuth\Claims\Collection
- */
- public function buildClaimsCollection()
- {
- return $this->buildClaims()->resolveClaims();
- }
-
- /**
- * Get a Payload instance with a claims collection.
- *
- * @param \Tymon\JWTAuth\Claims\Collection $claims
- *
- * @return \Tymon\JWTAuth\Payload
- */
- public function withClaims(Collection $claims)
- {
- return new Payload($claims, $this->validator, $this->refreshFlow);
- }
-
- /**
- * Set the default claims to be added to the Payload.
- *
- * @param array $claims
- *
- * @return $this
- */
- public function setDefaultClaims(array $claims)
- {
- $this->defaultClaims = $claims;
-
- return $this;
- }
-
- /**
- * Helper to set the ttl.
- *
- * @param int $ttl
- *
- * @return $this
- */
- public function setTTL($ttl)
- {
- $this->claimFactory->setTTL($ttl);
-
- return $this;
- }
-
- /**
- * Helper to get the ttl.
- *
- * @return int
- */
- public function getTTL()
- {
- return $this->claimFactory->getTTL();
- }
-
- /**
- * Get the default claims.
- *
- * @return array
- */
- public function getDefaultClaims()
- {
- return $this->defaultClaims;
- }
-
- /**
- * Get the PayloadValidator instance.
- *
- * @return \Tymon\JWTAuth\Validators\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 ClaimFactory::get($key, $value, $options);
+ });
- return $this;
+ // Validate the claims
+ return PayloadValidator::check($claims, $options);
}
}
diff --git a/src/Http/Middleware/Authenticate.php b/src/Http/Middleware/Authenticate.php
deleted file mode 100644
index cd3b99ae1..000000000
--- a/src/Http/Middleware/Authenticate.php
+++ /dev/null
@@ -1,34 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Tymon\JWTAuth\Http\Middleware;
-
-use Closure;
-
-class Authenticate extends BaseMiddleware
-{
- /**
- * Handle an incoming request.
- *
- * @param \Illuminate\Http\Request $request
- * @param \Closure $next
- *
- * @throws \Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException
- *
- * @return mixed
- */
- public function handle($request, Closure $next)
- {
- $this->authenticate($request);
-
- return $next($request);
- }
-}
diff --git a/src/Http/Middleware/AuthenticateAndRenew.php b/src/Http/Middleware/AuthenticateAndRenew.php
deleted file mode 100644
index a40bba5ce..000000000
--- a/src/Http/Middleware/AuthenticateAndRenew.php
+++ /dev/null
@@ -1,37 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Tymon\JWTAuth\Http\Middleware;
-
-use Closure;
-
-class AuthenticateAndRenew extends BaseMiddleware
-{
- /**
- * Handle an incoming request.
- *
- * @param \Illuminate\Http\Request $request
- * @param \Closure $next
- *
- * @throws \Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException
- *
- * @return mixed
- */
- public function handle($request, Closure $next)
- {
- $this->authenticate($request);
-
- $response = $next($request);
-
- // Send the refreshed token back to the client.
- return $this->setAuthenticationHeader($response);
- }
-}
diff --git a/src/Http/Middleware/BaseMiddleware.php b/src/Http/Middleware/BaseMiddleware.php
deleted file mode 100644
index 6cd851b64..000000000
--- a/src/Http/Middleware/BaseMiddleware.php
+++ /dev/null
@@ -1,93 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Tymon\JWTAuth\Http\Middleware;
-
-use Tymon\JWTAuth\JWTAuth;
-use Illuminate\Http\Request;
-use Tymon\JWTAuth\Exceptions\JWTException;
-use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException;
-
-abstract class BaseMiddleware
-{
- /**
- * The JWT Authenticator.
- *
- * @var \Tymon\JWTAuth\JWTAuth
- */
- protected $auth;
-
- /**
- * Create a new BaseMiddleware instance.
- *
- * @param \Tymon\JWTAuth\JWTAuth $auth
- *
- * @return void
- */
- public function __construct(JWTAuth $auth)
- {
- $this->auth = $auth;
- }
-
- /**
- * Check the request for the presence of a token.
- *
- * @param \Illuminate\Http\Request $request
- *
- * @throws \Symfony\Component\HttpKernel\Exception\BadRequestHttpException
- *
- * @return void
- */
- 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.
- *
- * @param \Illuminate\Http\Request $request
- *
- * @throws \Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException
- *
- * @return void
- */
- 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 \Illuminate\Http\Response|\Illuminate\Http\JsonResponse $response
- * @param string|null $token
- *
- * @return \Illuminate\Http\Response|\Illuminate\Http\JsonResponse
- */
- protected function setAuthenticationHeader($response, $token = null)
- {
- $token = $token ?: $this->auth->refresh();
- $response->headers->set('Authorization', 'Bearer '.$token);
-
- return $response;
- }
-}
diff --git a/src/Http/Middleware/Check.php b/src/Http/Middleware/Check.php
deleted file mode 100644
index 38d2bfb5a..000000000
--- a/src/Http/Middleware/Check.php
+++ /dev/null
@@ -1,39 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Tymon\JWTAuth\Http\Middleware;
-
-use Closure;
-use Exception;
-
-class Check extends BaseMiddleware
-{
- /**
- * Handle an incoming request.
- *
- * @param \Illuminate\Http\Request $request
- * @param \Closure $next
- *
- * @return mixed
- */
- public function handle($request, Closure $next)
- {
- if ($this->auth->parser()->setRequest($request)->hasToken()) {
- try {
- $this->auth->parseToken()->authenticate();
- } catch (Exception $e) {
- //
- }
- }
-
- return $next($request);
- }
-}
diff --git a/src/Http/Middleware/RefreshToken.php b/src/Http/Middleware/RefreshToken.php
deleted file mode 100644
index c70007fe5..000000000
--- a/src/Http/Middleware/RefreshToken.php
+++ /dev/null
@@ -1,45 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Tymon\JWTAuth\Http\Middleware;
-
-use Closure;
-use Tymon\JWTAuth\Exceptions\JWTException;
-use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException;
-
-class RefreshToken extends BaseMiddleware
-{
- /**
- * Handle an incoming request.
- *
- * @param \Illuminate\Http\Request $request
- * @param \Closure $next
- *
- * @throws \Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException
- *
- * @return mixed
- */
- 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);
- }
-}
diff --git a/src/Http/Parser/AuthHeaders.php b/src/Http/Parser/AuthHeaders.php
index 53808f032..8206c39a1 100644
--- a/src/Http/Parser/AuthHeaders.php
+++ b/src/Http/Parser/AuthHeaders.php
@@ -18,54 +18,33 @@ class AuthHeaders implements ParserContract
{
/**
* The header name.
- *
- * @var string
*/
- protected $header = 'authorization';
+ protected string $header = 'authorization';
/**
* The header prefix.
- *
- * @var string
*/
- protected $prefix = 'bearer';
-
- /**
- * Attempt to parse the token from some other possible headers.
- *
- * @param \Illuminate\Http\Request $request
- *
- * @return null|string
- */
- protected function fromAltHeaders(Request $request)
- {
- return $request->server->get('HTTP_AUTHORIZATION') ?: $request->server->get('REDIRECT_HTTP_AUTHORIZATION');
- }
+ protected string $prefix = 'bearer';
/**
* Try to parse the token from the request header.
- *
- * @param \Illuminate\Http\Request $request
- *
- * @return null|string
*/
- public function parse(Request $request)
+ public function parse(Request $request): ?string
{
- $header = $request->headers->get($this->header) ?: $this->fromAltHeaders($request);
+ $header = $request->headers->get($this->header)
+ ?: $this->fromAltHeaders($request);
if ($header && preg_match('/'.$this->prefix.'\s*(\S+)\b/i', $header, $matches)) {
return $matches[1];
}
+
+ return null;
}
/**
* Set the header name.
- *
- * @param string $headerName
- *
- * @return $this
*/
- public function setHeaderName($headerName)
+ public function setHeaderName(string $headerName): self
{
$this->header = $headerName;
@@ -74,15 +53,20 @@ public function setHeaderName($headerName)
/**
* Set the header prefix.
- *
- * @param string $headerPrefix
- *
- * @return $this
*/
- public function setHeaderPrefix($headerPrefix)
+ public function setHeaderPrefix(string $headerPrefix): self
{
$this->prefix = $headerPrefix;
return $this;
}
+
+ /**
+ * Attempt to parse the token from some other possible headers.
+ */
+ protected function fromAltHeaders(Request $request): ?string
+ {
+ return $request->server->get('HTTP_AUTHORIZATION')
+ ?? $request->server->get('REDIRECT_HTTP_AUTHORIZATION');
+ }
}
diff --git a/src/Http/Parser/Cookies.php b/src/Http/Parser/Cookies.php
index ad3d5e211..3ebf78088 100644
--- a/src/Http/Parser/Cookies.php
+++ b/src/Http/Parser/Cookies.php
@@ -21,24 +21,21 @@ class Cookies implements ParserContract
/**
* Decrypt or not the cookie while parsing.
- *
- * @var bool
*/
- private $decrypt;
+ private bool $decrypt;
- public function __construct($decrypt = true)
+ /**
+ * Constructor.
+ */
+ public function __construct(bool $decrypt = true)
{
$this->decrypt = $decrypt;
}
/**
* Try to parse the token from the request cookies.
- *
- * @param \Illuminate\Http\Request $request
- *
- * @return null|string
*/
- public function parse(Request $request)
+ public function parse(Request $request): ?string
{
if ($this->decrypt && $request->hasCookie($this->key)) {
return Crypt::decrypt($request->cookie($this->key));
diff --git a/src/Http/Parser/InputSource.php b/src/Http/Parser/InputSource.php
index d5692bf0c..df7faf5c3 100644
--- a/src/Http/Parser/InputSource.php
+++ b/src/Http/Parser/InputSource.php
@@ -20,12 +20,8 @@ class InputSource implements ParserContract
/**
* Try to parse the token from the request input source.
- *
- * @param \Illuminate\Http\Request $request
- *
- * @return null|string
*/
- public function parse(Request $request)
+ public function parse(Request $request): ?string
{
return $request->input($this->key);
}
diff --git a/src/Http/Parser/KeyTrait.php b/src/Http/Parser/KeyTrait.php
index e65da38e6..f6dfd8ca1 100644
--- a/src/Http/Parser/KeyTrait.php
+++ b/src/Http/Parser/KeyTrait.php
@@ -15,19 +15,13 @@ trait KeyTrait
{
/**
* The key.
- *
- * @var string
*/
- protected $key = 'token';
+ protected string $key = 'token';
/**
* Set the key.
- *
- * @param string $key
- *
- * @return $this
*/
- public function setKey($key)
+ public function setKey(string $key): self
{
$this->key = $key;
@@ -36,10 +30,8 @@ public function setKey($key)
/**
* Get the key.
- *
- * @return string
*/
- public function getKey()
+ public function getKey(): string
{
return $this->key;
}
diff --git a/src/Http/Parser/LumenRouteParams.php b/src/Http/Parser/LumenRouteParams.php
index d9511f891..c829f15b3 100644
--- a/src/Http/Parser/LumenRouteParams.php
+++ b/src/Http/Parser/LumenRouteParams.php
@@ -11,19 +11,15 @@
namespace Tymon\JWTAuth\Http\Parser;
-use Illuminate\Support\Arr;
use Illuminate\Http\Request;
+use Illuminate\Support\Arr;
class LumenRouteParams extends RouteParams
{
/**
* Try to get the token from the route parameters.
- *
- * @param \Illuminate\Http\Request $request
- *
- * @return null|string
*/
- public function parse(Request $request)
+ public function parse(Request $request): ?string
{
// WARNING: Only use this parser if you know what you're doing!
// It will only work with poorly-specified aspects of certain Lumen releases.
diff --git a/src/Http/Parser/Parser.php b/src/Http/Parser/Parser.php
index fff0d2d0a..7e03d535c 100644
--- a/src/Http/Parser/Parser.php
+++ b/src/Http/Parser/Parser.php
@@ -12,30 +12,23 @@
namespace Tymon\JWTAuth\Http\Parser;
use Illuminate\Http\Request;
+use Illuminate\Support\Arr;
+use Tymon\JWTAuth\Contracts\Http\Parser as ParserContract;
class Parser
{
/**
- * The chain.
- *
- * @var array
+ * The request.
*/
- private $chain;
+ protected Request $request;
/**
- * The request.
- *
- * @var \Illuminate\Http\Request
+ * The chain.
*/
- protected $request;
+ private array $chain;
/**
* Constructor.
- *
- * @param \Illuminate\Http\Request $request
- * @param array $chain
- *
- * @return void
*/
public function __construct(Request $request, array $chain = [])
{
@@ -45,22 +38,16 @@ public function __construct(Request $request, array $chain = [])
/**
* Get the parser chain.
- *
- * @return array
*/
- public function getChain()
+ public function getChain(): array
{
return $this->chain;
}
/**
* Set the order of the parser chain.
- *
- * @param array $chain
- *
- * @return $this
*/
- public function setChain(array $chain)
+ public function setChain(array $chain): self
{
$this->chain = $chain;
@@ -69,52 +56,58 @@ public function setChain(array $chain)
/**
* Alias for setting the order of the chain.
- *
- * @param array $chain
- *
- * @return $this
*/
- public function setChainOrder(array $chain)
+ public function setChainOrder(array $chain): self
{
return $this->setChain($chain);
}
+ /**
+ * Get a parser by key.
+ */
+ public function get(string $key): ?ParserContract
+ {
+ return Arr::get($this->chain, $key);
+ }
+
/**
* Iterate through the parsers and attempt to retrieve
* a value, otherwise return null.
- *
- * @return string|null
*/
- public function parseToken()
+ public function parseToken(): ?string
{
foreach ($this->chain as $parser) {
- if ($response = $parser->parse($this->request)) {
- return $response;
+ if ($token = $parser->parse($this->request)) {
+ return $token;
}
}
+
+ return null;
}
/**
* Check whether a token exists in the chain.
- *
- * @return bool
*/
- public function hasToken()
+ public function hasToken(): bool
{
return $this->parseToken() !== null;
}
/**
* Set the request instance.
- *
- * @param \Illuminate\Http\Request $request
- *
- * @return $this
*/
- public function setRequest(Request $request)
+ public function setRequest(Request $request): self
{
$this->request = $request;
return $this;
}
+
+ /**
+ * Get the request instance.
+ */
+ public function getRequest(): Request
+ {
+ return $this->request;
+ }
}
diff --git a/src/Http/Parser/QueryString.php b/src/Http/Parser/QueryString.php
index 68b1a3594..8b1a44a20 100644
--- a/src/Http/Parser/QueryString.php
+++ b/src/Http/Parser/QueryString.php
@@ -20,12 +20,8 @@ class QueryString implements ParserContract
/**
* Try to parse the token from the request query string.
- *
- * @param \Illuminate\Http\Request $request
- *
- * @return null|string
*/
- public function parse(Request $request)
+ public function parse(Request $request): ?string
{
return $request->query($this->key);
}
diff --git a/src/Http/Parser/RouteParams.php b/src/Http/Parser/RouteParams.php
index b178b238a..ab70f07b1 100644
--- a/src/Http/Parser/RouteParams.php
+++ b/src/Http/Parser/RouteParams.php
@@ -20,12 +20,8 @@ class RouteParams implements ParserContract
/**
* Try to get the token from the route parameters.
- *
- * @param \Illuminate\Http\Request $request
- *
- * @return null|string
*/
- public function parse(Request $request)
+ public function parse(Request $request): ?string
{
$route = $request->route();
@@ -35,5 +31,7 @@ public function parse(Request $request)
if (is_callable([$route, 'parameter'])) {
return $route->parameter($this->key);
}
+
+ return null;
}
}
diff --git a/src/Http/TokenResponse.php b/src/Http/TokenResponse.php
new file mode 100644
index 000000000..2c2967c37
--- /dev/null
+++ b/src/Http/TokenResponse.php
@@ -0,0 +1,81 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Tymon\JWTAuth\Http;
+
+use Illuminate\Contracts\Support\Responsable;
+use Illuminate\Http\JsonResponse;
+use Illuminate\Support\Traits\ForwardsCalls;
+use Tymon\JWTAuth\Token;
+
+class TokenResponse implements Responsable
+{
+ use ForwardsCalls;
+
+ /**
+ * The token itself.
+ */
+ protected Token $token;
+
+ /**
+ * The token ttl.
+ */
+ protected int $ttl;
+
+ /**
+ * The token type.
+ */
+ protected string $type;
+
+ /**
+ * Constructor.
+ */
+ public function __construct(Token $token, int $ttl, string $type = 'bearer')
+ {
+ $this->token = $token;
+ $this->ttl = $ttl;
+ $this->type = $type;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function toResponse($request)
+ {
+ return new JsonResponse([
+ 'access_token' => $this->token->get(),
+ 'token_type' => $this->type,
+ 'expires_in' => $this->ttl * 60,
+ ]);
+ }
+
+ /**
+ * Get the token when casting to string.
+ */
+ public function __toString(): string
+ {
+ return $this->token->get();
+ }
+
+ /**
+ * Magically call the Token.
+ *
+ * @throws \BadMethodCallException
+ *
+ * @return mixed
+ */
+ public function __call(string $method, array $parameters)
+ {
+ return $this->forwardCallTo($this->token, $method, $parameters);
+ }
+}
diff --git a/src/JWT.php b/src/JWT.php
index 55d14e235..e6e4db461 100644
--- a/src/JWT.php
+++ b/src/JWT.php
@@ -1,5 +1,7 @@
builder = $builder;
$this->manager = $manager;
$this->parser = $parser;
}
/**
* Generate a token for a given subject.
- *
- * @param \Tymon\JWTAuth\Contracts\JWTSubject $subject
- *
- * @return string
*/
- public function fromSubject(JWTSubject $subject)
+ public function fromSubject(JWTSubject $subject): Token
{
- $payload = $this->makePayload($subject);
-
- return $this->manager->encode($payload)->get();
+ return $this->manager->tokenForSubject($subject, $this->customClaims);
}
/**
* Alias to generate a token for a given user.
- *
- * @param \Tymon\JWTAuth\Contracts\JWTSubject $user
- *
- * @return string
*/
- public function fromUser(JWTSubject $user)
+ public function fromUser(JWTSubject $user): Token
{
return $this->fromSubject($user);
}
/**
- * Refresh an expired token.
- *
- * @param bool $forceForever
- * @param bool $resetClaims
- *
- * @return string
+ * Invalidate a token (add it to the blacklist).
*/
- public function refresh($forceForever = false, $resetClaims = false)
+ public function invalidate(): self
{
$this->requireToken();
- return $this->manager->customClaims($this->getCustomClaims())
- ->refresh($this->token, $forceForever, $resetClaims)
- ->get();
+ $this->manager->invalidate($this->token);
+
+ return $this;
}
/**
- * Invalidate a token (add it to the blacklist).
- *
- * @param bool $forceForever
- *
- * @return $this
+ * Refresh a token.
*/
- public function invalidate($forceForever = false)
+ public function refresh(): Token
{
$this->requireToken();
- $this->manager->invalidate($this->token, $forceForever);
-
- return $this;
+ return $this->manager->refresh($this->token);
}
/**
@@ -128,22 +99,18 @@ public function invalidate($forceForever = false)
* the token is valid i.e. not expired or blacklisted.
*
* @throws \Tymon\JWTAuth\Exceptions\JWTException
- *
- * @return \Tymon\JWTAuth\Payload
*/
- public function checkOrFail()
+ public function checkOrFail(): Payload
{
- return $this->getPayload();
+ return $this->payload();
}
/**
* Check that the token is valid.
*
- * @param bool $getPayload
- *
* @return \Tymon\JWTAuth\Payload|bool
*/
- public function check($getPayload = false)
+ public function check(bool $getPayload = false)
{
try {
$payload = $this->checkOrFail();
@@ -156,12 +123,10 @@ public function check($getPayload = false)
/**
* Get the token.
- *
- * @return \Tymon\JWTAuth\Token|null
*/
- public function getToken()
+ public function getToken(bool $fresh = false): ?Token
{
- if ($this->token === null) {
+ if ($this->token === null || $fresh === true) {
try {
$this->parseToken();
} catch (JWTException $e) {
@@ -176,10 +141,8 @@ public function getToken()
* Parse the token from the request.
*
* @throws \Tymon\JWTAuth\Exceptions\JWTException
- *
- * @return $this
*/
- public function parseToken()
+ public function parseToken(): self
{
if (! $token = $this->parser->parseToken()) {
throw new JWTException('The token could not be parsed from the request');
@@ -190,128 +153,56 @@ public function parseToken()
/**
* Get the raw Payload instance.
- *
- * @return \Tymon\JWTAuth\Payload
*/
- public function getPayload()
+ public function payload(): Payload
{
$this->requireToken();
return $this->manager->decode($this->token);
}
- /**
- * Alias for getPayload().
- *
- * @return \Tymon\JWTAuth\Payload
- */
- public function payload()
- {
- return $this->getPayload();
- }
-
/**
* Convenience method to get a claim value.
*
- * @param string $claim
- *
* @return mixed
*/
- public function getClaim($claim)
+ public function getClaim(string $claim)
{
return $this->payload()->get($claim);
}
/**
- * Create a Payload instance.
- *
- * @param \Tymon\JWTAuth\Contracts\JWTSubject $subject
- *
- * @return \Tymon\JWTAuth\Payload
- */
- public function makePayload(JWTSubject $subject)
- {
- return $this->factory()->customClaims($this->getClaimsArray($subject))->make();
- }
-
- /**
- * Build the claims array and return it.
- *
- * @param \Tymon\JWTAuth\Contracts\JWTSubject $subject
- *
- * @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.
- *
- * @param \Tymon\JWTAuth\Contracts\JWTSubject $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
+ * Check if the subject model matches the one saved in the payload.
*/
- protected function hashSubjectModel($model)
+ public function checkSubjectModel($model, ?Payload $payload = null): bool
{
- return sha1(is_object($model) ? get_class($model) : $model);
- }
+ $payload ??= $this->payload();
- /**
- * 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) {
+ if (! $hash = $payload->get(HashedSubject::NAME)) {
return true;
}
- return $this->hashSubjectModel($model) === $prv;
+ return $this->builder->hashSubjectModel($model) === $hash;
}
/**
* Set the token.
*
* @param \Tymon\JWTAuth\Token|string $token
- *
- * @return $this
*/
- public function setToken($token)
+ public function setToken($token): self
{
- $this->token = $token instanceof Token ? $token : new Token($token);
+ $this->token = $token instanceof Token
+ ? $token
+ : new Token($token);
return $this;
}
/**
* Unset the current token.
- *
- * @return $this
*/
- public function unsetToken()
+ public function unsetToken(): self
{
$this->token = null;
@@ -322,10 +213,8 @@ public function unsetToken()
* Ensure that a token is available.
*
* @throws \Tymon\JWTAuth\Exceptions\JWTException
- *
- * @return void
*/
- protected function requireToken()
+ protected function requireToken(): void
{
if (! $this->token) {
throw new JWTException('A token is required');
@@ -334,38 +223,27 @@ protected function requireToken()
/**
* Set the request instance.
- *
- * @param \Illuminate\Http\Request $request
- *
- * @return $this
*/
- public function setRequest(Request $request)
+ public function setRequest(Request $request): self
{
+ $this->builder->setRequest($request);
$this->parser->setRequest($request);
return $this;
}
/**
- * Set whether the subject should be "locked".
- *
- * @param bool $lock
- *
- * @return $this
+ * Get the Builder instance.
*/
- public function lockSubject($lock)
+ public function builder(): Builder
{
- $this->lockSubject = $lock;
-
- return $this;
+ return $this->builder;
}
/**
* Get the Manager instance.
- *
- * @return \Tymon\JWTAuth\Manager
*/
- public function manager()
+ public function manager(): Manager
{
return $this->manager;
}
@@ -373,49 +251,81 @@ public function manager()
/**
* Get the Parser instance.
*
- * @return \Tymon\JWTAuth\Http\Parser\Parser
+ * @return \Tymon\JWTAuth\Http\Parser\Parser|\Tymon\JWTAuth\Contracts\Http\Parser
*/
- public function parser()
+ public function parser(?string $key = null)
{
- return $this->parser;
+ return $key === null
+ ? $this->parser
+ : $this->parser->get($key);
}
/**
- * Get the Payload Factory.
- *
- * @return \Tymon\JWTAuth\Factory
+ * Get the Blacklist.
*/
- public function factory()
+ public function blacklist(): Blacklist
{
- return $this->manager->getPayloadFactory();
+ return $this->manager->getBlacklist();
}
/**
- * Get the Blacklist.
- *
- * @return \Tymon\JWTAuth\Blacklist
+ * Set the token ttl (in minutes).
*/
- public function blacklist()
+ public function setTTL(?int $ttl): self
{
- return $this->manager->getBlacklist();
+ $this->builder->setTTL($ttl);
+
+ return $this;
+ }
+
+ /**
+ * Get the token ttl.
+ */
+ public function getTTL(): ?int
+ {
+ return $this->builder->getTTL();
+ }
+
+ /**
+ * Set the secret.
+ */
+ public function setSecret(string $secret): self
+ {
+ $this->manager->getJWTProvider()
+ ->setSecret($secret);
+
+ return $this;
+ }
+
+ /**
+ * Set the required claims.
+ */
+ public function setRequiredClaims(array $claims = []): self
+ {
+ $this->builder->setRequiredClaims($claims);
+
+ return $this;
+ }
+
+ /**
+ * Register a custom claim validator.
+ */
+ public function registerCustomValidator(string $key, callable $validator): self
+ {
+ $this->builder->setCustomValidator($key, $validator);
+
+ return $this;
}
/**
* Magically call the JWT Manager.
*
- * @param string $method
- * @param array $parameters
- *
* @throws \BadMethodCallException
*
* @return mixed
*/
- public function __call($method, $parameters)
+ public function __call(string $method, array $parameters)
{
- if (method_exists($this->manager, $method)) {
- return call_user_func_array([$this->manager, $method], $parameters);
- }
-
- throw new BadMethodCallException("Method [$method] does not exist.");
+ return $this->forwardCallTo($this->manager, $method, $parameters);
}
}
diff --git a/src/JWTAuth.php b/src/JWTAuth.php
deleted file mode 100644
index 27df1d85b..000000000
--- a/src/JWTAuth.php
+++ /dev/null
@@ -1,92 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Tymon\JWTAuth;
-
-use Tymon\JWTAuth\Http\Parser\Parser;
-use Tymon\JWTAuth\Contracts\Providers\Auth;
-
-class JWTAuth extends JWT
-{
- /**
- * The authentication provider.
- *
- * @var \Tymon\JWTAuth\Contracts\Providers\Auth
- */
- protected $auth;
-
- /**
- * Constructor.
- *
- * @param \Tymon\JWTAuth\Manager $manager
- * @param \Tymon\JWTAuth\Contracts\Providers\Auth $auth
- * @param \Tymon\JWTAuth\Http\Parser\Parser $parser
- *
- * @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.
- *
- * @param array $credentials
- *
- * @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 \Tymon\JWTAuth\Contracts\JWTSubject|false
- */
- public function authenticate()
- {
- $id = $this->getPayload()->get('sub');
-
- if (! $this->auth->byId($id)) {
- return false;
- }
-
- return $this->user();
- }
-
- /**
- * Alias for authenticate().
- *
- * @return \Tymon\JWTAuth\Contracts\JWTSubject|false
- */
- public function toUser()
- {
- return $this->authenticate();
- }
-
- /**
- * Get the authenticated user.
- *
- * @return \Tymon\JWTAuth\Contracts\JWTSubject
- */
- public function user()
- {
- return $this->auth->user();
- }
-}
diff --git a/src/JWTGuard.php b/src/JWTGuard.php
index 39bb06c6d..53b0c702a 100644
--- a/src/JWTGuard.php
+++ b/src/JWTGuard.php
@@ -1,5 +1,7 @@
jwt = $jwt;
$this->provider = $provider;
$this->request = $request;
+ $this->events = $events;
}
/**
@@ -72,11 +88,8 @@ public function 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']);
+ if (($payload = $this->getPayload()) && $this->validateSubject($payload)) {
+ return $this->user = $this->provider->retrieveById($payload[Subject::NAME]);
}
}
@@ -98,12 +111,8 @@ public function userOrFail()
/**
* Validate a user's credentials.
- *
- * @param array $credentials
- *
- * @return bool
*/
- public function validate(array $credentials = [])
+ public function validate(array $credentials = []): bool
{
return (bool) $this->attempt($credentials, false);
}
@@ -111,15 +120,14 @@ public function validate(array $credentials = [])
/**
* Attempt to authenticate the user using the given credentials and return the token.
*
- * @param array $credentials
- * @param bool $login
- *
- * @return bool|string
+ * @return bool|Token
*/
- public function attempt(array $credentials = [], $login = true)
+ public function attempt(array $credentials = [], bool $login = true)
{
$this->lastAttempted = $user = $this->provider->retrieveByCredentials($credentials);
+ $this->events->dispatch(new JWTAttempt($user));
+
if ($this->hasValidCredentials($user, $credentials)) {
return $login ? $this->login($user) : true;
}
@@ -130,28 +138,30 @@ public function attempt(array $credentials = [], $login = true)
/**
* Create a token for a user.
*
- * @param \Tymon\JWTAuth\Contracts\JWTSubject $user
- *
- * @return string
+ * @return \Tymon\JWTAuth\Http\TokenResponse|\Tymon\JWTAuth\Token
*/
public function login(JWTSubject $user)
{
$token = $this->jwt->fromUser($user);
$this->setToken($token)->setUser($user);
- return $token;
+ $this->events->dispatch(
+ new JWTLogin($user, $token)
+ );
+
+ return $this->tokenResponse($token);
}
/**
* Logout the user, thus invalidating the token.
- *
- * @param bool $forceForever
- *
- * @return void
*/
- public function logout($forceForever = false)
+ public function logout(): void
{
- $this->requireToken()->invalidate($forceForever);
+ $this->requireToken()->invalidate();
+
+ $this->events->dispatch(
+ new JWTLogout($this->user, $this->jwt)
+ );
$this->user = null;
$this->jwt->unsetToken();
@@ -160,50 +170,49 @@ public function logout($forceForever = false)
/**
* Refresh the token.
*
- * @param bool $forceForever
- * @param bool $resetClaims
- *
- * @return string
+ * @return \Tymon\JWTAuth\Http\TokenResponse|\Tymon\JWTAuth\Token
*/
- public function refresh($forceForever = false, $resetClaims = false)
+ public function refresh()
{
- return $this->requireToken()->refresh($forceForever, $resetClaims);
+ $token = $this->requireToken()->refresh();
+
+ $this->events->dispatch(
+ new JWTRefresh($this->user, $token)
+ );
+
+ return $this->tokenResponse($token);
}
/**
* Invalidate the token.
- *
- * @param bool $forceForever
- *
- * @return \Tymon\JWTAuth\JWT
*/
- public function invalidate($forceForever = false)
+ public function invalidate(): self
{
- return $this->requireToken()->invalidate($forceForever);
+ $this->requireToken()->invalidate();
+
+ $this->events->dispatch(
+ new JWTInvalidate($this->user, $this->jwt)
+ );
+
+ return $this;
}
/**
* Create a new token by User id.
*
* @param mixed $id
- *
- * @return string|null
*/
- public function tokenById($id)
+ public function tokenById($id): ?Token
{
- if ($user = $this->provider->retrieveById($id)) {
- return $this->jwt->fromUser($user);
- }
+ return ($user = $this->provider->retrieveById($id))
+ ? $this->jwt->fromUser($user)
+ : null;
}
/**
* Log a user into the application using their credentials.
- *
- * @param array $credentials
- *
- * @return bool
*/
- public function once(array $credentials = [])
+ public function once(array $credentials = []): bool
{
if ($this->validate($credentials)) {
$this->setUser($this->lastAttempted);
@@ -218,10 +227,8 @@ public function once(array $credentials = [])
* Log the given User into the application.
*
* @param mixed $id
- *
- * @return bool
*/
- public function onceUsingId($id)
+ public function onceUsingId($id): bool
{
if ($user = $this->provider->retrieveById($id)) {
$this->setUser($user);
@@ -236,22 +243,16 @@ public function onceUsingId($id)
* Alias for onceUsingId.
*
* @param mixed $id
- *
- * @return bool
*/
- public function byId($id)
+ public function byId($id): bool
{
return $this->onceUsingId($id);
}
/**
* Add any custom claims.
- *
- * @param array $claims
- *
- * @return $this
*/
- public function claims(array $claims)
+ public function claims(array $claims): self
{
$this->jwt->claims($claims);
@@ -259,33 +260,19 @@ public function claims(array $claims)
}
/**
- * Get the raw Payload instance.
- *
- * @return \Tymon\JWTAuth\Payload
+ * Get the payload.
*/
- public function getPayload()
+ public function payload(): Payload
{
- return $this->requireToken()->getPayload();
- }
-
- /**
- * Alias for getPayload().
- *
- * @return \Tymon\JWTAuth\Payload
- */
- public function payload()
- {
- return $this->getPayload();
+ return $this->requireToken()->payload();
}
/**
* Set the token.
*
* @param \Tymon\JWTAuth\Token|string $token
- *
- * @return $this
*/
- public function setToken($token)
+ public function setToken($token): self
{
$this->jwt->setToken($token);
@@ -294,36 +281,26 @@ public function setToken($token)
/**
* Set the token ttl.
- *
- * @param int $ttl
- *
- * @return $this
*/
- public function setTTL($ttl)
+ public function setTTL(?int $ttl): self
{
- $this->jwt->factory()->setTTL($ttl);
+ $this->jwt->setTTL($ttl);
return $this;
}
/**
* Get the user provider used by the guard.
- *
- * @return \Illuminate\Contracts\Auth\UserProvider
*/
- public function getProvider()
+ public function getProvider(): UserProvider
{
return $this->provider;
}
/**
* Set the user provider used by the guard.
- *
- * @param \Illuminate\Contracts\Auth\UserProvider $provider
- *
- * @return $this
*/
- public function setProvider(UserProvider $provider)
+ public function setProvider(UserProvider $provider): self
{
$this->provider = $provider;
@@ -342,22 +319,16 @@ public function getUser()
/**
* Get the current request instance.
- *
- * @return \Illuminate\Http\Request
*/
- public function getRequest()
+ public function getRequest(): Request
{
- return $this->request ?: Request::createFromGlobals();
+ return $this->request ?? Request::createFromGlobals();
}
/**
* Set the current request instance.
- *
- * @param \Illuminate\Http\Request $request
- *
- * @return $this
*/
- public function setRequest(Request $request)
+ public function setRequest(Request $request): self
{
$this->request = $request;
@@ -374,25 +345,44 @@ public function getLastAttempted()
return $this->lastAttempted;
}
+ /**
+ * Get the responsable Token.
+ *
+ * @return \Tymon\JWTAuth\Http\TokenResponse|\Tymon\JWTAuth\Token
+ */
+ protected function tokenResponse(Token $token)
+ {
+ return $this->useResponsable
+ ? new TokenResponse($token, $this->jwt->getTTL())
+ : $token;
+ }
+
+ /**
+ * Get the payload from a token that may exist in the request.
+ */
+ protected function getPayload(): ?Payload
+ {
+ if ($this->jwt->setRequest($this->request)->getToken() === null) {
+ return null;
+ }
+
+ return $this->jwt->check(true) ?: null;
+ }
+
/**
* Determine if the user matches the credentials.
*
* @param mixed $user
- * @param array $credentials
- *
- * @return bool
*/
- protected function hasValidCredentials($user, $credentials)
+ protected function hasValidCredentials($user, array $credentials): bool
{
return $user !== null && $this->provider->validateCredentials($user, $credentials);
}
/**
* Ensure the JWTSubject matches what is in the token.
- *
- * @return bool
*/
- protected function validateSubject()
+ protected function validateSubject(?Payload $payload = null): bool
{
// If the provider doesn't have the necessary method
// to get the underlying model name then allow.
@@ -400,17 +390,15 @@ protected function validateSubject()
return true;
}
- return $this->jwt->checkSubjectModel($this->provider->getModel());
+ return $this->jwt->checkSubjectModel($this->provider->getModel(), $payload);
}
/**
* Ensure that a token is available in the request.
*
* @throws \Tymon\JWTAuth\Exceptions\JWTException
- *
- * @return \Tymon\JWTAuth\JWT
*/
- protected function requireToken()
+ protected function requireToken(): JWT
{
if (! $this->jwt->setRequest($this->getRequest())->getToken()) {
throw new JWTException('Token could not be parsed from the request.');
@@ -419,22 +407,29 @@ protected function requireToken()
return $this->jwt;
}
+ /**
+ * Determine whether to use Laravel Responsable interface.
+ */
+ public function useResponsable(bool $use = true): self
+ {
+ $this->useResponsable = $use;
+
+ return $this;
+ }
+
/**
* Magically call the JWT instance.
*
- * @param string $method
- * @param array $parameters
- *
* @throws \BadMethodCallException
*
* @return mixed
*/
- public function __call($method, $parameters)
+ public function __call(string $method, array $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.");
+ return $this->forwardCallTo($this->jwt, $method, $parameters);
}
}
diff --git a/src/Manager.php b/src/Manager.php
index b317470c1..2b332eed7 100644
--- a/src/Manager.php
+++ b/src/Manager.php
@@ -1,5 +1,7 @@
provider = $provider;
$this->blacklist = $blacklist;
- $this->payloadFactory = $payloadFactory;
+ $this->builder = $builder;
}
/**
* Encode a Payload and return the Token.
- *
- * @param \Tymon\JWTAuth\Payload $payload
- *
- * @return \Tymon\JWTAuth\Token
*/
- public function encode(Payload $payload)
+ public function encode(Payload $payload): Token
{
- $token = $this->provider->encode($payload->get());
-
- return new Token($token);
+ return $this->provider->token($payload);
}
/**
* Decode a Token and return the Payload.
*
- * @param \Tymon\JWTAuth\Token $token
- * @param bool $checkBlacklist
- *
* @throws \Tymon\JWTAuth\Exceptions\TokenBlacklistedException
- *
- * @return \Tymon\JWTAuth\Payload
*/
- public function decode(Token $token, $checkBlacklist = true)
+ public function decode(Token $token, bool $checkBlacklist = true): Payload
{
- $payloadArray = $this->provider->decode($token->get());
-
- $payload = $this->payloadFactory
- ->setRefreshFlow($this->refreshFlow)
- ->customClaims($payloadArray)
- ->make();
+ $payload = $this->provider->payload($token, $this->builder->getOptions());
if ($checkBlacklist && $this->blacklistEnabled && $this->blacklist->has($payload)) {
- throw new TokenBlacklistedException('The token has been blacklisted');
+ throw new TokenBlacklistedException();
}
return $payload;
@@ -114,125 +79,75 @@ public function decode(Token $token, $checkBlacklist = true)
/**
* Refresh a Token and return a new Token.
- *
- * @param \Tymon\JWTAuth\Token $token
- * @param bool $forceForever
- * @param bool $resetClaims
- *
- * @return \Tymon\JWTAuth\Token
*/
- public function refresh(Token $token, $forceForever = false, $resetClaims = false)
+ public function refresh(Token $token): Token
{
- $this->setRefreshFlow();
-
- $claims = $this->buildRefreshClaims($this->decode($token));
+ // Get the claims for the new token
+ $claims = $this->builder->buildRefreshClaims($this->decode($token));
if ($this->blacklistEnabled) {
// Invalidate old token
- $this->invalidate($token, $forceForever);
+ $this->invalidate($token);
}
// Return the new token
- return $this->encode(
- $this->payloadFactory->customClaims($claims)->make($resetClaims)
- );
+ return $this->encode($this->builder->make($claims));
}
/**
* Invalidate a Token by adding it to the blacklist.
*
- * @param \Tymon\JWTAuth\Token $token
- * @param bool $forceForever
- *
* @throws \Tymon\JWTAuth\Exceptions\JWTException
- *
- * @return bool
*/
- public function invalidate(Token $token, $forceForever = false)
+ public function invalidate(Token $token): void
{
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)
- );
+ $this->blacklist->add($this->decode($token, false));
}
/**
- * Build the claims to go into the refreshed token.
- *
- * @param \Tymon\JWTAuth\Payload $payload
- *
- * @return array
+ * Get a token for the given subject and claims.
*/
- protected function buildRefreshClaims(Payload $payload)
+ public function tokenForSubject(JWTSubject $subject, array $claims = []): Token
{
- // assign the payload values as variables for use later
- extract($payload->toArray());
-
- // persist the relevant claims
- return array_merge(
- $this->customClaims,
- compact($this->persistentClaims, 'sub', 'iat')
- );
- }
+ $payload = $this->builder->makeForSubject($subject, $claims);
- /**
- * Get the Payload Factory instance.
- *
- * @return \Tymon\JWTAuth\Factory
- */
- public function getPayloadFactory()
- {
- return $this->payloadFactory;
+ return $this->encode($payload);
}
/**
* Get the JWTProvider instance.
- *
- * @return \Tymon\JWTAuth\Contracts\Providers\JWT
*/
- public function getJWTProvider()
+ public function getJWTProvider(): JWTContract
{
return $this->provider;
}
/**
* Get the Blacklist instance.
- *
- * @return \Tymon\JWTAuth\Blacklist
*/
- public function getBlacklist()
+ public function getBlacklist(): Blacklist
{
return $this->blacklist;
}
/**
- * Set whether the blacklist is enabled.
- *
- * @param bool $enabled
- *
- * @return $this
+ * Get the Builder instance.
*/
- public function setBlacklistEnabled($enabled)
+ public function builder(): Builder
{
- $this->blacklistEnabled = $enabled;
-
- return $this;
+ return $this->builder;
}
/**
- * Set the claims to be persisted when refreshing a token.
- *
- * @param array $claims
- *
- * @return $this
+ * Set whether the blacklist is enabled.
*/
- public function setPersistentClaims(array $claims)
+ public function setBlacklistEnabled(bool $enabled): self
{
- $this->persistentClaims = $claims;
+ $this->blacklistEnabled = $enabled;
return $this;
}
diff --git a/src/Options.php b/src/Options.php
new file mode 100644
index 000000000..a1f1ec437
--- /dev/null
+++ b/src/Options.php
@@ -0,0 +1,84 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Tymon\JWTAuth;
+
+use Illuminate\Support\Arr;
+
+final class Options
+{
+ /**
+ * @var string
+ */
+ const LEEWAY = 'leeway';
+
+ /**
+ * @var string
+ */
+ const REQUIRED_CLAIMS = 'required_claims';
+
+ /**
+ * @var string
+ */
+ const MAX_REFRESH_PERIOD = 'max_refresh_period';
+
+ /**
+ * @var string
+ */
+ const VALIDATORS = 'validators';
+
+ /**
+ * The provided options.
+ */
+ protected array $options = [];
+
+ /**
+ * Options constructor.
+ */
+ public function __construct(array $options = [])
+ {
+ $this->options = $options;
+ }
+
+ /**
+ * Get the required claims.
+ */
+ public function requiredClaims(): array
+ {
+ return Arr::get($this->options, static::REQUIRED_CLAIMS, []);
+ }
+
+ /**
+ * Get the leeway.
+ */
+ public function leeway(): int
+ {
+ return Arr::get($this->options, static::LEEWAY, 0);
+ }
+
+ /**
+ * Get the maximum refresh period.
+ */
+ public function maxRefreshPeriod(): ?int
+ {
+ return Arr::get($this->options, static::MAX_REFRESH_PERIOD);
+ }
+
+ /**
+ * Get the custom validators.
+ */
+ public function validators(): array
+ {
+ return Arr::get($this->options, static::VALIDATORS, []);
+ }
+}
diff --git a/src/Payload.php b/src/Payload.php
index f1030cea4..f68d2ae9e 100644
--- a/src/Payload.php
+++ b/src/Payload.php
@@ -1,5 +1,7 @@
claims = $validator->setRefreshFlow($refreshFlow)->check($claims);
+ $this->claims = $claims;
}
/**
- * Get the array of claim instances.
- *
- * @return \Tymon\JWTAuth\Claims\Collection
+ * Get the collection of claim instances.
*/
- public function getClaims()
+ public function getClaims(): Collection
{
return $this->claims;
}
/**
* Checks if a payload matches some expected values.
- *
- * @param array $values
- * @param bool $strict
- *
- * @return bool
*/
- public function matches(array $values, $strict = false)
+ public function matches(array $values, bool $strict = false): bool
{
if (empty($values)) {
return false;
@@ -83,12 +74,8 @@ public function matches(array $values, $strict = false)
/**
* Checks if a payload strictly matches some expected values.
- *
- * @param array $values
- *
- * @return bool
*/
- public function matchesStrict(array $values)
+ public function matchesStrict(array $values): bool
{
return $this->matches($values, true);
}
@@ -102,8 +89,6 @@ public function matchesStrict(array $values)
*/
public function get($claim = null)
{
- $claim = value($claim);
-
if ($claim !== null) {
if (is_array($claim)) {
return array_map([$this, 'get'], $claim);
@@ -117,78 +102,64 @@ public function get($claim = null)
/**
* Get the underlying Claim instance.
- *
- * @param string $claim
- *
- * @return \Tymon\JWTAuth\Claims\Claim
*/
- public function getInternal($claim)
+ public function getInternal(string $claim): ?ClaimContract
{
return $this->claims->getByClaimName($claim);
}
/**
* Determine whether the payload has the claim (by instance).
- *
- * @param \Tymon\JWTAuth\Claims\Claim $claim
- *
- * @return bool
*/
- public function has(Claim $claim)
+ public function has(Claim $claim): bool
{
return $this->claims->has($claim->getName());
}
/**
* Determine whether the payload has the claim (by key).
- *
- * @param string $claim
- *
- * @return bool
*/
- public function hasKey($claim)
+ public function hasKey(string $claim): bool
{
return $this->offsetExists($claim);
}
+ /**
+ * Get the token for this payload.
+ */
+ public function token(): Token
+ {
+ return JWTManager::encode($this);
+ }
+
/**
* Get the array of claims.
- *
- * @return array
*/
- public function toArray()
+ public function toArray(): array
{
return $this->claims->toPlainArray();
}
/**
* Convert the object into something JSON serializable.
- *
- * @return array
*/
- public function jsonSerialize()
+ public function jsonSerialize(): array
{
return $this->toArray();
}
/**
* Get the payload as JSON.
- *
- * @param int $options
- *
- * @return string
*/
- public function toJson($options = JSON_UNESCAPED_SLASHES)
+ public function toJson($options = JSON_UNESCAPED_SLASHES): string
{
return json_encode($this->toArray(), $options);
}
/**
* Get the payload as a string.
- *
- * @return string
*/
- public function __toString()
+ public function __toString(): string
{
return $this->toJson();
}
@@ -197,10 +168,8 @@ public function __toString()
* Determine if an item exists at an offset.
*
* @param mixed $key
- *
- * @return bool
*/
- public function offsetExists($key)
+ public function offsetExists($key): bool
{
return Arr::has($this->toArray(), $key);
}
@@ -227,29 +196,25 @@ public function offsetGet($key)
*/
public function offsetSet($key, $value)
{
- throw new PayloadException('The payload is immutable');
+ throw new PayloadException();
}
/**
* Don't allow changing the payload as it should be immutable.
*
- * @param string $key
+ * @param mixed $key
*
* @throws \Tymon\JWTAuth\Exceptions\PayloadException
- *
- * @return void
*/
public function offsetUnset($key)
{
- throw new PayloadException('The payload is immutable');
+ throw new PayloadException();
}
/**
* Count the number of claims.
- *
- * @return int
*/
- public function count()
+ public function count(): int
{
return count($this->toArray());
}
@@ -269,23 +234,25 @@ public function __invoke($claim = null)
/**
* Magically get a claim value.
*
- * @param string $method
- * @param array $parameters
- *
* @throws \BadMethodCallException
*
* @return mixed
*/
- public function __call($method, $parameters)
+ public function __call(string $method, array $parameters)
{
if (preg_match('/get(.+)\b/i', $method, $matches)) {
+ $match = $matches[1];
foreach ($this->claims as $claim) {
- if (get_class($claim) === 'Tymon\\JWTAuth\\Claims\\'.$matches[1]) {
+ if (get_class($claim) === 'Tymon\\JWTAuth\\Claims\\'.$match) {
return $claim->getValue();
}
}
+
+ throw new BadMethodCallException(
+ sprintf('The claim [%s] does not exist on the payload.', $match ?? $method)
+ );
}
- throw new BadMethodCallException(sprintf('The claim [%s] does not exist on the payload.', $method));
+ static::throwBadMethodCallException($method);
}
}
diff --git a/src/Providers/AbstractServiceProvider.php b/src/Providers/AbstractServiceProvider.php
index 97f536dd7..152bcc150 100644
--- a/src/Providers/AbstractServiceProvider.php
+++ b/src/Providers/AbstractServiceProvider.php
@@ -11,78 +11,49 @@
namespace Tymon\JWTAuth\Providers;
-use Namshi\JOSE\JWS;
-use Tymon\JWTAuth\JWT;
-use Tymon\JWTAuth\Factory;
-use Tymon\JWTAuth\JWTAuth;
-use Tymon\JWTAuth\Manager;
-use Tymon\JWTAuth\JWTGuard;
-use Tymon\JWTAuth\Blacklist;
-use Lcobucci\JWT\Parser as JWTParser;
-use Tymon\JWTAuth\Http\Parser\Parser;
-use Tymon\JWTAuth\Http\Parser\Cookies;
use Illuminate\Support\ServiceProvider;
use Lcobucci\JWT\Builder as JWTBuilder;
-use Tymon\JWTAuth\Providers\JWT\Namshi;
-use Tymon\JWTAuth\Http\Middleware\Check;
-use Tymon\JWTAuth\Providers\JWT\Lcobucci;
+use Lcobucci\JWT\Parser as JWTParser;
+use Tymon\JWTAuth\Blacklist;
+use Tymon\JWTAuth\Builder;
+use Tymon\JWTAuth\Console\JWTGenerateSecretCommand;
+use Tymon\JWTAuth\Contracts\Providers\Auth;
+use Tymon\JWTAuth\Contracts\Providers\JWT as JWTContract;
+use Tymon\JWTAuth\Contracts\Providers\Storage;
use Tymon\JWTAuth\Http\Parser\AuthHeaders;
+use Tymon\JWTAuth\Http\Parser\Cookies;
use Tymon\JWTAuth\Http\Parser\InputSource;
+use Tymon\JWTAuth\Http\Parser\Parser;
use Tymon\JWTAuth\Http\Parser\QueryString;
use Tymon\JWTAuth\Http\Parser\RouteParams;
-use Tymon\JWTAuth\Contracts\Providers\Auth;
-use Tymon\JWTAuth\Contracts\Providers\Storage;
-use Tymon\JWTAuth\Validators\PayloadValidator;
-use Tymon\JWTAuth\Http\Middleware\Authenticate;
-use Tymon\JWTAuth\Http\Middleware\RefreshToken;
-use Tymon\JWTAuth\Claims\Factory as ClaimFactory;
-use Tymon\JWTAuth\Console\JWTGenerateSecretCommand;
-use Tymon\JWTAuth\Http\Middleware\AuthenticateAndRenew;
-use Tymon\JWTAuth\Contracts\Providers\JWT as JWTContract;
+use Tymon\JWTAuth\JWT;
+use Tymon\JWTAuth\JWTGuard;
+use Tymon\JWTAuth\Manager;
+use Tymon\JWTAuth\Providers\JWT\Lcobucci;
abstract class AbstractServiceProvider extends ServiceProvider
{
- /**
- * The middleware aliases.
- *
- * @var array
- */
- protected $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->registerBuilder();
$this->registerManager();
$this->registerTokenParser();
$this->registerJWT();
- $this->registerJWTAuth();
- $this->registerPayloadValidator();
- $this->registerClaimFactory();
- $this->registerPayloadFactory();
$this->registerJWTCommand();
$this->commands('tymon.jwt.secret');
@@ -90,8 +61,6 @@ public function register()
/**
* Extend Laravel's Auth.
- *
- * @return void
*/
protected function extendAuthGuard()
{
@@ -99,7 +68,8 @@ protected function extendAuthGuard()
$guard = new JwtGuard(
$app['tymon.jwt'],
$app['auth']->createUserProvider($config['provider']),
- $app['request']
+ $app['request'],
+ $app['events']
);
$app->refresh('request', $guard, 'setRequest');
@@ -110,32 +80,23 @@ protected function extendAuthGuard()
/**
* 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.builder', Builder::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', function ($app) {
@@ -145,25 +106,6 @@ protected function registerJWTProvider()
/**
* Register the bindings for the Lcobucci JWT provider.
- *
- * @return void
- */
- protected function registerNamshiProvider()
- {
- $this->app->singleton('tymon.jwt.provider.jwt.namshi', function ($app) {
- return new Namshi(
- new JWS(['typ' => 'JWT', 'alg' => $this->config('algo')]),
- $this->config('secret'),
- $this->config('algo'),
- $this->config('keys')
- );
- });
- }
-
- /**
- * Register the bindings for the Lcobucci JWT provider.
- *
- * @return void
*/
protected function registerLcobucciProvider()
{
@@ -179,66 +121,62 @@ protected function registerLcobucciProvider()
}
/**
- * Register the bindings for the Auth provider.
- *
- * @return void
+ * Register the bindings for the Storage provider.
*/
- protected function registerAuthProvider()
+ protected function registerStorageProvider()
{
- $this->app->singleton('tymon.jwt.provider.auth', function () {
- return $this->getConfigInstance('providers.auth');
+ $this->app->singleton('tymon.jwt.provider.storage', function () {
+ return $this->getConfigInstance('providers.storage');
});
}
/**
- * Register the bindings for the Storage provider.
- *
- * @return void
+ * Register the bindings for the JWT builder.
*/
- protected function registerStorageProvider()
+ protected function registerBuilder()
{
- $this->app->singleton('tymon.jwt.provider.storage', function () {
- return $this->getConfigInstance('providers.storage');
+ $this->app->singleton('tymon.jwt.builder', function ($app) {
+ $builder = new Builder($app['request']);
+
+ $app->refresh('request', $builder, 'setRequest');
+
+ return $builder->lockSubject($this->config('lock_subject'))
+ ->setTTL($this->config('ttl'))
+ ->setRequiredClaims($this->config('required_claims'))
+ ->setLeeway($this->config('leeway'))
+ ->setMaxRefreshPeriod($this->config('max_refresh_period'));
});
}
/**
* Register the bindings for the JWT Manager.
- *
- * @return void
*/
protected function registerManager()
{
$this->app->singleton('tymon.jwt.manager', function ($app) {
- $instance = new Manager(
+ $manager = new Manager(
$app['tymon.jwt.provider.jwt'],
$app['tymon.jwt.blacklist'],
- $app['tymon.jwt.payload.factory']
+ $app['tymon.jwt.builder']
);
- return $instance->setBlacklistEnabled((bool) $this->config('blacklist_enabled'))
- ->setPersistentClaims($this->config('persistent_claims'));
+ return $manager->setBlacklistEnabled((bool) $this->config('blacklist_enabled'));
});
}
/**
* 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,
- new RouteParams,
- new Cookies($this->config('decrypt_cookies')),
- ]
- );
+ $parser = new Parser($app['request'], [
+ 'header' => new AuthHeaders,
+ 'query' => new QueryString,
+ 'input' => new InputSource,
+ 'route' => new RouteParams,
+ 'cookie' => new Cookies($this->config('decrypt_cookies')),
+ ]);
$app->refresh('request', $parser, 'setRequest');
@@ -248,30 +186,13 @@ protected function registerTokenParser()
/**
* Register the bindings for the main JWT class.
- *
- * @return void
*/
protected function registerJWT()
{
$this->app->singleton('tymon.jwt', function ($app) {
- return (new JWT(
- $app['tymon.jwt.manager'],
- $app['tymon.jwt.parser']
- ))->lockSubject($this->config('lock_subject'));
- });
- }
-
- /**
- * Register the bindings for the main JWTAuth class.
- *
- * @return void
- */
- protected function registerJWTAuth()
- {
- $this->app->singleton('tymon.jwt.auth', function ($app) {
- return new JWTAuth(
+ return new JWT(
+ $app['tymon.jwt.builder'],
$app['tymon.jwt.manager'],
- $app['tymon.jwt.provider.auth'],
$app['tymon.jwt.parser']
);
});
@@ -279,68 +200,18 @@ protected function registerJWTAuth()
/**
* 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']);
+ $blacklist = new Blacklist($app['tymon.jwt.provider.storage']);
- return $instance->setGracePeriod($this->config('blacklist_grace_period'))
- ->setRefreshTTL($this->config('refresh_ttl'));
- });
- }
-
- /**
- * Register the bindings for the payload validator.
- *
- * @return void
- */
- protected function registerPayloadValidator()
- {
- $this->app->singleton('tymon.jwt.validators.payload', function () {
- return (new PayloadValidator)
- ->setRefreshTTL($this->config('refresh_ttl'))
- ->setRequiredClaims($this->config('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($this->config('ttl'))
- ->setLeeway($this->config('leeway'));
- });
- }
-
- /**
- * Register the bindings for the Payload Factory.
- *
- * @return void
- */
- protected function registerPayloadFactory()
- {
- $this->app->singleton('tymon.jwt.payload.factory', function ($app) {
- return new Factory(
- $app['tymon.jwt.claim.factory'],
- $app['tymon.jwt.validators.payload']
- );
+ return $blacklist->setGracePeriod($this->config('blacklist_grace_period'));
});
}
/**
* Register the Artisan command.
- *
- * @return void
*/
protected function registerJWTCommand()
{
@@ -352,12 +223,11 @@ protected function registerJWTCommand()
/**
* Helper to get the config values.
*
- * @param string $key
- * @param string $default
+ * @param mixed $default
*
* @return mixed
*/
- protected function config($key, $default = null)
+ protected function config(string $key, $default = null)
{
return config("jwt.$key", $default);
}
@@ -365,11 +235,9 @@ protected function config($key, $default = null)
/**
* Get an instantiable configuration instance.
*
- * @param string $key
- *
* @return mixed
*/
- protected function getConfigInstance($key)
+ protected function getConfigInstance(string $key)
{
$instance = $this->config($key);
diff --git a/src/Providers/Auth/Illuminate.php b/src/Providers/Auth/Illuminate.php
deleted file mode 100644
index feec83adf..000000000
--- a/src/Providers/Auth/Illuminate.php
+++ /dev/null
@@ -1,71 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Tymon\JWTAuth\Providers\Auth;
-
-use Tymon\JWTAuth\Contracts\Providers\Auth;
-use Illuminate\Contracts\Auth\Guard as GuardContract;
-
-class Illuminate implements Auth
-{
- /**
- * The authentication guard.
- *
- * @var \Illuminate\Contracts\Auth\Guard
- */
- protected $auth;
-
- /**
- * Constructor.
- *
- * @param \Illuminate\Contracts\Auth\Guard $auth
- *
- * @return void
- */
- public function __construct(GuardContract $auth)
- {
- $this->auth = $auth;
- }
-
- /**
- * Check a user's credentials.
- *
- * @param array $credentials
- *
- * @return bool
- */
- public function byCredentials(array $credentials)
- {
- return $this->auth->once($credentials);
- }
-
- /**
- * Authenticate a user via the id.
- *
- * @param mixed $id
- *
- * @return bool
- */
- public function byId($id)
- {
- return $this->auth->onceUsingId($id);
- }
-
- /**
- * Get the currently authenticated user.
- *
- * @return mixed
- */
- public function user()
- {
- return $this->auth->user();
- }
-}
diff --git a/src/Providers/Auth/Sentinel.php b/src/Providers/Auth/Sentinel.php
deleted file mode 100644
index ca679f7f7..000000000
--- a/src/Providers/Auth/Sentinel.php
+++ /dev/null
@@ -1,77 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Tymon\JWTAuth\Providers\Auth;
-
-use Tymon\JWTAuth\Contracts\Providers\Auth;
-use Cartalyst\Sentinel\Sentinel as SentinelAuth;
-
-class Sentinel implements Auth
-{
- /**
- * The sentinel authentication.
- *
- * @var \Cartalyst\Sentinel\Sentinel
- */
- protected $sentinel;
-
- /**
- * Constructor.
- *
- * @param \Cartalyst\Sentinel\Sentinel $sentinel
- *
- * @return void
- */
- public function __construct(SentinelAuth $sentinel)
- {
- $this->sentinel = $sentinel;
- }
-
- /**
- * Check a user's credentials.
- *
- * @param array $credentials
- *
- * @return mixed
- */
- public function byCredentials(array $credentials)
- {
- return $this->sentinel->stateless($credentials);
- }
-
- /**
- * Authenticate a user via the id.
- *
- * @param mixed $id
- *
- * @return bool
- */
- public function byId($id)
- {
- if ($user = $this->sentinel->getUserRepository()->findById($id)) {
- $this->sentinel->setUser($user);
-
- return true;
- }
-
- return false;
- }
-
- /**
- * Get the currently authenticated user.
- *
- * @return \Cartalyst\Sentinel\Users\UserInterface
- */
- public function user()
- {
- return $this->sentinel->getUser();
- }
-}
diff --git a/src/Providers/JWT/Lcobucci.php b/src/Providers/JWT/Lcobucci.php
index 1dd172ca4..8774a1647 100644
--- a/src/Providers/JWT/Lcobucci.php
+++ b/src/Providers/JWT/Lcobucci.php
@@ -1,5 +1,7 @@
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,
+ protected $algorithms = [
+ 'HS256' => Signer\Hmac\Sha256::class,
+ 'HS384' => Signer\Hmac\Sha384::class,
+ 'HS512' => Signer\Hmac\Sha512::class,
+ 'RS256' => Signer\Rsa\Sha256::class,
+ 'RS384' => Signer\Rsa\Sha384::class,
+ 'RS512' => Signer\Rsa\Sha512::class,
+ 'ES256' => Signer\Ecdsa\Sha256::class,
+ 'ES384' => Signer\Ecdsa\Sha384::class,
+ 'ES512' => Signer\Ecdsa\Sha512::class,
];
/**
* Create a JSON Web Token.
*
- * @param array $payload
- *
* @throws \Tymon\JWTAuth\Exceptions\JWTException
- *
- * @return string
*/
- public function encode(array $payload)
+ public function encode(array $payload): string
{
// Remove the signature on the builder instance first.
$this->builder->unsign();
@@ -119,53 +98,52 @@ public function encode(array $payload)
/**
* Decode a JSON Web Token.
*
- * @param string $token
- *
* @throws \Tymon\JWTAuth\Exceptions\JWTException
- *
- * @return array
*/
- public function decode($token)
+ public function decode(string $token): array
{
try {
$jwt = $this->parser->parse($token);
} catch (Exception $e) {
- throw new TokenInvalidException('Could not decode token: '.$e->getMessage(), $e->getCode(), $e);
+ throw new TokenInvalidException(
+ 'Could not decode token: '.$e->getMessage(),
+ $e->getCode(),
+ $e
+ );
}
if (! $jwt->verify($this->signer, $this->getVerificationKey())) {
throw new TokenInvalidException('Token Signature could not be verified.');
}
- return (new Collection($jwt->getClaims()))->map(function ($claim) {
- return is_object($claim) ? $claim->getValue() : $claim;
- })->toArray();
+ return Collection::make($jwt->getClaims())
+ ->map(fn ($claim) => is_object($claim) ? $claim->getValue() : $claim)
+ ->toArray();
}
/**
- * Get the signer instance.
+ * Get the Signer instance.
*
* @throws \Tymon\JWTAuth\Exceptions\JWTException
- *
- * @return \Lcobucci\JWT\Signer
*/
- protected function getSigner()
+ protected function getSigner(): Signer
{
- if (! array_key_exists($this->algo, $this->signers)) {
+ if (! array_key_exists($this->algo, $this->algorithms)) {
throw new JWTException('The given algorithm could not be found');
}
- return new $this->signers[$this->algo];
+ return new $this->algorithms[$this->algo];
}
/**
* {@inheritdoc}
*/
- protected function isAsymmetric()
+ protected function isAsymmetric(): bool
{
$reflect = new ReflectionClass($this->signer);
- return $reflect->isSubclassOf(Rsa::class) || $reflect->isSubclassOf(Ecdsa::class);
+ return $reflect->isSubclassOf(Signer\Rsa::class)
+ || $reflect->isSubclassOf(Signer\Ecdsa::class);
}
/**
@@ -173,9 +151,9 @@ protected function isAsymmetric()
*/
protected function getSigningKey()
{
- return $this->isAsymmetric() ?
- (new Keychain())->getPrivateKey($this->getPrivateKey(), $this->getPassphrase()) :
- $this->getSecret();
+ return $this->isAsymmetric()
+ ? (new Signer\Keychain())->getPrivateKey($this->getPrivateKey(), $this->getPassphrase())
+ : $this->getSecret();
}
/**
@@ -183,8 +161,8 @@ protected function getSigningKey()
*/
protected function getVerificationKey()
{
- return $this->isAsymmetric() ?
- (new Keychain())->getPublicKey($this->getPublicKey()) :
- $this->getSecret();
+ return $this->isAsymmetric()
+ ? (new Signer\Keychain())->getPublicKey($this->getPublicKey())
+ : $this->getSecret();
}
}
diff --git a/src/Providers/JWT/Namshi.php b/src/Providers/JWT/Namshi.php
deleted file mode 100644
index 361a03754..000000000
--- a/src/Providers/JWT/Namshi.php
+++ /dev/null
@@ -1,106 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Tymon\JWTAuth\Providers\JWT;
-
-use Exception;
-use Namshi\JOSE\JWS;
-use ReflectionClass;
-use ReflectionException;
-use InvalidArgumentException;
-use Namshi\JOSE\Signer\OpenSSL\PublicKey;
-use Tymon\JWTAuth\Contracts\Providers\JWT;
-use Tymon\JWTAuth\Exceptions\JWTException;
-use Tymon\JWTAuth\Exceptions\TokenInvalidException;
-
-class Namshi extends Provider implements JWT
-{
- /**
- * The JWS.
- *
- * @var \Namshi\JOSE\JWS
- */
- protected $jws;
-
- /**
- * Constructor.
- *
- * @param \Namshi\JOSE\JWS $jws
- * @param string $secret
- * @param string $algo
- * @param array $keys
- *
- * @return void
- */
- public function __construct(JWS $jws, $secret, $algo, array $keys)
- {
- parent::__construct($secret, $algo, $keys);
-
- $this->jws = $jws;
- }
-
- /**
- * Create a JSON Web Token.
- *
- * @param array $payload
- *
- * @throws \Tymon\JWTAuth\Exceptions\JWTException
- *
- * @return string
- */
- 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
- *
- * @throws \Tymon\JWTAuth\Exceptions\JWTException
- *
- * @return array
- */
- 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();
- }
-
- /**
- * {@inheritdoc}
- */
- 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);
- }
- }
-}
diff --git a/src/Providers/JWT/Provider.php b/src/Providers/JWT/Provider.php
index 2b08f71f0..2181d5741 100644
--- a/src/Providers/JWT/Provider.php
+++ b/src/Providers/JWT/Provider.php
@@ -1,5 +1,7 @@
secret = $secret;
$this->algo = $algo;
$this->keys = $keys;
}
+ /**
+ * Get the decoded token as a Payload instance.
+ */
+ public function payload(Token $token, ?Options $options = null): Payload
+ {
+ return Factory::make($this->decode($token->get()), $options);
+ }
+
+ /**
+ * Get an encoded Token instance.
+ */
+ public function token(Payload $payload): Token
+ {
+ return new Token($this->encode($payload->get()));
+ }
+
/**
* Set the algorithm used to sign the token.
- *
- * @param string $algo
- *
- * @return $this
*/
- public function setAlgo($algo)
+ public function setAlgo(string $algo): self
{
$this->algo = $algo;
@@ -68,22 +75,16 @@ public function setAlgo($algo)
/**
* Get the algorithm used to sign the token.
- *
- * @return string
*/
- public function getAlgo()
+ public function getAlgo(): string
{
return $this->algo;
}
/**
* Set the secret used to sign the token.
- *
- * @param string $secret
- *
- * @return $this
*/
- public function setSecret($secret)
+ public function setSecret(string $secret): self
{
$this->secret = $secret;
@@ -92,22 +93,16 @@ public function setSecret($secret)
/**
* Get the secret used to sign the token.
- *
- * @return string
*/
- public function getSecret()
+ public function getSecret(): string
{
return $this->secret;
}
/**
* Set the keys used to sign the token.
- *
- * @param array $keys
- *
- * @return $this
*/
- public function setKeys(array $keys)
+ public function setKeys(array $keys): self
{
$this->keys = $keys;
@@ -117,10 +112,8 @@ public function setKeys(array $keys)
/**
* Get the array of keys used to sign tokens
* with an asymmetric algorithm.
- *
- * @return array
*/
- public function getKeys()
+ public function getKeys(): array
{
return $this->keys;
}
@@ -150,10 +143,8 @@ public function getPrivateKey()
/**
* Get the passphrase used to sign tokens
* with an asymmetric algorithm.
- *
- * @return string
*/
- public function getPassphrase()
+ public function getPassphrase(): ?string
{
return Arr::get($this->keys, 'passphrase');
}
@@ -165,7 +156,9 @@ public function getPassphrase()
*/
protected function getSigningKey()
{
- return $this->isAsymmetric() ? $this->getPrivateKey() : $this->getSecret();
+ return $this->isAsymmetric()
+ ? $this->getPrivateKey()
+ : $this->getSecret();
}
/**
@@ -175,16 +168,14 @@ protected function getSigningKey()
*/
protected function getVerificationKey()
{
- return $this->isAsymmetric() ? $this->getPublicKey() : $this->getSecret();
+ return $this->isAsymmetric()
+ ? $this->getPublicKey()
+ : $this->getSecret();
}
/**
* Determine if the algorithm is asymmetric, and thus
* requires a public/private key combo.
- *
- * @throws \Tymon\JWTAuth\Exceptions\JWTException
- *
- * @return bool
*/
- abstract protected function isAsymmetric();
+ abstract protected function isAsymmetric(): bool;
}
diff --git a/src/Providers/JWT/WebToken.php b/src/Providers/JWT/WebToken.php
new file mode 100644
index 000000000..4fc209358
--- /dev/null
+++ b/src/Providers/JWT/WebToken.php
@@ -0,0 +1,188 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Tymon\JWTAuth\Providers\JWT;
+
+use Exception;
+use Jose\Component\Checker\AlgorithmChecker;
+use Jose\Component\Checker\HeaderCheckerManager;
+use Jose\Component\Core\Algorithm;
+use Jose\Component\Core\AlgorithmManager;
+use Jose\Component\Core\JWK;
+use Jose\Component\KeyManagement\JWKFactory;
+use Jose\Component\Signature\Algorithm as Algorithms;
+use Jose\Component\Signature\JWSBuilder;
+use Jose\Component\Signature\JWSLoader;
+use Jose\Component\Signature\JWSTokenSupport;
+use Jose\Component\Signature\JWSVerifier;
+use Jose\Component\Signature\Serializer\CompactSerializer;
+use Jose\Component\Signature\Serializer\JWSSerializer;
+use Jose\Component\Signature\Serializer\JWSSerializerManager;
+use RuntimeException;
+use Tymon\JWTAuth\Exceptions\JWTException;
+use Tymon\JWTAuth\Exceptions\TokenInvalidException;
+
+class WebToken extends Provider
+{
+ /**
+ * Algorithms that this provider supports.
+ */
+ protected array $algorithms = [
+ 'HS256' => Algorithms\HS256::class,
+ 'HS384' => Algorithms\HS384::class,
+ 'HS512' => Algorithms\HS512::class,
+ 'RS256' => Algorithms\RS256::class,
+ 'RS384' => Algorithms\RS384::class,
+ 'RS512' => Algorithms\RS512::class,
+ 'ES256' => Algorithms\ES256::class,
+ 'ES384' => Algorithms\ES384::class,
+ 'ES512' => Algorithms\ES512::class,
+ ];
+
+ /**
+ * Create a JSON Web Token.
+ *
+ * @throws \Tymon\JWTAuth\Exceptions\JWTException
+ */
+ public function encode(array $payload): string
+ {
+ try {
+ $jws = $this->getJWSBuilder()
+ ->create()
+ ->withPayload(json_encode($payload))
+ ->addSignature($this->getJWK(), ['alg' => $this->getAlgo()])
+ ->build();
+ } catch (RuntimeException $e) {
+ throw new JWTException('Could not create token: '.$e->getMessage(), $e->getCode(), $e);
+ }
+
+ return $this->getSerializer()->serialize($jws);
+ }
+
+ /**
+ * Decode a JSON Web Token.
+ *
+ * @throws \Tymon\JWTAuth\Exceptions\JWTException
+ */
+ public function decode(string $token): array
+ {
+ $signature = 0;
+
+ try {
+ $jws = $this->getJWSLoader()->loadAndVerifyWithKey($token, $this->getJWK(), $signature);
+ } catch (Exception $e) {
+ throw new TokenInvalidException('Token Signature could not be verified.', $e->getCode(), $e);
+ }
+
+ return json_decode($jws->getPayload(), true);
+ }
+
+ /**
+ * Get the Algorithm instance.
+ *
+ * @throws \Tymon\JWTAuth\Exceptions\JWTException
+ */
+ protected function getAlgorithm(): Algorithm
+ {
+ if (! array_key_exists($this->algo, $this->algorithms)) {
+ throw new JWTException('The given algorithm could not be found');
+ }
+
+ return new $this->algorithms[$this->algo];
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function isAsymmetric(): bool
+ {
+ return ! in_array('oct', $this->getAlgorithm()->allowedKeyTypes());
+ }
+
+ /**
+ * Get the JWK used to create and verify the token.
+ */
+ protected function getJWK(): JWK
+ {
+ if ($this->isAsymmetric()) {
+ return JWKFactory::createFromKeyFile($this->getPrivateKey(), $this->getPassphrase(), [
+ 'use' => 'sig',
+ ]);
+ }
+
+ return JWKFactory::createFromSecret($this->getSecret(), [
+ 'alg' => $this->getAlgo(),
+ 'use' => 'sig',
+ ]);
+ }
+
+ /**
+ * Get the JWS builder.
+ */
+ protected function getJWSBuilder(): JWSBuilder
+ {
+ return new JWSBuilder($this->getAlgorithmManager());
+ }
+
+ /**
+ * Get the JWS loader.
+ */
+ protected function getJWSLoader(): JWSLoader
+ {
+ return new JWSLoader(
+ $this->getSerializerManager(),
+ new JWSVerifier($this->getAlgorithmManager()),
+ $this->getHeaderCheckerManager()
+ );
+ }
+
+ /**
+ * Get the JWS serializer.
+ */
+ protected function getSerializer(): JWSSerializer
+ {
+ return new CompactSerializer();
+ }
+
+ /**
+ * Get the algorithm manager.
+ */
+ protected function getAlgorithmManager(): AlgorithmManager
+ {
+ return new AlgorithmManager([
+ $this->getAlgorithm(),
+ ]);
+ }
+
+ /**
+ * Get the serializer manager.
+ */
+ protected function getSerializerManager(): JWSSerializerManager
+ {
+ return new JWSSerializerManager([
+ $this->getSerializer(),
+ ]);
+ }
+
+ /**
+ * Get the header checker manager.
+ */
+ protected function getHeaderCheckerManager(): HeaderCheckerManager
+ {
+ return new HeaderCheckerManager([
+ new AlgorithmChecker([$this->getAlgo()]),
+ ], [
+ new JWSTokenSupport(),
+ ]);
+ }
+}
diff --git a/src/Providers/LaravelServiceProvider.php b/src/Providers/LaravelServiceProvider.php
index 5c772d378..51674fa23 100644
--- a/src/Providers/LaravelServiceProvider.php
+++ b/src/Providers/LaravelServiceProvider.php
@@ -23,24 +23,22 @@ public function boot()
$this->publishes([$path => config_path('jwt.php')], 'config');
$this->mergeConfigFrom($path, 'jwt');
- $this->aliasMiddleware();
-
$this->extendAuthGuard();
}
/**
- * Alias the middleware.
- *
- * @return void
+ * {@inheritdoc}
*/
- protected function aliasMiddleware()
+ protected function registerStorageProvider()
{
- $router = $this->app['router'];
+ $this->app->singleton('tymon.jwt.provider.storage', function () {
+ $instance = $this->getConfigInstance('providers.storage');
- $method = method_exists($router, 'aliasMiddleware') ? 'aliasMiddleware' : 'middleware';
+ if (method_exists($instance, 'setLaravelVersion')) {
+ $instance->setLaravelVersion($this->app->version());
+ }
- foreach ($this->middlewareAliases as $alias => $middleware) {
- $router->$method($alias, $middleware);
- }
+ return $instance;
+ });
}
}
diff --git a/src/Providers/LumenServiceProvider.php b/src/Providers/LumenServiceProvider.php
index 7397e7619..212d1e407 100644
--- a/src/Providers/LumenServiceProvider.php
+++ b/src/Providers/LumenServiceProvider.php
@@ -12,9 +12,10 @@
namespace Tymon\JWTAuth\Providers;
use Tymon\JWTAuth\Http\Parser\AuthHeaders;
+use Tymon\JWTAuth\Http\Parser\Cookies;
use Tymon\JWTAuth\Http\Parser\InputSource;
-use Tymon\JWTAuth\Http\Parser\QueryString;
use Tymon\JWTAuth\Http\Parser\LumenRouteParams;
+use Tymon\JWTAuth\Http\Parser\QueryString;
class LumenServiceProvider extends AbstractServiceProvider
{
@@ -28,15 +29,14 @@ public function boot()
$path = realpath(__DIR__.'/../../config/config.php');
$this->mergeConfigFrom($path, 'jwt');
- $this->app->routeMiddleware($this->middlewareAliases);
-
$this->extendAuthGuard();
$this->app['tymon.jwt.parser']->setChain([
- new AuthHeaders,
- new QueryString,
- new InputSource,
- new LumenRouteParams,
+ 'header' => new AuthHeaders,
+ 'query' => new QueryString,
+ 'input' => new InputSource,
+ 'route' => new LumenRouteParams,
+ 'cookie' => new Cookies($this->config('decrypt_cookies')),
]);
}
}
diff --git a/src/Providers/Storage/Illuminate.php b/src/Providers/Storage/Illuminate.php
index f0a595942..b001f40cc 100644
--- a/src/Providers/Storage/Illuminate.php
+++ b/src/Providers/Storage/Illuminate.php
@@ -12,37 +12,33 @@
namespace Tymon\JWTAuth\Providers\Storage;
use BadMethodCallException;
-use Tymon\JWTAuth\Contracts\Providers\Storage;
-use Psr\SimpleCache\CacheInterface as PsrCacheInterface;
use Illuminate\Contracts\Cache\Repository as CacheContract;
+use Tymon\JWTAuth\Contracts\Providers\Storage;
class Illuminate implements Storage
{
/**
* The cache repository contract.
- *
- * @var \Illuminate\Contracts\Cache\Repository
*/
- protected $cache;
+ protected CacheContract $cache;
/**
* The used cache tag.
- *
- * @var string
*/
- protected $tag = 'tymon.jwt';
+ protected string $tag = 'tymon.jwt';
/**
- * @var bool
+ * Whether the storage driver supports tagging.
*/
- protected $supportsTags;
+ protected ?bool $supportsTags = null;
+
+ /**
+ * The laravel version.
+ */
+ protected ?string $laravelVersion = null;
/**
* Constructor.
- *
- * @param \Illuminate\Contracts\Cache\Repository $cache
- *
- * @return void
*/
public function __construct(CacheContract $cache)
{
@@ -51,71 +47,56 @@ public function __construct(CacheContract $cache)
/**
* Add a new item into storage.
- *
- * @param string $key
- * @param mixed $value
- * @param int $minutes
- *
- * @return void
*/
- public function add($key, $value, $minutes)
+ public function add(string $key, $value, $minutes): void
{
+ // If the laravel version is 5.8 or higher then convert minutes to seconds.
+ if ($this->laravelVersion !== null
+ && 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
- * @param mixed $value
- *
- * @return void
*/
- public function forever($key, $value)
+ public function forever(string $key, $value): void
{
$this->cache()->forever($key, $value);
}
/**
* Get an item from storage.
- *
- * @param string $key
- *
- * @return mixed
*/
- public function get($key)
+ public function get(string $key)
{
return $this->cache()->get($key);
}
/**
* Remove an item from storage.
- *
- * @param string $key
- *
- * @return bool
*/
- public function destroy($key)
+ public function destroy(string $key): void
{
- return $this->cache()->forget($key);
+ $this->cache()->forget($key);
}
/**
* Remove all items associated with the tag.
- *
- * @return void
*/
- public function flush()
+ public function flush(): void
{
$this->cache()->flush();
}
/**
* Return the cache instance with tags attached.
- *
- * @return \Illuminate\Contracts\Cache\Repository
*/
- protected function cache()
+ protected function cache(): CacheContract
{
if ($this->supportsTags === null) {
$this->determineTagSupport();
@@ -128,33 +109,28 @@ protected function cache()
return $this->cache;
}
+ /**
+ * Set the laravel version.
+ */
+ public function setLaravelVersion(string $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()
+ protected function determineTagSupport(): void
{
- // 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;
- }
+ 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;
}
}
}
diff --git a/src/Support/CustomClaims.php b/src/Support/CustomClaims.php
index d5443eb99..aa95ebaff 100644
--- a/src/Support/CustomClaims.php
+++ b/src/Support/CustomClaims.php
@@ -1,5 +1,7 @@
customClaims = $customClaims;
@@ -36,22 +32,16 @@ public function customClaims(array $customClaims)
/**
* Alias to set the custom claims.
- *
- * @param array $customClaims
- *
- * @return $this
*/
- public function claims(array $customClaims)
+ public function claims(array $customClaims): self
{
return $this->customClaims($customClaims);
}
/**
* Get the custom claims.
- *
- * @return array
*/
- public function getCustomClaims()
+ public function getCustomClaims(): array
{
return $this->customClaims;
}
diff --git a/src/Support/RefreshFlow.php b/src/Support/RefreshFlow.php
deleted file mode 100644
index 988b6eae1..000000000
--- a/src/Support/RefreshFlow.php
+++ /dev/null
@@ -1,36 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Tymon\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;
- }
-}
diff --git a/src/Support/Utils.php b/src/Support/Utils.php
deleted file mode 100644
index 5738e6ed7..000000000
--- a/src/Support/Utils.php
+++ /dev/null
@@ -1,73 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Tymon\JWTAuth\Support;
-
-use Carbon\Carbon;
-
-class Utils
-{
- /**
- * Get the Carbon instance for the current time.
- *
- * @return \Carbon\Carbon
- */
- public static function now()
- {
- return Carbon::now('UTC');
- }
-
- /**
- * Get the Carbon instance for the timestamp.
- *
- * @param int $timestamp
- *
- * @return \Carbon\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();
- }
-}
diff --git a/src/Support/helpers.php b/src/Support/helpers.php
new file mode 100644
index 000000000..4e24089cc
--- /dev/null
+++ b/src/Support/helpers.php
@@ -0,0 +1,53 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Tymon\JWTAuth\Support;
+
+use Carbon\Carbon;
+
+/**
+ * Get the Carbon instance for the current time.
+ */
+function now()
+{
+ return Carbon::now('UTC');
+}
+
+/**
+ * Get the Carbon instance for the timestamp.
+ */
+function timestamp(int $timestamp): Carbon
+{
+ return Carbon::createFromTimestampUTC($timestamp)
+ ->timezone('UTC');
+}
+
+/**
+ * Checks if a timestamp is in the past.
+ */
+function is_past(int $timestamp, int $leeway = 0): bool
+{
+ return timestamp($timestamp)
+ ->addSeconds($leeway)
+ ->isPast();
+}
+
+/**
+ * Checks if a timestamp is in the future.
+ */
+function is_future(int $timestamp, int $leeway = 0): bool
+{
+ return timestamp($timestamp)
+ ->subSeconds($leeway)
+ ->isFuture();
+}
diff --git a/src/Token.php b/src/Token.php
index a51a73626..c9d002523 100644
--- a/src/Token.php
+++ b/src/Token.php
@@ -1,5 +1,7 @@
value = (string) (new TokenValidator)->check($value);
+ $this->value = TokenValidator::check($value);
}
/**
* Get the token.
- *
- * @return string
*/
- public function get()
+ public function get(): string
{
return $this->value;
}
+ /**
+ * Get the payload for this token.
+ */
+ public function payload(bool $checkBlacklist = true): Payload
+ {
+ return JWTManager::decode($this, $checkBlacklist);
+ }
+
+ /**
+ * Checks if a token matches this one.
+ */
+ public function matches($token): bool
+ {
+ return (string) $this->get() === (string) $token;
+ }
+
/**
* Get the token when casting to string.
- *
- * @return string
*/
- public function __toString()
+ public function __toString(): string
{
return $this->get();
}
diff --git a/src/Validators/PayloadValidator.php b/src/Validators/PayloadValidator.php
index f7f73b7b9..5eaaa33d9 100644
--- a/src/Validators/PayloadValidator.php
+++ b/src/Validators/PayloadValidator.php
@@ -11,117 +11,53 @@
namespace Tymon\JWTAuth\Validators;
+use Illuminate\Support\Arr;
use Tymon\JWTAuth\Claims\Collection;
-use Tymon\JWTAuth\Exceptions\TokenInvalidException;
+use Tymon\JWTAuth\Claims\Expiration;
+use Tymon\JWTAuth\Options;
+use Tymon\JWTAuth\Payload;
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 \Tymon\JWTAuth\Claims\Collection $value
- *
- * @return \Tymon\JWTAuth\Claims\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.
- *
- * @param \Tymon\JWTAuth\Claims\Collection $claims
- *
- * @throws \Tymon\JWTAuth\Exceptions\TokenInvalidException
- *
- * @return void
- */
- protected function validateStructure(Collection $claims)
- {
- if (! $claims->hasAllClaims($this->requiredClaims)) {
- throw new TokenInvalidException('JWT payload does not contain the required claims');
- }
- }
-
- /**
- * Validate the payload timestamps.
- *
- * @param \Tymon\JWTAuth\Claims\Collection $claims
- *
* @throws \Tymon\JWTAuth\Exceptions\TokenExpiredException
* @throws \Tymon\JWTAuth\Exceptions\TokenInvalidException
- *
- * @return \Tymon\JWTAuth\Claims\Collection
*/
- protected function validatePayload(Collection $claims)
+ public static function check(Collection $claims, ?Options $options = null): Payload
{
- return $claims->validate('payload');
- }
+ $options ??= new Options();
- /**
- * Check the token in the refresh flow context.
- *
- * @param \Tymon\JWTAuth\Claims\Collection $claims
- *
- * @throws \Tymon\JWTAuth\Exceptions\TokenExpiredException
- *
- * @return \Tymon\JWTAuth\Claims\Collection
- */
- protected function validateRefresh(Collection $claims)
- {
- return $this->refreshTTL === null ? $claims : $claims->validate('refresh', $this->refreshTTL);
- }
+ if (! static::hasRequiredClaims($claims, $options)) {
+ static::throwFailed('JWT does not contain the required claims');
+ }
- /**
- * Set the required claims.
- *
- * @param array $claims
- *
- * @return $this
- */
- public function setRequiredClaims(array $claims)
- {
- $this->requiredClaims = $claims;
+ // Run the built in verifications
+ $claims->verify();
+
+ // Run any custom validators
+ foreach ($options->validators() as $name => $validator) {
+ if ($claim = $claims->getByClaimName($name)) {
+ if ($validator($claim->getValue(), $name) === false) {
+ static::throwFailed('Validation failed for claim ['.$name.']');
+ }
+ }
+ }
- return $this;
+ return new Payload($claims);
}
/**
- * Set the refresh ttl.
- *
- * @param int $ttl
- *
- * @return $this
+ * Determine whether the given collection of claims has all the required claims.
*/
- public function setRefreshTTL($ttl)
+ protected static function hasRequiredClaims(Collection $claims, ?Options $options = null): bool
{
- $this->refreshTTL = $ttl;
+ // If the collection doesn't have an exp then remove it from the required claims.
+ $requiredClaims = $claims->has(Expiration::NAME)
+ ? $options->requiredClaims()
+ : Arr::except($options->requiredClaims(), [Expiration::NAME]);
- return $this;
+ return $claims->hasAllClaims($requiredClaims);
}
}
diff --git a/src/Validators/TokenValidator.php b/src/Validators/TokenValidator.php
index b76f13e9e..5f0be6b5a 100644
--- a/src/Validators/TokenValidator.php
+++ b/src/Validators/TokenValidator.php
@@ -11,41 +11,25 @@
namespace Tymon\JWTAuth\Validators;
-use Tymon\JWTAuth\Exceptions\TokenInvalidException;
-
class TokenValidator extends Validator
{
/**
* Check the structure of the token.
*
- * @param string $value
- *
- * @return string
- */
- public function check($value)
- {
- return $this->validateStructure($value);
- }
-
- /**
- * @param string $token
- *
* @throws \Tymon\JWTAuth\Exceptions\TokenInvalidException
- *
- * @return string
*/
- protected function validateStructure($token)
+ public static function check(string $token): string
{
$parts = explode('.', $token);
if (count($parts) !== 3) {
- throw new TokenInvalidException('Wrong number of segments');
+ static::throwFailed('Wrong number of segments');
}
$parts = array_filter(array_map('trim', $parts));
if (count($parts) !== 3 || implode('.', $parts) !== $token) {
- throw new TokenInvalidException('Malformed token');
+ static::throwFailed('Malformed token');
}
return $token;
diff --git a/src/Validators/Validator.php b/src/Validators/Validator.php
index bb97f1f83..78d369aab 100644
--- a/src/Validators/Validator.php
+++ b/src/Validators/Validator.php
@@ -11,25 +11,18 @@
namespace Tymon\JWTAuth\Validators;
-use Tymon\JWTAuth\Support\RefreshFlow;
use Tymon\JWTAuth\Exceptions\JWTException;
-use Tymon\JWTAuth\Contracts\Validator as ValidatorContract;
+use Tymon\JWTAuth\Exceptions\TokenInvalidException;
-abstract class Validator implements ValidatorContract
+abstract class Validator
{
- use RefreshFlow;
-
/**
* Helper function to return a boolean.
- *
- * @param array $value
- *
- * @return bool
*/
- public function isValid($value)
+ public static function isValid(...$args): bool
{
try {
- $this->check($value);
+ forward_static_call('static::check', ...$args);
} catch (JWTException $e) {
return false;
}
@@ -38,11 +31,10 @@ public function isValid($value)
}
/**
- * Run the validation.
- *
- * @param array $value
- *
- * @return void
+ * Validation failed.
*/
- abstract public function check($value);
+ public static function throwFailed(string $message = 'Invalid'): void
+ {
+ throw new TokenInvalidException($message);
+ }
}
diff --git a/tests/AbstractTestCase.php b/tests/AbstractTestCase.php
index 7ba31e3f3..84d5261b0 100644
--- a/tests/AbstractTestCase.php
+++ b/tests/AbstractTestCase.php
@@ -11,8 +11,8 @@
namespace Tymon\JWTAuth\Test;
-use Mockery;
use Carbon\Carbon;
+use Mockery;
use PHPUnit\Framework\TestCase;
abstract class AbstractTestCase extends TestCase
@@ -22,7 +22,7 @@ abstract class AbstractTestCase extends TestCase
*/
protected $testNowTimestamp;
- public function setUp()
+ public function setUp(): void
{
parent::setUp();
@@ -30,7 +30,7 @@ public function setUp()
$this->testNowTimestamp = $now->getTimestamp();
}
- public function tearDown()
+ public function tearDown(): void
{
Carbon::setTestNow();
Mockery::close();
diff --git a/tests/BlacklistTest.php b/tests/BlacklistTest.php
index e9c0396c5..21a4f10d8 100644
--- a/tests/BlacklistTest.php
+++ b/tests/BlacklistTest.php
@@ -12,17 +12,15 @@
namespace Tymon\JWTAuth\Test;
use Mockery;
-use Tymon\JWTAuth\Payload;
use Tymon\JWTAuth\Blacklist;
-use Tymon\JWTAuth\Claims\JwtId;
-use Tymon\JWTAuth\Claims\Issuer;
-use Tymon\JWTAuth\Claims\Subject;
+use Tymon\JWTAuth\Claims\Expiration;
use Tymon\JWTAuth\Claims\IssuedAt;
+use Tymon\JWTAuth\Claims\Issuer;
+use Tymon\JWTAuth\Claims\JwtId;
use Tymon\JWTAuth\Claims\NotBefore;
-use Tymon\JWTAuth\Claims\Collection;
-use Tymon\JWTAuth\Claims\Expiration;
+use Tymon\JWTAuth\Claims\Subject;
use Tymon\JWTAuth\Contracts\Providers\Storage;
-use Tymon\JWTAuth\Validators\PayloadValidator;
+use Tymon\JWTAuth\Factory;
class BlacklistTest extends AbstractTestCase
{
@@ -36,117 +34,100 @@ class BlacklistTest extends AbstractTestCase
*/
protected $blacklist;
- /**
- * @var \Mockery\MockInterface|\Tymon\JWTAuth\Validators\Validator
- */
- protected $validator;
-
- public function setUp()
+ public function setUp(): void
{
parent::setUp();
$this->storage = Mockery::mock(Storage::class);
$this->blacklist = new Blacklist($this->storage);
- $this->validator = Mockery::mock(PayloadValidator::class);
}
/** @test */
public function it_should_add_a_valid_token_to_the_blacklist()
{
- $claims = [
+ $payload = Factory::make([
new Subject(1),
new Issuer('http://example.com'),
new Expiration($this->testNowTimestamp + 3600),
new NotBefore($this->testNowTimestamp),
new IssuedAt($this->testNowTimestamp),
new JwtId('foo'),
- ];
-
- $collection = Collection::make($claims);
+ ]);
- $this->validator->shouldReceive('setRefreshFlow->check')->andReturn($collection);
+ $this->storage->shouldReceive('get')
+ ->with('foo')
+ ->once()
+ ->andReturn([]);
- $payload = new Payload($collection, $this->validator);
+ $this->storage->shouldReceive('add')
+ ->with('foo', ['valid_until' => $this->testNowTimestamp], 61)
+ ->once();
- $this->storage->shouldReceive('add')->with('foo', ['valid_until' => $this->testNowTimestamp], 20161)->once();
$this->blacklist->add($payload);
}
/** @test */
- public function it_should_add_a_token_with_no_exp_to_the_blacklist_forever()
+ public function it_should_return_early_when_adding_an_item_and_it_already_exists()
{
- $claims = [
+ $payload = Factory::make([
new Subject(1),
new Issuer('http://example.com'),
+ new Expiration($this->testNowTimestamp + 3600),
new NotBefore($this->testNowTimestamp),
new IssuedAt($this->testNowTimestamp),
new JwtId('foo'),
- ];
- $collection = Collection::make($claims);
+ ]);
- $this->validator->shouldReceive('setRefreshFlow->check')->andReturn($collection);
+ $this->storage->shouldReceive('get')
+ ->with('foo')
+ ->once()
+ ->andReturn(['valid_until' => $this->testNowTimestamp]);
- $payload = new Payload($collection, $this->validator);
+ $this->storage->shouldReceive('add')
+ ->with('foo', ['valid_until' => $this->testNowTimestamp], 61)
+ ->never();
- $this->storage->shouldReceive('forever')->with('foo', 'forever')->once();
$this->blacklist->add($payload);
}
/** @test */
- public function it_should_return_true_when_adding_an_expired_token_to_the_blacklist()
+ public function it_should_add_a_token_with_no_exp_to_the_blacklist_forever()
{
- $claims = [
+ $payload = Factory::make([
new Subject(1),
new Issuer('http://example.com'),
- new Expiration($this->testNowTimestamp - 3600),
new NotBefore($this->testNowTimestamp),
new IssuedAt($this->testNowTimestamp),
new JwtId('foo'),
- ];
- $collection = Collection::make($claims);
-
- $this->validator->shouldReceive('setRefreshFlow->check')->andReturn($collection);
+ ]);
- $payload = new Payload($collection, $this->validator, true);
+ $this->storage->shouldReceive('forever')
+ ->with('foo', $this->blacklist::FOREVER)
+ ->once();
- $this->storage->shouldReceive('add')->with('foo', ['valid_until' => $this->testNowTimestamp], 20161)->once();
- $this->assertTrue($this->blacklist->add($payload));
+ $this->blacklist->add($payload);
}
/** @test */
public function it_should_check_whether_a_token_has_been_blacklisted()
{
- $claims = [
+ $payload = Factory::make([
new Subject(1),
new Issuer('http://example.com'),
new Expiration($this->testNowTimestamp + 3600),
new NotBefore($this->testNowTimestamp),
new IssuedAt($this->testNowTimestamp),
new JwtId('foobar'),
- ];
-
- $collection = Collection::make($claims);
+ ]);
- $this->validator->shouldReceive('setRefreshFlow->check')->andReturn($collection);
-
- $payload = new Payload($collection, $this->validator);
-
- $this->storage->shouldReceive('get')->with('foobar')->once()->andReturn(['valid_until' => $this->testNowTimestamp]);
+ $this->storage->shouldReceive('get')
+ ->with('foobar')
+ ->once()
+ ->andReturn(['valid_until' => $this->testNowTimestamp]);
$this->assertTrue($this->blacklist->has($payload));
}
- public function blacklist_provider()
- {
- return [
- [null],
- [0],
- [''],
- [[]],
- [['valid_until' => strtotime('+1day')]],
- ];
- }
-
/**
* @test
* @dataProvider blacklist_provider
@@ -155,43 +136,39 @@ public function blacklist_provider()
*/
public function it_should_check_whether_a_token_has_not_been_blacklisted($result)
{
- $claims = [
+ $payload = Factory::make([
new Subject(1),
new Issuer('http://example.com'),
new Expiration($this->testNowTimestamp + 3600),
new NotBefore($this->testNowTimestamp),
new IssuedAt($this->testNowTimestamp),
new JwtId('foobar'),
- ];
-
- $collection = Collection::make($claims);
+ ]);
- $this->validator->shouldReceive('setRefreshFlow->check')->andReturn($collection);
+ $this->storage->shouldReceive('get')
+ ->with('foobar')
+ ->once()
+ ->andReturn($result);
- $payload = new Payload($collection, $this->validator);
-
- $this->storage->shouldReceive('get')->with('foobar')->once()->andReturn($result);
$this->assertFalse($this->blacklist->has($payload));
}
/** @test */
public function it_should_check_whether_a_token_has_been_blacklisted_forever()
{
- $claims = [
+ $payload = Factory::make([
new Subject(1),
new Issuer('http://example.com'),
new Expiration($this->testNowTimestamp + 3600),
new NotBefore($this->testNowTimestamp),
new IssuedAt($this->testNowTimestamp),
new JwtId('foobar'),
- ];
- $collection = Collection::make($claims);
-
- $this->validator->shouldReceive('setRefreshFlow->check')->andReturn($collection);
+ ]);
- $payload = new Payload($collection, $this->validator);
-
- $this->storage->shouldReceive('get')->with('foobar')->once()->andReturn('forever');
+ $this->storage->shouldReceive('get')
+ ->with('foobar')
+ ->once()
+ ->andReturn($this->blacklist::FOREVER);
$this->assertTrue($this->blacklist->has($payload));
}
@@ -199,21 +176,19 @@ public function it_should_check_whether_a_token_has_been_blacklisted_forever()
/** @test */
public function it_should_check_whether_a_token_has_been_blacklisted_when_the_token_is_not_blacklisted()
{
- $claims = [
+ $payload = Factory::make([
new Subject(1),
new Issuer('http://example.com'),
new Expiration($this->testNowTimestamp + 3600),
new NotBefore($this->testNowTimestamp),
new IssuedAt($this->testNowTimestamp),
new JwtId('foobar'),
- ];
- $collection = Collection::make($claims);
-
- $this->validator->shouldReceive('setRefreshFlow->check')->andReturn($collection);
+ ]);
- $payload = new Payload($collection, $this->validator);
-
- $this->storage->shouldReceive('get')->with('foobar')->once()->andReturn(null);
+ $this->storage->shouldReceive('get')
+ ->with('foobar')
+ ->once()
+ ->andReturn(null);
$this->assertFalse($this->blacklist->has($payload));
}
@@ -221,52 +196,48 @@ public function it_should_check_whether_a_token_has_been_blacklisted_when_the_to
/** @test */
public function it_should_remove_a_token_from_the_blacklist()
{
- $claims = [
+ $payload = Factory::make([
new Subject(1),
new Issuer('http://example.com'),
new Expiration($this->testNowTimestamp + 3600),
new NotBefore($this->testNowTimestamp),
new IssuedAt($this->testNowTimestamp),
new JwtId('foobar'),
- ];
- $collection = Collection::make($claims);
-
- $this->validator->shouldReceive('setRefreshFlow->check')->andReturn($collection);
+ ]);
- $payload = new Payload($collection, $this->validator);
+ $this->storage->shouldReceive('destroy')
+ ->with('foobar')
+ ->once();
- $this->storage->shouldReceive('destroy')->with('foobar')->andReturn(true);
- $this->assertTrue($this->blacklist->remove($payload));
+ $this->blacklist->remove($payload);
}
/** @test */
public function it_should_set_a_custom_unique_key_for_the_blacklist()
{
- $claims = [
+ $payload = Factory::make([
new Subject(1),
new Issuer('http://example.com'),
new Expiration($this->testNowTimestamp + 3600),
new NotBefore($this->testNowTimestamp),
new IssuedAt($this->testNowTimestamp),
new JwtId('foobar'),
- ];
- $collection = Collection::make($claims);
+ ]);
- $this->validator->shouldReceive('setRefreshFlow->check')->andReturn($collection);
+ $this->storage->shouldReceive('get')
+ ->with(1)
+ ->once()
+ ->andReturn(['valid_until' => $this->testNowTimestamp]);
- $payload = new Payload($collection, $this->validator);
-
- $this->storage->shouldReceive('get')->with(1)->once()->andReturn(['valid_until' => $this->testNowTimestamp]);
-
- $this->assertTrue($this->blacklist->setKey('sub')->has($payload));
- $this->assertSame(1, $this->blacklist->getKey($payload));
+ $this->assertTrue($this->blacklist->setKey(Subject::NAME)->has($payload));
+ $this->assertSame('1', $this->blacklist->getKey($payload));
}
/** @test */
public function it_should_empty_the_blacklist()
{
- $this->storage->shouldReceive('flush');
- $this->assertTrue($this->blacklist->clear());
+ $this->storage->shouldReceive('flush')->once();
+ $this->blacklist->clear();
}
/** @test */
@@ -276,10 +247,8 @@ public function it_should_set_and_get_the_blacklist_grace_period()
$this->assertSame(15, $this->blacklist->getGracePeriod());
}
- /** @test */
- public function it_should_set_and_get_the_blacklist_refresh_ttl()
+ public function blacklist_provider()
{
- $this->assertInstanceOf(Blacklist::class, $this->blacklist->setRefreshTTL(15));
- $this->assertSame(15, $this->blacklist->getRefreshTTL());
+ return [[null], [0], [''], [[]], [['valid_until' => strtotime('+1day')]]];
}
}
diff --git a/tests/Claims/ClaimTest.php b/tests/Claims/ClaimTest.php
index 75f4bacf0..d199f324a 100644
--- a/tests/Claims/ClaimTest.php
+++ b/tests/Claims/ClaimTest.php
@@ -11,9 +11,10 @@
namespace Tymon\JWTAuth\Test\Claims;
+use Illuminate\Contracts\Support\Arrayable;
use Tymon\JWTAuth\Claims\Expiration;
+use Tymon\JWTAuth\Exceptions\InvalidClaimException;
use Tymon\JWTAuth\Test\AbstractTestCase;
-use Illuminate\Contracts\Support\Arrayable;
class ClaimTest extends AbstractTestCase
{
@@ -22,27 +23,26 @@ class ClaimTest extends AbstractTestCase
*/
protected $claim;
- public function setUp()
+ public function setUp(): void
{
parent::setUp();
$this->claim = new Expiration($this->testNowTimestamp);
}
- /**
- * @test
- * @expectedException \Tymon\JWTAuth\Exceptions\InvalidClaimException
- * @expectedExceptionMessage Invalid value provided for claim [exp]
- */
+ /** @test */
public function it_should_throw_an_exception_when_passing_an_invalid_value()
{
+ $this->expectException(InvalidClaimException::class);
+ $this->expectExceptionMessage('Invalid value provided for claim [exp]');
+
$this->claim->setValue('foo');
}
/** @test */
public function it_should_convert_the_claim_to_an_array()
{
- $this->assertSame(['exp' => $this->testNowTimestamp], $this->claim->toArray());
+ $this->assertSame([Expiration::NAME => $this->testNowTimestamp], $this->claim->toArray());
}
/** @test */
diff --git a/tests/Claims/CollectionTest.php b/tests/Claims/CollectionTest.php
index 0c9bf4780..76409f2d1 100644
--- a/tests/Claims/CollectionTest.php
+++ b/tests/Claims/CollectionTest.php
@@ -11,37 +11,30 @@
namespace Tymon\JWTAuth\Test\Claims;
-use Tymon\JWTAuth\Claims\JwtId;
-use Tymon\JWTAuth\Claims\Issuer;
-use Tymon\JWTAuth\Claims\Subject;
-use Tymon\JWTAuth\Claims\IssuedAt;
-use Tymon\JWTAuth\Claims\NotBefore;
use Tymon\JWTAuth\Claims\Collection;
use Tymon\JWTAuth\Claims\Expiration;
+use Tymon\JWTAuth\Claims\IssuedAt;
+use Tymon\JWTAuth\Claims\Issuer;
+use Tymon\JWTAuth\Claims\JwtId;
+use Tymon\JWTAuth\Claims\NotBefore;
+use Tymon\JWTAuth\Claims\Subject;
use Tymon\JWTAuth\Test\AbstractTestCase;
class CollectionTest extends AbstractTestCase
{
- private function getCollection()
- {
- $claims = [
- new Subject(1),
- new Issuer('http://example.com'),
- new Expiration($this->testNowTimestamp + 3600),
- new NotBefore($this->testNowTimestamp),
- new IssuedAt($this->testNowTimestamp),
- new JwtId('foo'),
- ];
-
- return new Collection($claims);
- }
-
/** @test */
public function it_should_sanitize_the_claims_to_associative_array()
{
$collection = $this->getCollection();
- $this->assertSame(array_keys($collection->toArray()), ['sub', 'iss', 'exp', 'nbf', 'iat', 'jti']);
+ $this->assertSame(array_keys($collection->toArray()), [
+ Subject::NAME,
+ Issuer::NAME,
+ Expiration::NAME,
+ NotBefore::NAME,
+ IssuedAt::NAME,
+ JwtId::NAME,
+ ]);
}
/** @test */
@@ -49,12 +42,14 @@ public function it_should_determine_if_a_collection_contains_all_the_given_claim
{
$collection = $this->getCollection();
- $this->assertFalse($collection->hasAllClaims(['sub', 'iss', 'exp', 'nbf', 'iat', 'jti', 'abc']));
+ $this->assertFalse(
+ $collection->hasAllClaims([Subject::NAME, Issuer::NAME, Expiration::NAME, NotBefore::NAME, IssuedAt::NAME, JwtId::NAME, 'abc'])
+ );
$this->assertFalse($collection->hasAllClaims(['foo', 'bar']));
- $this->assertFalse($collection->hasAllClaims([]));
- $this->assertTrue($collection->hasAllClaims(['sub', 'iss']));
- $this->assertTrue($collection->hasAllClaims(['sub', 'iss', 'exp', 'nbf', 'iat', 'jti']));
+ $this->assertTrue($collection->hasAllClaims([]));
+ $this->assertTrue($collection->hasAllClaims([Subject::NAME, Issuer::NAME]));
+ $this->assertTrue($collection->hasAllClaims([Subject::NAME, Issuer::NAME, Expiration::NAME, NotBefore::NAME, IssuedAt::NAME, JwtId::NAME]));
}
/** @test */
@@ -62,7 +57,21 @@ public function it_should_get_a_claim_instance_by_name()
{
$collection = $this->getCollection();
- $this->assertInstanceOf(Expiration::class, $collection->getByClaimName('exp'));
- $this->assertInstanceOf(Subject::class, $collection->getByClaimName('sub'));
+ $this->assertInstanceOf(Expiration::class, $collection->getByClaimName(Expiration::NAME));
+ $this->assertInstanceOf(Subject::class, $collection->getByClaimName(Subject::NAME));
+ $this->assertInstanceOf(Issuer::class, $collection->getByClaimName(Issuer::NAME));
+ $this->assertInstanceOf(JwtId::class, $collection->getByClaimName(JwtId::NAME));
+ }
+
+ private function getCollection()
+ {
+ return new Collection([
+ new Subject(1),
+ new Issuer('http://example.com'),
+ new Expiration($this->testNowTimestamp + 3600),
+ new NotBefore($this->testNowTimestamp),
+ new IssuedAt($this->testNowTimestamp),
+ new JwtId('foo'),
+ ]);
}
}
diff --git a/tests/Claims/DatetimeClaimTest.php b/tests/Claims/DatetimeClaimTest.php
index ebd56d55c..75849e025 100644
--- a/tests/Claims/DatetimeClaimTest.php
+++ b/tests/Claims/DatetimeClaimTest.php
@@ -11,22 +11,21 @@
namespace Tymon\JWTAuth\Test\Claims;
-use Mockery;
-use DateTime;
-use DateInterval;
use Carbon\Carbon;
+use Carbon\CarbonInterval;
+use DateInterval;
+use DateTime;
use DateTimeImmutable;
use DateTimeInterface;
-use Tymon\JWTAuth\Payload;
-use Tymon\JWTAuth\Claims\JwtId;
-use Tymon\JWTAuth\Claims\Issuer;
-use Tymon\JWTAuth\Claims\Subject;
-use Tymon\JWTAuth\Claims\IssuedAt;
-use Tymon\JWTAuth\Claims\NotBefore;
use Tymon\JWTAuth\Claims\Collection;
use Tymon\JWTAuth\Claims\Expiration;
+use Tymon\JWTAuth\Claims\IssuedAt;
+use Tymon\JWTAuth\Claims\Issuer;
+use Tymon\JWTAuth\Claims\JwtId;
+use Tymon\JWTAuth\Claims\NotBefore;
+use Tymon\JWTAuth\Claims\Subject;
+use Tymon\JWTAuth\Payload;
use Tymon\JWTAuth\Test\AbstractTestCase;
-use Tymon\JWTAuth\Validators\PayloadValidator;
class DatetimeClaimTest extends AbstractTestCase
{
@@ -40,20 +39,17 @@ class DatetimeClaimTest extends AbstractTestCase
*/
protected $claimsTimestamp;
- public function setUp()
+ public function setUp(): void
{
parent::setUp();
- $this->validator = Mockery::mock(PayloadValidator::class);
- $this->validator->shouldReceive('setRefreshFlow->check');
-
$this->claimsTimestamp = [
- 'sub' => new Subject(1),
- 'iss' => new Issuer('http://example.com'),
- 'exp' => new Expiration($this->testNowTimestamp + 3600),
- 'nbf' => new NotBefore($this->testNowTimestamp),
- 'iat' => new IssuedAt($this->testNowTimestamp),
- 'jti' => new JwtId('foo'),
+ Subject::NAME => new Subject(1),
+ Issuer::NAME => new Issuer('http://example.com'),
+ Expiration::NAME => new Expiration($this->testNowTimestamp + 3600),
+ NotBefore::NAME => new NotBefore($this->testNowTimestamp),
+ IssuedAt::NAME => new IssuedAt($this->testNowTimestamp),
+ JwtId::NAME => new JwtId('foo'),
];
}
@@ -68,16 +64,16 @@ public function it_should_handle_carbon_claims()
$this->assertInstanceOf(DatetimeInterface::class, $testCarbon);
$claimsDatetime = [
- 'sub' => new Subject(1),
- 'iss' => new Issuer('http://example.com'),
- 'exp' => new Expiration($testCarbonCopy->addHour()),
- 'nbf' => new NotBefore($testCarbon),
- 'iat' => new IssuedAt($testCarbon),
- 'jti' => new JwtId('foo'),
+ Subject::NAME => new Subject(1),
+ Issuer::NAME => new Issuer('http://example.com'),
+ Expiration::NAME => new Expiration($testCarbonCopy->addHour()),
+ NotBefore::NAME => new NotBefore($testCarbon),
+ IssuedAt::NAME => new IssuedAt($testCarbon),
+ JwtId::NAME => new JwtId('foo'),
];
- $payloadTimestamp = new Payload(Collection::make($this->claimsTimestamp), $this->validator);
- $payloadDatetime = new Payload(Collection::make($claimsDatetime), $this->validator);
+ $payloadTimestamp = new Payload(Collection::make($this->claimsTimestamp));
+ $payloadDatetime = new Payload(Collection::make($claimsDatetime));
$this->assertEquals($payloadTimestamp, $payloadDatetime);
}
@@ -92,16 +88,16 @@ public function it_should_handle_datetime_claims()
$this->assertInstanceOf(DatetimeInterface::class, $testDateTime);
$claimsDatetime = [
- 'sub' => new Subject(1),
- 'iss' => new Issuer('http://example.com'),
- 'exp' => new Expiration($testDateTimeCopy->modify('+3600 seconds')),
- 'nbf' => new NotBefore($testDateTime),
- 'iat' => new IssuedAt($testDateTime),
- 'jti' => new JwtId('foo'),
+ Subject::NAME => new Subject(1),
+ Issuer::NAME => new Issuer('http://example.com'),
+ Expiration::NAME => new Expiration($testDateTimeCopy->modify('+3600 seconds')),
+ NotBefore::NAME => new NotBefore($testDateTime),
+ IssuedAt::NAME => new IssuedAt($testDateTime),
+ JwtId::NAME => new JwtId('foo'),
];
- $payloadTimestamp = new Payload(Collection::make($this->claimsTimestamp), $this->validator);
- $payloadDatetime = new Payload(Collection::make($claimsDatetime), $this->validator);
+ $payloadTimestamp = new Payload(Collection::make($this->claimsTimestamp));
+ $payloadDatetime = new Payload(Collection::make($claimsDatetime));
$this->assertEquals($payloadTimestamp, $payloadDatetime);
}
@@ -109,22 +105,25 @@ public function it_should_handle_datetime_claims()
/** @test */
public function it_should_handle_datetime_immutable_claims()
{
- $testDateTimeImmutable = DateTimeImmutable::createFromFormat('U', (string) $this->testNowTimestamp);
+ $testDateTimeImmutable = DateTimeImmutable::createFromFormat(
+ 'U',
+ (string) $this->testNowTimestamp
+ );
$this->assertInstanceOf(DateTimeImmutable::class, $testDateTimeImmutable);
$this->assertInstanceOf(DatetimeInterface::class, $testDateTimeImmutable);
$claimsDatetime = [
- 'sub' => new Subject(1),
- 'iss' => new Issuer('http://example.com'),
- 'exp' => new Expiration($testDateTimeImmutable->modify('+3600 seconds')),
- 'nbf' => new NotBefore($testDateTimeImmutable),
- 'iat' => new IssuedAt($testDateTimeImmutable),
- 'jti' => new JwtId('foo'),
+ Subject::NAME => new Subject(1),
+ Issuer::NAME => new Issuer('http://example.com'),
+ Expiration::NAME => new Expiration($testDateTimeImmutable->modify('+3600 seconds')),
+ NotBefore::NAME => new NotBefore($testDateTimeImmutable),
+ IssuedAt::NAME => new IssuedAt($testDateTimeImmutable),
+ JwtId::NAME => new JwtId('foo'),
];
- $payloadTimestamp = new Payload(Collection::make($this->claimsTimestamp), $this->validator);
- $payloadDatetime = new Payload(Collection::make($claimsDatetime), $this->validator);
+ $payloadTimestamp = new Payload(Collection::make($this->claimsTimestamp));
+ $payloadDatetime = new Payload(Collection::make($claimsDatetime));
$this->assertEquals($payloadTimestamp, $payloadDatetime);
}
@@ -132,22 +131,50 @@ public function it_should_handle_datetime_immutable_claims()
/** @test */
public function it_should_handle_datetinterval_claims()
{
- $testDateInterval = DateInterval::createFromDateString('PT1H');
+ $testDateInterval = new DateInterval('PT1H');
+ $carbonDateInterval = CarbonInterval::hours(1);
$this->assertInstanceOf(DateInterval::class, $testDateInterval);
+ $this->assertInstanceOf(DateInterval::class, $carbonDateInterval);
$claimsDateInterval = [
- 'sub' => new Subject(1),
- 'iss' => new Issuer('http://example.com'),
- 'exp' => new Expiration($testDateInterval),
- 'nbf' => new NotBefore($this->testNowTimestamp),
- 'iat' => new IssuedAt($this->testNowTimestamp),
- 'jti' => new JwtId('foo'),
+ Subject::NAME => new Subject(1),
+ Issuer::NAME => new Issuer('http://example.com'),
+ Expiration::NAME => new Expiration($testDateInterval),
+ NotBefore::NAME => new NotBefore($this->testNowTimestamp),
+ IssuedAt::NAME => new IssuedAt($this->testNowTimestamp),
+ JwtId::NAME => new JwtId('foo'),
+ ];
+
+ $claimsCarbonInterval = [
+ Subject::NAME => new Subject(1),
+ Issuer::NAME => new Issuer('http://example.com'),
+ Expiration::NAME => new Expiration($carbonDateInterval),
+ NotBefore::NAME => new NotBefore($this->testNowTimestamp),
+ IssuedAt::NAME => new IssuedAt($this->testNowTimestamp),
+ JwtId::NAME => new JwtId('foo'),
];
- $payloadTimestamp = new Payload(Collection::make($this->claimsTimestamp), $this->validator);
- $payloadDateInterval = new Payload(Collection::make($claimsDateInterval), $this->validator);
+ $payloadTimestamp = new Payload(Collection::make($this->claimsTimestamp));
+
+ $payloadDateInterval = new Payload(Collection::make($claimsDateInterval));
+ $payloadClaimInterval = new Payload(Collection::make($claimsCarbonInterval));
$this->assertEquals($payloadTimestamp, $payloadDateInterval);
+ $this->assertEquals($payloadTimestamp, $payloadClaimInterval);
+ }
+
+ /** @test */
+ public function it_should_get_the_date_interval_instance()
+ {
+ $exp = new Expiration($this->testNowTimestamp + ($seconds = 3600));
+ $this->assertInstanceOf(CarbonInterval::class, $exp->asCarbonInterval());
+ $this->assertEquals(CarbonInterval::seconds($seconds)->cascade(), $exp->asCarbonInterval());
+ $this->assertEquals('PT1H', $exp->asCarbonInterval()->spec());
+
+ $iat = new IssuedAt($this->testNowTimestamp);
+ $this->assertInstanceOf(CarbonInterval::class, $iat->asCarbonInterval());
+ $this->assertEquals(CarbonInterval::seconds(0)->cascade(), $iat->asCarbonInterval());
+ $this->assertEquals('PT0S', $iat->asCarbonInterval()->spec());
}
}
diff --git a/tests/Claims/FactoryTest.php b/tests/Claims/FactoryTest.php
index cf915890a..78af12d82 100644
--- a/tests/Claims/FactoryTest.php
+++ b/tests/Claims/FactoryTest.php
@@ -11,97 +11,60 @@
namespace Tymon\JWTAuth\Test\Claims;
-use Illuminate\Http\Request;
-use Tymon\JWTAuth\Claims\JwtId;
use Tymon\JWTAuth\Claims\Custom;
-use Tymon\JWTAuth\Claims\Issuer;
+use Tymon\JWTAuth\Claims\Expiration;
use Tymon\JWTAuth\Claims\Factory;
-use Tymon\JWTAuth\Claims\Subject;
use Tymon\JWTAuth\Claims\IssuedAt;
+use Tymon\JWTAuth\Claims\Issuer;
+use Tymon\JWTAuth\Claims\JwtId;
use Tymon\JWTAuth\Claims\NotBefore;
-use Tymon\JWTAuth\Claims\Expiration;
-use Tymon\JWTAuth\Test\Fixtures\Foo;
+use Tymon\JWTAuth\Claims\Subject;
+use Tymon\JWTAuth\Options;
use Tymon\JWTAuth\Test\AbstractTestCase;
class FactoryTest extends AbstractTestCase
{
- /**
- * @var \Tymon\JWTAuth\Claims\Factory
- */
- protected $factory;
-
- public function setUp()
- {
- parent::setUp();
-
- $this->factory = new Factory(Request::create('/foo', 'GET'));
- }
-
- /** @test */
- public function it_should_set_the_request()
- {
- $factory = $this->factory->setRequest(Request::create('/bar', 'GET'));
- $this->assertInstanceOf(Factory::class, $factory);
- }
-
- /** @test */
- public function it_should_set_the_ttl()
- {
- $this->assertInstanceOf(Factory::class, $this->factory->setTTL(30));
- }
-
- /** @test */
- public function it_should_get_the_ttl()
- {
- $this->factory->setTTL($ttl = 30);
- $this->assertSame($ttl, $this->factory->getTTL());
- }
-
/** @test */
public function it_should_get_a_defined_claim_instance_when_passing_a_name_and_value()
{
- $this->assertInstanceOf(Subject::class, $this->factory->get('sub', 1));
- $this->assertInstanceOf(Issuer::class, $this->factory->get('iss', 'http://example.com'));
- $this->assertInstanceOf(Expiration::class, $this->factory->get('exp', $this->testNowTimestamp + 3600));
- $this->assertInstanceOf(NotBefore::class, $this->factory->get('nbf', $this->testNowTimestamp));
- $this->assertInstanceOf(IssuedAt::class, $this->factory->get('iat', $this->testNowTimestamp));
- $this->assertInstanceOf(JwtId::class, $this->factory->get('jti', 'foo'));
+ $this->assertInstanceOf(Subject::class, Factory::get(Subject::NAME, 1));
+ $this->assertInstanceOf(Issuer::class, Factory::get(Issuer::NAME, 'http://example.com'));
+ $this->assertInstanceOf(
+ Expiration::class,
+ Factory::get(Expiration::NAME, $this->testNowTimestamp + 3600)
+ );
+ $this->assertInstanceOf(NotBefore::class, Factory::get(NotBefore::NAME, $this->testNowTimestamp));
+ $this->assertInstanceOf(IssuedAt::class, Factory::get(IssuedAt::NAME, $this->testNowTimestamp));
+ $this->assertInstanceOf(JwtId::class, Factory::get(JwtId::NAME, 'foo'));
}
/** @test */
public function it_should_get_a_custom_claim_instance_when_passing_a_non_defined_name_and_value()
{
- $this->assertInstanceOf(Custom::class, $this->factory->get('foo', ['bar']));
+ $this->assertInstanceOf(Custom::class, Factory::get('foo', ['bar']));
}
/** @test */
- public function it_should_make_a_claim_instance_with_a_value()
+ public function it_should_make_a_claim_instance_for_inferred_claims()
{
- $iat = $this->factory->make('iat');
- $this->assertSame($iat->getValue(), $this->testNowTimestamp);
+ $iat = Factory::get(IssuedAt::NAME, null, new Options([
+ 'leeway' => 10,
+ 'max_refresh_period' => 2,
+ ]));
+ $this->assertSame($this->testNowTimestamp, $iat->getValue());
$this->assertInstanceOf(IssuedAt::class, $iat);
-
- $nbf = $this->factory->make('nbf');
- $this->assertSame($nbf->getValue(), $this->testNowTimestamp);
+ $this->assertEquals($iat->getLeeway(), 10);
+ $this->assertEquals($iat->getMaxRefreshPeriod(), 2);
+
+ $nbf = Factory::get(NotBefore::NAME, null, new Options([
+ 'leeway' => 20,
+ 'max_refresh_period' => 1,
+ ]));
+ $this->assertSame($this->testNowTimestamp, $nbf->getValue());
$this->assertInstanceOf(NotBefore::class, $nbf);
+ $this->assertEquals($nbf->getLeeway(), 20);
+ $this->assertEquals($nbf->getMaxRefreshPeriod(), 1);
- $iss = $this->factory->make('iss');
- $this->assertSame($iss->getValue(), 'http://localhost/foo');
- $this->assertInstanceOf(Issuer::class, $iss);
-
- $exp = $this->factory->make('exp');
- $this->assertSame($exp->getValue(), $this->testNowTimestamp + 3600);
- $this->assertInstanceOf(Expiration::class, $exp);
-
- $jti = $this->factory->make('jti');
- $this->assertInstanceOf(JwtId::class, $jti);
- }
-
- /** @test */
- public function it_should_extend_claim_factory_to_add_a_custom_claim()
- {
- $this->factory->extend('foo', Foo::class);
-
- $this->assertInstanceOf(Foo::class, $this->factory->get('foo', 'bar'));
+ $this->assertInstanceOf(JwtId::class, Factory::get(JwtId::NAME));
}
}
diff --git a/tests/Claims/IssuedAtTest.php b/tests/Claims/IssuedAtTest.php
index 700731130..f57874aea 100644
--- a/tests/Claims/IssuedAtTest.php
+++ b/tests/Claims/IssuedAtTest.php
@@ -12,17 +12,17 @@
namespace Tymon\JWTAuth\Test\Claims;
use Tymon\JWTAuth\Claims\IssuedAt;
+use Tymon\JWTAuth\Exceptions\InvalidClaimException;
use Tymon\JWTAuth\Test\AbstractTestCase;
class IssuedAtTest extends AbstractTestCase
{
- /**
- * @test
- * @expectedException \Tymon\JWTAuth\Exceptions\InvalidClaimException
- * @expectedExceptionMessage Invalid value provided for claim [iat]
- */
+ /** @test */
public function it_should_throw_an_exception_when_passing_a_future_timestamp()
{
+ $this->expectException(InvalidClaimException::class);
+ $this->expectExceptionMessage('Invalid value provided for claim [iat]');
+
new IssuedAt($this->testNowTimestamp + 3600);
}
}
diff --git a/tests/Claims/NotBeforeTest.php b/tests/Claims/NotBeforeTest.php
index d8475b335..185c108aa 100644
--- a/tests/Claims/NotBeforeTest.php
+++ b/tests/Claims/NotBeforeTest.php
@@ -12,27 +12,17 @@
namespace Tymon\JWTAuth\Test\Claims;
use Tymon\JWTAuth\Claims\NotBefore;
+use Tymon\JWTAuth\Exceptions\InvalidClaimException;
use Tymon\JWTAuth\Test\AbstractTestCase;
class NotBeforeTest extends AbstractTestCase
{
- /**
- * @test
- * @expectedException \Tymon\JWTAuth\Exceptions\InvalidClaimException
- * @expectedExceptionMessage Invalid value provided for claim [nbf]
- */
- public function it_should_throw_an_exception_when_passing_a_future_timestamp()
- {
- new NotBefore($this->testNowTimestamp + 3600);
- }
-
- /**
- * @test
- * @expectedException \Tymon\JWTAuth\Exceptions\InvalidClaimException
- * @expectedExceptionMessage Invalid value provided for claim [nbf]
- */
+ /** @test */
public function it_should_throw_an_exception_when_passing_an_invalid_value()
{
+ $this->expectException(InvalidClaimException::class);
+ $this->expectExceptionMessage('Invalid value provided for claim [nbf]');
+
new NotBefore('foo');
}
}
diff --git a/tests/FactoryTest.php b/tests/FactoryTest.php
index f915a59d0..2f022737e 100644
--- a/tests/FactoryTest.php
+++ b/tests/FactoryTest.php
@@ -11,236 +11,102 @@
namespace Tymon\JWTAuth\Test;
-use Mockery;
-use Tymon\JWTAuth\Factory;
-use Tymon\JWTAuth\Payload;
-use Tymon\JWTAuth\Claims\JwtId;
use Tymon\JWTAuth\Claims\Custom;
-use Tymon\JWTAuth\Claims\Issuer;
-use Tymon\JWTAuth\Claims\Subject;
use Tymon\JWTAuth\Claims\IssuedAt;
+use Tymon\JWTAuth\Claims\Issuer;
+use Tymon\JWTAuth\Claims\JwtId;
use Tymon\JWTAuth\Claims\NotBefore;
-use Tymon\JWTAuth\Claims\Collection;
-use Tymon\JWTAuth\Claims\Expiration;
-use Tymon\JWTAuth\Validators\PayloadValidator;
-use Tymon\JWTAuth\Claims\Factory as ClaimFactory;
+use Tymon\JWTAuth\Claims\Subject;
+use Tymon\JWTAuth\Exceptions\TokenInvalidException;
+use Tymon\JWTAuth\Factory;
+use Tymon\JWTAuth\Options;
+use Tymon\JWTAuth\Payload;
class FactoryTest extends AbstractTestCase
{
- /**
- * @var \Mockery\MockInterface|\Tymon\JWTAuth\Claims\Factory
- */
- protected $claimFactory;
-
- /**
- * @var \Mockery\MockInterface|\Tymon\JWTAuth\Validators\PayloadValidator
- */
- protected $validator;
-
- /**
- * @var \Tymon\JWTAuth\Factory
- */
- protected $factory;
-
- public function setUp()
- {
- parent::setUp();
-
- $this->claimFactory = Mockery::mock(ClaimFactory::class);
- $this->validator = Mockery::mock(PayloadValidator::class);
- $this->factory = new Factory($this->claimFactory, $this->validator);
- }
-
/** @test */
public function it_should_return_a_payload_when_passing_an_array_of_claims()
{
- $expTime = $this->testNowTimestamp + 3600;
-
- // these are added from default claims
- $this->claimFactory->shouldReceive('make')->twice()->with('iss')->andReturn(new Issuer('/foo'));
- $this->claimFactory->shouldReceive('make')->twice()->with('exp')->andReturn(new Expiration($expTime));
- $this->claimFactory->shouldReceive('make')->twice()->with('jti')->andReturn(new JwtId('foo'));
- $this->claimFactory->shouldReceive('make')->twice()->with('nbf')->andReturn(new NotBefore(123));
- $this->claimFactory->shouldReceive('make')->twice()->with('iat')->andReturn(new IssuedAt(123));
-
- // custom claims that override
- $this->claimFactory->shouldReceive('get')->twice()->with('sub', 1)->andReturn(new Subject(1));
- $this->claimFactory->shouldReceive('get')->twice()->with('jti', 'foo')->andReturn(new JwtId('foo'));
- $this->claimFactory->shouldReceive('get')->twice()->with('nbf', 123)->andReturn(new NotBefore(123));
- $this->claimFactory->shouldReceive('get')->twice()->with('iat', 123)->andReturn(new IssuedAt(123));
-
- $this->claimFactory->shouldReceive('getTTL')->andReturn(60);
-
- // once
- $claims = $this->factory->customClaims([
- 'sub' => 1,
- 'jti' => 'foo',
- 'iat' => 123,
- 'nbf' => 123,
- ])->buildClaimsCollection();
-
- $this->validator->shouldReceive('setRefreshFlow->check')->andReturn($claims);
-
- // twice
- $payload = $this->factory->claims(['sub' => 1, 'jti' => 'foo', 'iat' => 123, 'nbf' => 123])->make();
-
- $this->assertSame($payload->get('sub'), 1);
- $this->assertSame($payload->get('iat'), 123);
- $this->assertSame($payload['exp'], $expTime);
- $this->assertSame($payload['jti'], 'foo');
+ $payload = Factory::make([
+ JwtId::NAME, // auto generated
+ IssuedAt::NAME, // auto generated
+ NotBefore::NAME, // auto generated
+ Subject::NAME => 1,
+ 'foo' => 'bar',
+ ]);
+
+ $this->assertSame($payload->get(Subject::NAME), 1);
+ $this->assertSame($payload(IssuedAt::NAME), $this->testNowTimestamp);
+ $this->assertSame($payload(NotBefore::NAME), $this->testNowTimestamp);
+ $this->assertSame($payload['foo'], 'bar');
$this->assertInstanceOf(Payload::class, $payload);
+ $this->assertInstanceOf(Subject::class, $payload->getInternal(Subject::NAME));
+ $this->assertInstanceOf(IssuedAt::class, $payload->getInternal(IssuedAt::NAME));
+ $this->assertInstanceOf(JwtId::class, $payload->getInternal(JwtId::NAME));
+ $this->assertInstanceOf(NotBefore::class, $payload->getInternal(NotBefore::NAME));
+ $this->assertInstanceOf(Custom::class, $payload->getInternal('foo'));
}
/** @test */
- public function it_should_return_a_payload_when_chaining_claim_methods()
+ public function it_should_return_a_payload_when_passing_an_array_of_claims_with_values()
{
- $this->claimFactory->shouldReceive('get')->twice()->with('sub', 1)->andReturn(new Subject(1));
- $this->claimFactory->shouldReceive('get')->twice()->with('foo', 'baz')->andReturn(new Custom('foo', 'baz'));
-
- $this->claimFactory->shouldReceive('make')->twice()->with('iss')->andReturn(new Issuer('/foo'));
- $this->claimFactory->shouldReceive('make')->twice()->with('exp')->andReturn(new Expiration($this->testNowTimestamp + 3600));
- $this->claimFactory->shouldReceive('make')->twice()->with('iat')->andReturn(new IssuedAt($this->testNowTimestamp));
- $this->claimFactory->shouldReceive('make')->twice()->with('jti')->andReturn(new JwtId('foo'));
- $this->claimFactory->shouldReceive('make')->twice()->with('nbf')->andReturn(new NotBefore($this->testNowTimestamp));
-
- $this->claimFactory->shouldReceive('getTTL')->andReturn(60);
-
- // once
- $claims = $this->factory->sub(1)->foo('baz')->buildClaimsCollection();
-
- $this->validator->shouldReceive('setRefreshFlow->check')->andReturn($claims);
-
- // twice
- $payload = $this->factory->sub(1)->foo('baz')->make();
-
- $this->assertSame($payload['sub'], 1);
- $this->assertSame($payload->get('jti'), 'foo');
- $this->assertSame($payload->get('foo'), 'baz');
+ $payload = Factory::make([
+ JwtId::NAME => 'foo',
+ IssuedAt::NAME => $this->testNowTimestamp - 3600,
+ Issuer::NAME => 'example.com',
+ Subject::NAME => 1,
+ 'foo' => 'bar',
+ ]);
+
+ $this->assertSame($payload->get(Subject::NAME), 1);
+ $this->assertSame($payload->get(JwtId::NAME), 'foo');
+ $this->assertSame($payload(IssuedAt::NAME), $this->testNowTimestamp - 3600);
+ $this->assertSame($payload['foo'], 'bar');
+ $this->assertSame($payload[Issuer::NAME], 'example.com');
$this->assertInstanceOf(Payload::class, $payload);
+ $this->assertInstanceOf(Subject::class, $payload->getInternal(Subject::NAME));
+ $this->assertInstanceOf(IssuedAt::class, $payload->getInternal(IssuedAt::NAME));
+ $this->assertInstanceOf(JwtId::class, $payload->getInternal(JwtId::NAME));
+ $this->assertInstanceOf(Issuer::class, $payload->getInternal(Issuer::NAME));
+ $this->assertInstanceOf(Custom::class, $payload->getInternal('foo'));
}
/** @test */
- public function it_should_return_a_payload_when_passing_miltidimensional_array_as_custom_claim_to_make_method()
+ public function it_should_run_a_custom_validator_and_throw_exception()
{
- // these are added from default claims
- $this->claimFactory->shouldReceive('make')->twice()->with('iss')->andReturn(new Issuer('/foo'));
- $this->claimFactory->shouldReceive('make')->twice()->with('exp')->andReturn(new Expiration($this->testNowTimestamp + 3600));
- $this->claimFactory->shouldReceive('make')->twice()->with('jti')->andReturn(new JwtId('foo'));
- $this->claimFactory->shouldReceive('make')->twice()->with('nbf')->andReturn(new NotBefore(123));
- $this->claimFactory->shouldReceive('make')->twice()->with('iat')->andReturn(new IssuedAt(123));
-
- // custom claims that override
- $this->claimFactory->shouldReceive('get')->twice()->with('sub', 1)->andReturn(new Subject(1));
- $this->claimFactory->shouldReceive('get')->twice()->with('foo', ['bar' => [0, 0, 0]])->andReturn(new Custom('foo', ['bar' => [0, 0, 0]]));
-
- $this->claimFactory->shouldReceive('getTTL')->andReturn(60);
-
- // once
- $claims = $this->factory->sub(1)->foo(['bar' => [0, 0, 0]])->buildClaimsCollection();
-
- $this->validator->shouldReceive('setRefreshFlow->check')->andReturn($claims);
-
- // twice
- $payload = $this->factory->sub(1)->foo(['bar' => [0, 0, 0]])->make();
-
- $this->assertSame($payload->get('sub'), 1);
- $this->assertSame($payload->get('jti'), 'foo');
- $this->assertSame($payload->get('foo'), ['bar' => [0, 0, 0]]);
- $this->assertSame($payload->get('foo.bar'), [0, 0, 0]);
-
- $this->assertInstanceOf(Payload::class, $payload);
- }
-
- /** @test */
- public function it_should_exclude_the_exp_claim_when_setting_ttl_to_null()
- {
- // these are added from default claims
- $this->claimFactory->shouldReceive('make')->twice()->with('iss')->andReturn(new Issuer('/foo'));
- $this->claimFactory->shouldReceive('make')->twice()->with('jti')->andReturn(new JwtId('foo'));
- $this->claimFactory->shouldReceive('make')->twice()->with('nbf')->andReturn(new NotBefore(123));
- $this->claimFactory->shouldReceive('make')->twice()->with('iat')->andReturn(new IssuedAt(123));
-
- // custom claims that override
- $this->claimFactory->shouldReceive('get')->twice()->with('sub', 1)->andReturn(new Subject(1));
-
- $this->claimFactory->shouldReceive('setTTL')->with(null)->andReturn($this->claimFactory);
- $this->claimFactory->shouldReceive('getTTL')->andReturn(null);
-
- // once
- $claims = $this->factory->setTTL(null)->sub(1)->buildClaimsCollection();
-
- $this->validator->shouldReceive('setRefreshFlow->check')->andReturn($claims);
-
- // twice
- $payload = $this->factory->setTTL(null)->sub(1)->make();
-
- $this->assertNull($payload->get('exp'));
-
- $this->assertInstanceOf(Payload::class, $payload);
- }
-
- /** @test */
- public function it_should_exclude_claims_from_previous_payloads()
- {
- $fooClaim = new Custom('foo', 'bar');
- $barClaim = new Custom('baz', 'qux');
-
- $this->claimFactory->shouldReceive('getTTL')->andReturn(60);
- $this->claimFactory->shouldReceive('get')->with('foo', 'bar')->twice()->andReturn($fooClaim);
- $this->claimFactory->shouldReceive('get')->with('baz', 'qux')->twice()->andReturn($barClaim);
- $this->validator->shouldReceive('setRefreshFlow->check')->once()->andReturn(new Collection([$fooClaim, $barClaim]));
-
- $payload = $this->factory->setDefaultClaims([])
- ->customClaims([
- 'foo' => 'bar',
- 'baz' => 'qux',
- ])->make();
-
- $this->assertSame($payload->get('foo'), 'bar');
- $this->assertSame($payload->get('baz'), 'qux');
-
- $this->validator->shouldReceive('setRefreshFlow->check')->once()->andReturn(new Collection([$fooClaim]));
-
- $payload = $this->factory->setDefaultClaims([])->customClaims(['foo' => 'bar'])->make(true);
-
- $this->assertSame($payload->get('foo'), 'bar');
- $this->assertFalse($payload->hasKey('baz'));
- }
-
- /** @test */
- public function it_should_set_the_default_claims()
- {
- $this->factory->setDefaultClaims(['sub', 'iat']);
-
- $this->assertSame($this->factory->getDefaultClaims(), ['sub', 'iat']);
- }
-
- /** @test */
- public function it_should_get_payload_with_a_predefined_collection_of_claims()
- {
- $claims = [
- new Subject(1),
- new Issuer('http://example.com'),
- new Expiration($this->testNowTimestamp + 3600),
- new NotBefore($this->testNowTimestamp),
- new IssuedAt($this->testNowTimestamp),
- new JwtId('foo'),
- ];
-
- $collection = Collection::make($claims);
- $this->validator->shouldReceive('setRefreshFlow->check')->andReturn($collection);
-
- $payload = $this->factory->withClaims($collection);
-
- $this->assertInstanceOf(Payload::class, $payload);
- $this->assertSame($payload->get('sub'), 1);
+ $this->expectException(TokenInvalidException::class);
+ $this->expectExceptionMessage('Validation failed for claim [foo]');
+
+ Factory::make([
+ JwtId::NAME => 'foo',
+ IssuedAt::NAME => $this->testNowTimestamp - 3600,
+ Issuer::NAME => 'example.com',
+ Subject::NAME => 1,
+ 'foo' => 'bar',
+ ], new Options([
+ 'validators' => [
+ // This will fail as the value is `bar`
+ 'foo' => fn ($value) => $value === 'baz',
+ ],
+ ]));
}
/** @test */
- public function it_should_get_the_validator()
+ public function it_should_not_run_a_custom_validator_for_a_non_existent_claim()
{
- $this->assertInstanceOf(PayloadValidator::class, $this->factory->validator());
+ Factory::make([
+ JwtId::NAME => 'foo',
+ IssuedAt::NAME => $this->testNowTimestamp - 3600,
+ Issuer::NAME => 'example.com',
+ Subject::NAME => 1,
+ 'foo' => 'bar',
+ ], new Options([
+ 'validators' => [
+ // The `bar` claim does not exist
+ 'bar' => fn ($value) => $value === 'baz',
+ ],
+ ]));
}
}
diff --git a/tests/Http/ParserTest.php b/tests/Http/ParserTest.php
index ffc3c41cb..3c4c05147 100644
--- a/tests/Http/ParserTest.php
+++ b/tests/Http/ParserTest.php
@@ -11,18 +11,18 @@
namespace Tymon\JWTAuth\Test\Http;
-use Mockery;
use Illuminate\Http\Request;
use Illuminate\Routing\Route;
use Illuminate\Support\Facades\Crypt;
-use Tymon\JWTAuth\Http\Parser\Parser;
-use Tymon\JWTAuth\Http\Parser\Cookies;
-use Tymon\JWTAuth\Test\AbstractTestCase;
+use Mockery;
use Tymon\JWTAuth\Http\Parser\AuthHeaders;
+use Tymon\JWTAuth\Http\Parser\Cookies;
use Tymon\JWTAuth\Http\Parser\InputSource;
+use Tymon\JWTAuth\Http\Parser\LumenRouteParams;
+use Tymon\JWTAuth\Http\Parser\Parser;
use Tymon\JWTAuth\Http\Parser\QueryString;
use Tymon\JWTAuth\Http\Parser\RouteParams;
-use Tymon\JWTAuth\Http\Parser\LumenRouteParams;
+use Tymon\JWTAuth\Test\AbstractTestCase;
class ParserTest extends AbstractTestCase
{
@@ -35,10 +35,10 @@ public function it_should_return_the_token_from_the_authorization_header()
$parser = new Parser($request);
$parser->setChain([
- new QueryString,
- new InputSource,
- new AuthHeaders,
- new RouteParams,
+ 'query' => new QueryString(),
+ 'input' => new InputSource(),
+ 'header' => new AuthHeaders(),
+ 'route' => new RouteParams(),
]);
$this->assertSame($parser->parseToken(), 'foobar');
@@ -54,12 +54,14 @@ public function it_should_return_the_token_from_the_prefixed_authentication_head
$parser = new Parser($request);
$parser->setChain([
- new QueryString,
- new InputSource,
- (new AuthHeaders)->setHeaderPrefix('Custom'),
- new RouteParams,
+ 'query' => new QueryString(),
+ 'input' => new InputSource(),
+ 'header' => new AuthHeaders(),
+ 'route' => new RouteParams(),
]);
+ $parser->get('header')->setHeaderPrefix('Custom');
+
$this->assertSame($parser->parseToken(), 'foobar');
$this->assertTrue($parser->hasToken());
}
@@ -73,10 +75,10 @@ public function it_should_return_the_token_from_the_custom_authentication_header
$parser = new Parser($request);
$parser->setChain([
- new QueryString,
- new InputSource,
- (new AuthHeaders)->setHeaderName('custom_authorization'),
- new RouteParams,
+ 'query' => new QueryString(),
+ 'input' => new InputSource(),
+ 'header' => (new AuthHeaders())->setHeaderName('custom_authorization'),
+ 'route' => new RouteParams(),
]);
$this->assertSame($parser->parseToken(), 'foobar');
@@ -93,10 +95,10 @@ public function it_should_return_the_token_from_the_alt_authorization_headers()
$request2->server->set('REDIRECT_HTTP_AUTHORIZATION', 'Bearer foobarbaz');
$parser = new Parser($request1, [
- new AuthHeaders,
- new QueryString,
- new InputSource,
- new RouteParams,
+ 'header' => new AuthHeaders(),
+ 'query' => new QueryString(),
+ 'input' => new InputSource(),
+ 'route' => new RouteParams(),
]);
$this->assertSame($parser->parseToken(), 'foobar');
@@ -114,10 +116,10 @@ public function it_should_return_the_token_from_query_string()
$parser = new Parser($request);
$parser->setChain([
- new AuthHeaders,
- new QueryString,
- new InputSource,
- new RouteParams,
+ 'header' => new AuthHeaders(),
+ 'query' => new QueryString(),
+ 'input' => new InputSource(),
+ 'route' => new RouteParams(),
]);
$this->assertSame($parser->parseToken(), 'foobar');
@@ -131,10 +133,10 @@ public function it_should_return_the_token_from_the_custom_query_string()
$parser = new Parser($request);
$parser->setChain([
- new AuthHeaders,
- (new QueryString)->setKey('custom_token_key'),
- new InputSource,
- new RouteParams,
+ 'header' => new AuthHeaders(),
+ 'query' => (new QueryString())->setKey('custom_token_key'),
+ 'input' => new InputSource(),
+ 'route' => new RouteParams(),
]);
$this->assertSame($parser->parseToken(), 'foobar');
@@ -144,14 +146,22 @@ public function it_should_return_the_token_from_the_custom_query_string()
/** @test */
public function it_should_return_the_token_from_the_query_string_not_the_input_source()
{
- $request = Request::create('foo?token=foobar', 'POST', [], [], [], [], json_encode(['token' => 'foobarbaz']));
+ $request = Request::create(
+ 'foo?token=foobar',
+ 'POST',
+ [],
+ [],
+ [],
+ [],
+ json_encode(['token' => 'foobarbaz'])
+ );
$parser = new Parser($request);
$parser->setChain([
- new AuthHeaders,
- new QueryString,
- new InputSource,
- new RouteParams,
+ 'header' => new AuthHeaders(),
+ 'query' => new QueryString(),
+ 'input' => new InputSource(),
+ 'route' => new RouteParams(),
]);
$this->assertSame($parser->parseToken(), 'foobar');
@@ -161,14 +171,22 @@ public function it_should_return_the_token_from_the_query_string_not_the_input_s
/** @test */
public function it_should_return_the_token_from_the_custom_query_string_not_the_custom_input_source()
{
- $request = Request::create('foo?custom_token_key=foobar', 'POST', [], [], [], [], json_encode(['custom_token_key' => 'foobarbaz']));
+ $request = Request::create(
+ 'foo?custom_token_key=foobar',
+ 'POST',
+ [],
+ [],
+ [],
+ [],
+ json_encode(['custom_token_key' => 'foobarbaz'])
+ );
$parser = new Parser($request);
$parser->setChain([
- new AuthHeaders,
- (new QueryString)->setKey('custom_token_key'),
- (new InputSource)->setKey('custom_token_key'),
- new RouteParams,
+ 'header' => new AuthHeaders(),
+ 'query' => (new QueryString())->setKey('custom_token_key'),
+ 'input' => (new InputSource())->setKey('custom_token_key'),
+ 'route' => new RouteParams(),
]);
$this->assertSame($parser->parseToken(), 'foobar');
@@ -178,15 +196,23 @@ public function it_should_return_the_token_from_the_custom_query_string_not_the_
/** @test */
public function it_should_return_the_token_from_input_source()
{
- $request = Request::create('foo', 'POST', [], [], [], [], json_encode(['token' => 'foobar']));
+ $request = Request::create(
+ 'foo',
+ 'POST',
+ [],
+ [],
+ [],
+ [],
+ json_encode(['token' => 'foobar'])
+ );
$request->headers->set('Content-Type', 'application/json');
$parser = new Parser($request);
$parser->setChain([
- new AuthHeaders,
- new QueryString,
- new InputSource,
- new RouteParams,
+ 'header' => new AuthHeaders(),
+ 'query' => new QueryString(),
+ 'input' => new InputSource(),
+ 'route' => new RouteParams(),
]);
$this->assertSame($parser->parseToken(), 'foobar');
@@ -196,15 +222,23 @@ public function it_should_return_the_token_from_input_source()
/** @test */
public function it_should_return_the_token_from_the_custom_input_source()
{
- $request = Request::create('foo', 'POST', [], [], [], [], json_encode(['custom_token_key' => 'foobar']));
+ $request = Request::create(
+ 'foo',
+ 'POST',
+ [],
+ [],
+ [],
+ [],
+ json_encode(['custom_token_key' => 'foobar'])
+ );
$request->headers->set('Content-Type', 'application/json');
$parser = new Parser($request);
$parser->setChain([
- new AuthHeaders,
- new QueryString,
- (new InputSource)->setKey('custom_token_key'),
- new RouteParams,
+ 'header' => new AuthHeaders(),
+ 'query' => new QueryString(),
+ 'input' => (new InputSource())->setKey('custom_token_key'),
+ 'route' => new RouteParams(),
]);
$this->assertSame($parser->parseToken(), 'foobar');
@@ -218,10 +252,10 @@ public function it_should_return_the_token_from_an_unencrypted_cookie()
$parser = new Parser($request);
$parser->setChain([
- new AuthHeaders,
- new QueryString,
- new InputSource,
- new RouteParams,
+ 'header' => new AuthHeaders(),
+ 'query' => new QueryString(),
+ 'input' => new InputSource(),
+ 'route' => new RouteParams(),
new Cookies(false),
]);
@@ -241,10 +275,10 @@ public function it_should_return_the_token_from_a_crypted_cookie()
$parser = new Parser($request);
$parser->setChain([
- new AuthHeaders,
- new QueryString,
- new InputSource,
- new RouteParams,
+ 'header' => new AuthHeaders(),
+ 'query' => new QueryString(),
+ 'input' => new InputSource(),
+ 'route' => new RouteParams(),
new Cookies(true),
]);
@@ -261,16 +295,14 @@ public function it_should_return_the_token_from_a_crypted_cookie()
public function it_should_return_the_token_from_route()
{
$request = Request::create('foo', 'GET', ['foo' => 'bar']);
- $request->setRouteResolver(function () {
- return $this->getRouteMock('foobar');
- });
+ $request->setRouteResolver(fn () => $this->getRouteMock('foobar'));
$parser = new Parser($request);
$parser->setChain([
- new AuthHeaders,
- new QueryString,
- new InputSource,
- new RouteParams,
+ 'header' => new AuthHeaders(),
+ 'query' => new QueryString(),
+ 'input' => new InputSource(),
+ 'route' => new RouteParams(),
]);
$this->assertSame($parser->parseToken(), 'foobar');
@@ -281,16 +313,14 @@ public function it_should_return_the_token_from_route()
public function it_should_return_the_token_from_route_with_a_custom_param()
{
$request = Request::create('foo', 'GET', ['foo' => 'bar']);
- $request->setRouteResolver(function () {
- return $this->getRouteMock('foobar', 'custom_route_param');
- });
+ $request->setRouteResolver(fn () => $this->getRouteMock('foobar', 'custom_route_param'));
$parser = new Parser($request);
$parser->setChain([
- new AuthHeaders,
- new QueryString,
- new InputSource,
- (new RouteParams)->setKey('custom_route_param'),
+ 'header' => new AuthHeaders(),
+ 'query' => new QueryString(),
+ 'input' => new InputSource(),
+ 'route' => (new RouteParams())->setKey('custom_route_param'),
]);
$this->assertSame($parser->parseToken(), 'foobar');
@@ -307,10 +337,10 @@ public function it_should_ignore_routeless_requests()
$parser = new Parser($request);
$parser->setChain([
- new AuthHeaders,
- new QueryString,
- new InputSource,
- new RouteParams,
+ 'header' => new AuthHeaders(),
+ 'query' => new QueryString(),
+ 'input' => new InputSource(),
+ 'route' => new RouteParams(),
]);
$this->assertNull($parser->parseToken());
@@ -321,16 +351,18 @@ public function it_should_ignore_routeless_requests()
public function it_should_ignore_lumen_request_arrays()
{
$request = Request::create('foo', 'GET', ['foo' => 'bar']);
- $request->setRouteResolver(function () {
- return [false, ['uses' => 'someController'], ['token' => 'foobar']];
- });
+ $request->setRouteResolver(fn () => [
+ false,
+ ['uses' => 'someController'],
+ ['token' => 'foobar'],
+ ]);
$parser = new Parser($request);
$parser->setChain([
- new AuthHeaders,
- new QueryString,
- new InputSource,
- new RouteParams,
+ 'header' => new AuthHeaders(),
+ 'query' => new QueryString(),
+ 'input' => new InputSource(),
+ 'route' => new RouteParams(),
]);
$this->assertNull($parser->parseToken());
@@ -341,16 +373,18 @@ public function it_should_ignore_lumen_request_arrays()
public function it_should_accept_lumen_request_arrays_with_special_class()
{
$request = Request::create('foo', 'GET', ['foo' => 'bar']);
- $request->setRouteResolver(function () {
- return [false, ['uses' => 'someController'], ['token' => 'foo.bar.baz']];
- });
+ $request->setRouteResolver(fn () => [
+ false,
+ ['uses' => 'someController'],
+ ['token' => 'foo.bar.baz'],
+ ]);
$parser = new Parser($request);
$parser->setChain([
- new AuthHeaders,
- new QueryString,
- new InputSource,
- new LumenRouteParams,
+ 'header' => new AuthHeaders(),
+ 'query' => new QueryString(),
+ 'input' => new InputSource(),
+ new LumenRouteParams(),
]);
$this->assertSame($parser->parseToken(), 'foo.bar.baz');
@@ -361,16 +395,14 @@ public function it_should_accept_lumen_request_arrays_with_special_class()
public function it_should_return_null_if_no_token_in_request()
{
$request = Request::create('foo', 'GET', ['foo' => 'bar']);
- $request->setRouteResolver(function () {
- return $this->getRouteMock();
- });
+ $request->setRouteResolver(fn () => $this->getRouteMock());
$parser = new Parser($request);
$parser->setChain([
- new AuthHeaders,
- new QueryString,
- new InputSource,
- new RouteParams,
+ 'header' => new AuthHeaders(),
+ 'query' => new QueryString(),
+ 'input' => new InputSource(),
+ 'route' => new RouteParams(),
]);
$this->assertNull($parser->parseToken());
@@ -381,10 +413,10 @@ public function it_should_return_null_if_no_token_in_request()
public function it_should_retrieve_the_chain()
{
$chain = [
- new AuthHeaders,
- new QueryString,
- new InputSource,
- new RouteParams,
+ 'header' => new AuthHeaders(),
+ 'query' => new QueryString(),
+ 'input' => new InputSource(),
+ 'route' => new RouteParams(),
];
$parser = new Parser(Mockery::mock(Request::class));
@@ -397,10 +429,10 @@ public function it_should_retrieve_the_chain()
public function it_should_retrieve_the_chain_with_alias()
{
$chain = [
- new AuthHeaders,
- new QueryString,
- new InputSource,
- new RouteParams,
+ 'header' => new AuthHeaders(),
+ 'query' => new QueryString(),
+ 'input' => new InputSource(),
+ 'route' => new RouteParams(),
];
/* @var \Illuminate\Http\Request $request */
@@ -415,12 +447,14 @@ public function it_should_retrieve_the_chain_with_alias()
/** @test */
public function it_should_set_the_cookie_key()
{
- $cookies = (new Cookies)->setKey('test');
+ $cookies = (new Cookies())->setKey('test');
$this->assertInstanceOf(Cookies::class, $cookies);
}
- protected function getRouteMock($expectedParameterValue = null, $expectedParameterName = 'token')
- {
+ protected function getRouteMock(
+ $expectedParameterValue = null,
+ $expectedParameterName = 'token'
+ ) {
return Mockery::mock(Route::class)
->shouldReceive('parameter')
->with($expectedParameterName)
diff --git a/tests/JWTAuthTest.php b/tests/JWTAuthTest.php
deleted file mode 100644
index c7c2114cf..000000000
--- a/tests/JWTAuthTest.php
+++ /dev/null
@@ -1,343 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Tymon\JWTAuth\Test;
-
-use Mockery;
-use stdClass;
-use Tymon\JWTAuth\Token;
-use Tymon\JWTAuth\Factory;
-use Tymon\JWTAuth\JWTAuth;
-use Tymon\JWTAuth\Manager;
-use Tymon\JWTAuth\Payload;
-use Illuminate\Http\Request;
-use Tymon\JWTAuth\Http\Parser\Parser;
-use Tymon\JWTAuth\Test\Stubs\UserStub;
-use Tymon\JWTAuth\Exceptions\JWTException;
-use Tymon\JWTAuth\Contracts\Providers\Auth;
-use Tymon\JWTAuth\Exceptions\TokenInvalidException;
-
-class JWTAuthTest extends AbstractTestCase
-{
- /**
- * @var \Mockery\MockInterface|\Tymon\JWTAuth\Manager
- */
- protected $manager;
-
- /**
- * @var \Mockery\MockInterface|\Tymon\JWTAuth\Contracts\Providers\Auth
- */
- protected $auth;
-
- /**
- * @var \Mockery\MockInterface|\Tymon\JWTAuth\Http\Parser\Parser
- */
- protected $parser;
-
- /**
- * @var \Tymon\JWTAuth\JWTAuth
- */
- protected $jwtAuth;
-
- public function setUp()
- {
- $this->manager = Mockery::mock(Manager::class);
- $this->auth = Mockery::mock(Auth::class);
- $this->parser = Mockery::mock(Parser::class);
- $this->jwtAuth = new JWTAuth($this->manager, $this->auth, $this->parser);
- }
-
- /** @test */
- public function it_should_return_a_token_when_passing_a_user()
- {
- $payloadFactory = Mockery::mock(Factory::class);
- $payloadFactory->shouldReceive('make')->andReturn(Mockery::mock(Payload::class));
-
- $this->manager
- ->shouldReceive('getPayloadFactory->customClaims')
- ->once()
- ->with(['sub' => 1, 'prv' => sha1('Tymon\JWTAuth\Test\Stubs\UserStub'), 'foo' => 'bar', 'role' => 'admin'])
- ->andReturn($payloadFactory);
-
- $this->manager->shouldReceive('encode->get')->once()->andReturn('foo.bar.baz');
-
- $token = $this->jwtAuth->fromUser(new UserStub);
-
- $this->assertSame($token, 'foo.bar.baz');
- }
-
- /** @test */
- public function it_should_pass_provider_check_if_hash_matches()
- {
- $payloadFactory = Mockery::mock(Factory::class);
- $payloadFactory->shouldReceive('make')->andReturn(Mockery::mock(Payload::class));
- $payloadFactory->shouldReceive('get')
- ->with('prv')
- ->andReturn(sha1('Tymon\JWTAuth\Test\Stubs\UserStub'));
-
- $this->manager->shouldReceive('decode')->once()->andReturn($payloadFactory);
-
- $this->assertTrue($this->jwtAuth->setToken('foo.bar.baz')->checkSubjectModel('Tymon\JWTAuth\Test\Stubs\UserStub'));
- }
-
- /** @test */
- public function it_should_pass_provider_check_if_hash_matches_when_provider_is_null()
- {
- $payloadFactory = Mockery::mock(Factory::class);
- $payloadFactory->shouldReceive('make')->andReturn(Mockery::mock(Payload::class));
- $payloadFactory->shouldReceive('get')
- ->with('prv')
- ->andReturnNull();
-
- $this->manager->shouldReceive('decode')->once()->andReturn($payloadFactory);
-
- $this->assertTrue($this->jwtAuth->setToken('foo.bar.baz')->checkSubjectModel('Tymon\JWTAuth\Test\Stubs\UserStub'));
- }
-
- /** @test */
- public function it_should_not_pass_provider_check_if_hash_not_match()
- {
- $payloadFactory = Mockery::mock(Factory::class);
- $payloadFactory->shouldReceive('make')->andReturn(Mockery::mock(Payload::class));
- $payloadFactory->shouldReceive('get')
- ->with('prv')
- ->andReturn(sha1('Tymon\JWTAuth\Test\Stubs\UserStub1'));
-
- $this->manager->shouldReceive('decode')->once()->andReturn($payloadFactory);
-
- $this->assertFalse($this->jwtAuth->setToken('foo.bar.baz')->checkSubjectModel('Tymon\JWTAuth\Test\Stubs\UserStub'));
- }
-
- /** @test */
- public function it_should_return_a_token_when_passing_valid_credentials_to_attempt_method()
- {
- $payloadFactory = Mockery::mock(Factory::class);
- $payloadFactory->shouldReceive('make')->andReturn(Mockery::mock(Payload::class));
-
- $this->manager
- ->shouldReceive('getPayloadFactory->customClaims')
- ->once()
- ->with(['sub' => 1, 'prv' => sha1('Tymon\JWTAuth\Test\Stubs\UserStub'), 'foo' => 'bar', 'role' => 'admin'])
- ->andReturn($payloadFactory);
-
- $this->manager->shouldReceive('encode->get')->once()->andReturn('foo.bar.baz');
-
- $this->auth->shouldReceive('byCredentials')->once()->andReturn(true);
- $this->auth->shouldReceive('user')->once()->andReturn(new UserStub);
-
- $token = $this->jwtAuth->attempt(['foo' => 'bar']);
-
- $this->assertSame($token, 'foo.bar.baz');
- }
-
- /** @test */
- public function it_should_return_false_when_passing_invalid_credentials_to_attempt_method()
- {
- $this->manager->shouldReceive('encode->get')->never();
- $this->auth->shouldReceive('byCredentials')->once()->andReturn(false);
- $this->auth->shouldReceive('user')->never();
-
- $token = $this->jwtAuth->attempt(['foo' => 'bar']);
-
- $this->assertFalse($token);
- }
-
- /**
- * @test
- * @expectedException \Tymon\JWTAuth\Exceptions\JWTException
- * @expectedExceptionMessage A token is required
- */
- public function it_should_throw_an_exception_when_not_providing_a_token()
- {
- $this->jwtAuth->toUser();
- }
-
- /** @test */
- public function it_should_return_the_owning_user_from_a_token_containing_an_existing_user()
- {
- $payload = Mockery::mock(Payload::class);
- $payload->shouldReceive('get')->once()->with('sub')->andReturn(1);
-
- $this->manager->shouldReceive('decode')->once()->andReturn($payload);
-
- $this->auth->shouldReceive('byId')->once()->with(1)->andReturn(true);
- $this->auth->shouldReceive('user')->once()->andReturn((object) ['id' => 1]);
-
- $user = $this->jwtAuth->setToken('foo.bar.baz')->customClaims(['foo' => 'bar'])->authenticate();
-
- $this->assertSame($user->id, 1);
- }
-
- /** @test */
- public function it_should_return_false_when_passing_a_token_not_containing_an_existing_user()
- {
- $payload = Mockery::mock(Payload::class);
- $payload->shouldReceive('get')->once()->with('sub')->andReturn(1);
-
- $this->manager->shouldReceive('decode')->once()->andReturn($payload);
-
- $this->auth->shouldReceive('byId')->once()->with(1)->andReturn(false);
- $this->auth->shouldReceive('user')->never();
-
- $user = $this->jwtAuth->setToken('foo.bar.baz')->authenticate();
-
- $this->assertFalse($user);
- }
-
- /** @test */
- public function it_should_refresh_a_token()
- {
- $newToken = Mockery::mock(Token::class);
- $newToken->shouldReceive('get')->once()->andReturn('baz.bar.foo');
-
- $this->manager->shouldReceive('customClaims->refresh')->once()->andReturn($newToken);
-
- $result = $this->jwtAuth->setToken('foo.bar.baz')->refresh();
-
- $this->assertSame($result, 'baz.bar.foo');
- }
-
- /** @test */
- public function it_should_invalidate_a_token()
- {
- $token = new Token('foo.bar.baz');
-
- $this->manager->shouldReceive('invalidate')->once()->with($token, false)->andReturn(true);
-
- $this->jwtAuth->setToken($token)->invalidate();
- }
-
- /** @test */
- public function it_should_force_invalidate_a_token_forever()
- {
- $token = new Token('foo.bar.baz');
-
- $this->manager->shouldReceive('invalidate')->once()->with($token, true)->andReturn(true);
-
- $this->jwtAuth->setToken($token)->invalidate(true);
- }
-
- /** @test */
- public function it_should_retrieve_the_token_from_the_request()
- {
- $this->parser->shouldReceive('parseToken')->andReturn('foo.bar.baz');
-
- $this->assertInstanceOf(Token::class, $this->jwtAuth->parseToken()->getToken());
- $this->assertEquals($this->jwtAuth->getToken(), 'foo.bar.baz');
- }
-
- /** @test */
- public function it_should_get_the_authenticated_user()
- {
- $manager = $this->jwtAuth->manager();
- $this->assertInstanceOf(Manager::class, $manager);
- }
-
- /** @test */
- public function it_should_return_false_if_the_token_is_invalid()
- {
- $this->parser->shouldReceive('parseToken')->andReturn('foo.bar.baz');
- $this->manager->shouldReceive('decode')->once()->andThrow(new TokenInvalidException);
-
- $this->assertFalse($this->jwtAuth->parseToken()->check());
- }
-
- /** @test */
- public function it_should_return_true_if_the_token_is_valid()
- {
- $payload = Mockery::mock(Payload::class);
-
- $this->parser->shouldReceive('parseToken')->andReturn('foo.bar.baz');
- $this->manager->shouldReceive('decode')->once()->andReturn($payload);
-
- $this->assertTrue($this->jwtAuth->parseToken()->check());
- }
-
- /**
- * @test
- * @expectedException \Tymon\JWTAuth\Exceptions\JWTException
- * @expectedExceptionMessage The token could not be parsed from the request
- */
- public function it_should_throw_an_exception_when_token_not_present_in_request()
- {
- $this->parser->shouldReceive('parseToken')->andReturn(false);
-
- $this->jwtAuth->parseToken();
- }
-
- /** @test */
- public function it_should_return_false_when_no_token_is_set()
- {
- $this->parser->shouldReceive('parseToken')->andReturn(false);
-
- $this->assertNull($this->jwtAuth->getToken());
- }
-
- /** @test */
- public function it_should_magically_call_the_manager()
- {
- $this->manager->shouldReceive('getBlacklist')->andReturn(new stdClass);
-
- $blacklist = $this->jwtAuth->manager()->getBlacklist();
-
- $this->assertInstanceOf(stdClass::class, $blacklist);
- }
-
- /** @test */
- public function it_should_set_the_request()
- {
- $request = Request::create('/foo', 'GET', ['token' => 'some.random.token']);
-
- $this->parser->shouldReceive('setRequest')->once()->with($request);
- $this->parser->shouldReceive('parseToken')->andReturn('some.random.token');
-
- $token = $this->jwtAuth->setRequest($request)->getToken();
-
- $this->assertEquals('some.random.token', $token);
- }
-
- /** @test */
- public function it_should_unset_the_token()
- {
- $this->parser->shouldReceive('parseToken')->andThrow(new JWTException);
- $token = new Token('foo.bar.baz');
- $this->jwtAuth->setToken($token);
-
- $this->assertSame($this->jwtAuth->getToken(), $token);
- $this->jwtAuth->unsetToken();
- $this->assertNull($this->jwtAuth->getToken());
- }
-
- /** @test */
- public function it_should_get_the_manager_instance()
- {
- $manager = $this->jwtAuth->manager();
- $this->assertInstanceOf(Manager::class, $manager);
- }
-
- /** @test */
- public function it_should_get_the_parser_instance()
- {
- $parser = $this->jwtAuth->parser();
- $this->assertInstanceOf(Parser::class, $parser);
- }
-
- /** @test */
- public function it_should_get_a_claim_value()
- {
- $payload = Mockery::mock(Payload::class);
- $payload->shouldReceive('get')->once()->with('sub')->andReturn(1);
-
- $this->manager->shouldReceive('decode')->once()->andReturn($payload);
-
- $this->assertSame($this->jwtAuth->setToken('foo.bar.baz')->getClaim('sub'), 1);
- }
-}
diff --git a/tests/JWTGuardTest.php b/tests/JWTGuardTest.php
index d2fbed9d8..3a39d83c4 100644
--- a/tests/JWTGuardTest.php
+++ b/tests/JWTGuardTest.php
@@ -11,14 +11,24 @@
namespace Tymon\JWTAuth\Test;
+use Illuminate\Auth\EloquentUserProvider;
+use Illuminate\Http\Request;
+use Illuminate\Support\Testing\Fakes\EventFake;
use Mockery;
+use Tymon\JWTAuth\Builder;
+use Tymon\JWTAuth\Claims\Subject;
+use Tymon\JWTAuth\Events\JWTAttempt;
+use Tymon\JWTAuth\Events\JWTInvalidate;
+use Tymon\JWTAuth\Events\JWTLogin;
+use Tymon\JWTAuth\Events\JWTLogout;
+use Tymon\JWTAuth\Events\JWTRefresh;
+use Tymon\JWTAuth\Exceptions\JWTException;
+use Tymon\JWTAuth\Exceptions\UserNotDefinedException;
use Tymon\JWTAuth\JWT;
-use Tymon\JWTAuth\Factory;
-use Tymon\JWTAuth\Payload;
use Tymon\JWTAuth\JWTGuard;
-use Illuminate\Http\Request;
-use Illuminate\Auth\EloquentUserProvider;
+use Tymon\JWTAuth\Payload;
use Tymon\JWTAuth\Test\Stubs\LaravelUserStub;
+use Tymon\JWTAuth\Token;
class JWTGuardTest extends AbstractTestCase
{
@@ -37,48 +47,65 @@ class JWTGuardTest extends AbstractTestCase
*/
protected $guard;
- public function setUp()
+ /**
+ * @var \Illuminate\Support\Testing\Fakes\EventFake|\Mockery\MockInterface
+ */
+ protected $events;
+
+ public function setUp(): void
{
parent::setUp();
$this->jwt = Mockery::mock(JWT::class);
$this->provider = Mockery::mock(EloquentUserProvider::class);
- $this->guard = new JWTGuard($this->jwt, $this->provider, Request::create('/foo', 'GET'));
+
+ $this->events = Mockery::mock(EventFake::class);
+
+ $this->guard = new JWTGuard(
+ $this->jwt,
+ $this->provider,
+ Request::create('/foo', 'GET'),
+ $this->events
+ );
+ $this->guard->useResponsable(false);
}
- /**
- * @test
- * @group laravel-5.2
- */
+ /** @test */
public function it_should_get_the_request()
{
$this->assertInstanceOf(Request::class, $this->guard->getRequest());
}
- /**
- * @test
- * @group laravel-5.2
- */
+ /** @test */
public function it_should_get_the_authenticated_user_if_a_valid_token_is_provided()
{
- $payload = Mockery::mock(Payload::class);
- $payload->shouldReceive('offsetGet')->once()->with('sub')->andReturn(1);
+ $payload = Mockery::mock(Payload::class)
+ ->shouldReceive('offsetGet')
+ ->once()
+ ->with(Subject::NAME)
+ ->andReturn(1)
+ ->getMock();
$this->jwt->shouldReceive('setRequest')->andReturn($this->jwt);
- $this->jwt->shouldReceive('getToken')->once()->andReturn('foo.bar.baz');
- $this->jwt->shouldReceive('check')->once()->with(true)->andReturn($payload);
+ $this->jwt->shouldReceive('getToken')
+ ->once()
+ ->andReturn(new Token('foo.bar.baz'));
+ $this->jwt->shouldReceive('check')
+ ->once()
+ ->with(true)
+ ->andReturn($payload);
$this->jwt->shouldReceive('checkSubjectModel')
- ->once()
- ->with('\Tymon\JWTAuth\Test\Stubs\LaravelUserStub')
- ->andReturn(true);
+ ->once()
+ ->with(LaravelUserStub::class, $payload)
+ ->andReturn(true);
$this->provider->shouldReceive('getModel')
- ->once()
- ->andReturn('\Tymon\JWTAuth\Test\Stubs\LaravelUserStub');
+ ->once()
+ ->andReturn(LaravelUserStub::class);
$this->provider->shouldReceive('retrieveById')
- ->once()
- ->with(1)
- ->andReturn((object) ['id' => 1]);
+ ->once()
+ ->with(1)
+ ->andReturn((object) ['id' => 1]);
$this->assertSame(1, $this->guard->user()->id);
@@ -90,30 +117,36 @@ public function it_should_get_the_authenticated_user_if_a_valid_token_is_provide
$this->assertSame(1, $this->guard->userOrFail()->id);
}
- /**
- * @test
- * @group laravel-5.2
- */
+ /** @test */
public function it_should_get_the_authenticated_user_if_a_valid_token_is_provided_and_not_throw_an_exception()
{
- $payload = Mockery::mock(Payload::class);
- $payload->shouldReceive('offsetGet')->once()->with('sub')->andReturn(1);
+ $payload = Mockery::mock(Payload::class)
+ ->shouldReceive('offsetGet')
+ ->once()
+ ->with(Subject::NAME)
+ ->andReturn(1)
+ ->getMock();
$this->jwt->shouldReceive('setRequest')->andReturn($this->jwt);
- $this->jwt->shouldReceive('getToken')->once()->andReturn('foo.bar.baz');
- $this->jwt->shouldReceive('check')->once()->with(true)->andReturn($payload);
+ $this->jwt->shouldReceive('getToken')
+ ->once()
+ ->andReturn(new Token('foo.bar.baz'));
+ $this->jwt->shouldReceive('check')
+ ->once()
+ ->with(true)
+ ->andReturn($payload);
$this->jwt->shouldReceive('checkSubjectModel')
- ->once()
- ->with('\Tymon\JWTAuth\Test\Stubs\LaravelUserStub')
- ->andReturn(true);
+ ->once()
+ ->with('\Tymon\JWTAuth\Test\Stubs\LaravelUserStub', $payload)
+ ->andReturn(true);
$this->provider->shouldReceive('getModel')
- ->once()
- ->andReturn('\Tymon\JWTAuth\Test\Stubs\LaravelUserStub');
+ ->once()
+ ->andReturn('\Tymon\JWTAuth\Test\Stubs\LaravelUserStub');
$this->provider->shouldReceive('retrieveById')
- ->once()
- ->with(1)
- ->andReturn((object) ['id' => 1]);
+ ->once()
+ ->with(1)
+ ->andReturn((object) ['id' => 1]);
$this->assertSame(1, $this->guard->userOrFail()->id);
@@ -122,15 +155,16 @@ public function it_should_get_the_authenticated_user_if_a_valid_token_is_provide
$this->assertTrue($this->guard->check());
}
- /**
- * @test
- * @group laravel-5.2
- */
+ /** @test */
public function it_should_return_null_if_an_invalid_token_is_provided()
{
$this->jwt->shouldReceive('setRequest')->andReturn($this->jwt);
- $this->jwt->shouldReceive('getToken')->twice()->andReturn('invalid.token.here');
- $this->jwt->shouldReceive('check')->twice()->andReturn(false);
+ $this->jwt->shouldReceive('getToken')
+ ->twice()
+ ->andReturn(new Token('invalid.token.here'));
+ $this->jwt->shouldReceive('check')
+ ->twice()
+ ->andReturn(false);
$this->jwt->shouldReceive('getPayload->get')->never();
$this->provider->shouldReceive('retrieveById')->never();
@@ -138,14 +172,11 @@ public function it_should_return_null_if_an_invalid_token_is_provided()
$this->assertFalse($this->guard->check()); // twice
}
- /**
- * @test
- * @group laravel-5.2
- */
+ /** @test */
public function it_should_return_null_if_no_token_is_provided()
{
$this->jwt->shouldReceive('setRequest')->andReturn($this->jwt);
- $this->jwt->shouldReceive('getToken')->andReturn(false);
+ $this->jwt->shouldReceive('getToken')->andReturn(null);
$this->jwt->shouldReceive('check')->never();
$this->jwt->shouldReceive('getPayload->get')->never();
$this->provider->shouldReceive('retrieveById')->never();
@@ -154,17 +185,19 @@ public function it_should_return_null_if_no_token_is_provided()
$this->assertFalse($this->guard->check());
}
- /**
- * @test
- * @group laravel-5.2
- * @expectedException \Tymon\JWTAuth\Exceptions\UserNotDefinedException
- * @expectedExceptionMessage An error occurred
- */
+ /** @test */
public function it_should_throw_an_exception_if_an_invalid_token_is_provided()
{
+ $this->expectException(UserNotDefinedException::class);
+ $this->expectExceptionMessage('User not defined');
+
$this->jwt->shouldReceive('setRequest')->andReturn($this->jwt);
- $this->jwt->shouldReceive('getToken')->twice()->andReturn('invalid.token.here');
- $this->jwt->shouldReceive('check')->twice()->andReturn(false);
+ $this->jwt->shouldReceive('getToken')
+ ->twice()
+ ->andReturn(new Token('invalid.token.here'));
+ $this->jwt->shouldReceive('check')
+ ->twice()
+ ->andReturn(false);
$this->jwt->shouldReceive('getPayload->get')->never();
$this->provider->shouldReceive('retrieveById')->never();
@@ -172,16 +205,14 @@ public function it_should_throw_an_exception_if_an_invalid_token_is_provided()
$this->guard->userOrFail(); // twice, throws the exception
}
- /**
- * @test
- * @group laravel-5.2
- * @expectedException \Tymon\JWTAuth\Exceptions\UserNotDefinedException
- * @expectedExceptionMessage An error occurred
- */
+ /** @test */
public function it_should_throw_an_exception_if_no_token_is_provided()
{
+ $this->expectException(UserNotDefinedException::class);
+ $this->expectExceptionMessage('User not defined');
+
$this->jwt->shouldReceive('setRequest')->andReturn($this->jwt);
- $this->jwt->shouldReceive('getToken')->andReturn(false);
+ $this->jwt->shouldReceive('getToken')->andReturn(null);
$this->jwt->shouldReceive('check')->never();
$this->jwt->shouldReceive('getPayload->get')->never();
$this->provider->shouldReceive('retrieveById')->never();
@@ -190,300 +221,321 @@ public function it_should_throw_an_exception_if_no_token_is_provided()
$this->guard->userOrFail(); // throws the exception
}
- /**
- * @test
- * @group laravel-5.2
- */
+ /** @test */
public function it_should_return_a_token_if_credentials_are_ok_and_user_is_found()
{
$credentials = ['foo' => 'bar', 'baz' => 'bob'];
- $user = new LaravelUserStub;
+ $user = new LaravelUserStub();
+
+ $this->events->shouldReceive('dispatch')->times(2);
+ $this->events->shouldReceive('assertDispatched')->times(2);
$this->provider->shouldReceive('retrieveByCredentials')
- ->once()
- ->with($credentials)
- ->andReturn($user);
+ ->once()
+ ->with($credentials)
+ ->andReturn($user);
$this->provider->shouldReceive('validateCredentials')
- ->once()
- ->with($user, $credentials)
- ->andReturn(true);
+ ->once()
+ ->with($user, $credentials)
+ ->andReturn(true);
$this->jwt->shouldReceive('fromUser')
- ->once()
- ->with($user)
- ->andReturn('foo.bar.baz');
+ ->once()
+ ->with($user)
+ ->andReturn($token = new Token('foo.bar.baz'));
$this->jwt->shouldReceive('setToken')
- ->once()
- ->with('foo.bar.baz')
- ->andReturnSelf();
+ ->once()
+ ->with($token)
+ ->andReturnSelf();
$this->jwt->shouldReceive('claims')
- ->once()
- ->with(['foo' => 'bar'])
- ->andReturnSelf();
+ ->once()
+ ->with(['foo' => 'bar'])
+ ->andReturnSelf();
- $token = $this->guard->claims(['foo' => 'bar'])->attempt($credentials);
+ $jwt = $this->guard->claims(['foo' => 'bar'])->attempt($credentials);
+ $this->events->assertDispatched(JWTAttempt::class, 1);
+ $this->events->assertDispatched(JWTLogin::class, 1);
$this->assertSame($this->guard->getLastAttempted(), $user);
- $this->assertSame($token, 'foo.bar.baz');
+ $this->assertTrue($jwt->matches($token));
+ $this->assertSame((string) $jwt, 'foo.bar.baz');
}
- /**
- * @test
- * @group laravel-5.2
- */
+ /** @test */
public function it_should_return_true_if_credentials_are_ok_and_user_is_found_when_choosing_not_to_login()
{
$credentials = ['foo' => 'bar', 'baz' => 'bob'];
- $user = new LaravelUserStub;
+ $user = new LaravelUserStub();
+
+ $this->events->shouldReceive('dispatch')->times(2);
+ $this->events->shouldReceive('assertDispatched')->once();
$this->provider->shouldReceive('retrieveByCredentials')
- ->twice()
- ->with($credentials)
- ->andReturn($user);
+ ->twice()
+ ->with($credentials)
+ ->andReturn($user);
$this->provider->shouldReceive('validateCredentials')
- ->twice()
- ->with($user, $credentials)
- ->andReturn(true);
+ ->twice()
+ ->with($user, $credentials)
+ ->andReturn(true);
$this->assertTrue($this->guard->attempt($credentials, false)); // once
$this->assertTrue($this->guard->validate($credentials)); // twice
+ $this->events->assertDispatched(JWTAttempt::class, 2);
}
- /**
- * @test
- * @group laravel-5.2
- */
+ /** @test */
public function it_should_return_false_if_credentials_are_invalid()
{
$credentials = ['foo' => 'bar', 'baz' => 'bob'];
- $user = new LaravelUserStub;
+ $user = new LaravelUserStub();
+
+ $this->events->shouldReceive('dispatch')->once();
+ $this->events->shouldReceive('assertDispatched')->once();
$this->provider->shouldReceive('retrieveByCredentials')
- ->once()
- ->with($credentials)
- ->andReturn($user);
+ ->once()
+ ->with($credentials)
+ ->andReturn($user);
$this->provider->shouldReceive('validateCredentials')
- ->once()
- ->with($user, $credentials)
- ->andReturn(false);
+ ->once()
+ ->with($user, $credentials)
+ ->andReturn(false);
$this->assertFalse($this->guard->attempt($credentials));
+ $this->events->assertDispatched(JWTAttempt::class, 1);
}
- /**
- * @test
- * @group laravel-5.2
- */
+ /** @test */
public function it_should_magically_call_the_jwt_instance()
{
- $this->jwt->shouldReceive('factory')->andReturn(Mockery::mock(Factory::class));
- $this->assertInstanceOf(Factory::class, $this->guard->factory());
+ $this->jwt->shouldReceive('builder')->andReturn(Mockery::mock(Builder::class));
+ $this->assertInstanceOf(Builder::class, $this->guard->builder());
}
- /**
- * @test
- * @group laravel-5.2
- */
+ /** @test */
public function it_should_logout_the_user_by_invalidating_the_token()
{
$this->jwt->shouldReceive('setRequest')->andReturn($this->jwt);
- $this->jwt->shouldReceive('getToken')->once()->andReturn(true);
- $this->jwt->shouldReceive('invalidate')->once()->andReturn(true);
+ $this->jwt->shouldReceive('getToken')
+ ->once()
+ ->andReturn(new Token('foo.bar.baz'));
$this->jwt->shouldReceive('unsetToken')->once();
+ $this->jwt->shouldReceive('invalidate')
+ ->once()
+ ->andReturnSelf();
+
+ $this->events->shouldReceive('dispatch')->once();
+ $this->events->shouldReceive('assertDispatched')->once();
$this->guard->logout();
+
+ $this->events->assertDispatched(JWTLogout::class, 1);
$this->assertNull($this->guard->getUser());
}
- /**
- * @test
- * @group laravel-5.2
- */
+ /** @test */
public function it_should_refresh_the_token()
{
$this->jwt->shouldReceive('setRequest')->andReturn($this->jwt);
- $this->jwt->shouldReceive('getToken')->once()->andReturn(true);
- $this->jwt->shouldReceive('refresh')->once()->andReturn('foo.bar.baz');
-
- $this->assertSame($this->guard->refresh(), 'foo.bar.baz');
+ $this->jwt->shouldReceive('getToken')
+ ->twice()
+ ->andReturn(new Token('baz.bar.foo'));
+ $this->jwt->shouldReceive('refresh')
+ ->twice()
+ ->andReturn($token = new Token('foo.bar.baz'));
+
+ $this->events->shouldReceive('dispatch')->times(2);
+ $this->events->shouldReceive('assertDispatched')->once();
+
+ $this->assertTrue($token->matches($this->guard->refresh())); // once
+ $this->assertSame((string) $this->guard->refresh(), 'foo.bar.baz'); // twice
+ $this->events->assertDispatched(JWTRefresh::class, 2);
}
- /**
- * @test
- * @group laravel-5.2
- */
+ /** @test */
public function it_should_invalidate_the_token()
{
$this->jwt->shouldReceive('setRequest')->andReturn($this->jwt);
- $this->jwt->shouldReceive('getToken')->once()->andReturn(true);
- $this->jwt->shouldReceive('invalidate')->once()->andReturn(true);
-
- $this->assertTrue($this->guard->invalidate());
+ $this->jwt->shouldReceive('getToken')
+ ->once()
+ ->andReturn(new Token('foo.bar.baz'));
+ $this->jwt->shouldReceive('invalidate')
+ ->once()
+ ->andReturnSelf();
+
+ $this->events->shouldReceive('dispatch')->once();
+ $this->events->shouldReceive('assertDispatched')->once();
+
+ $this->guard->invalidate();
+ $this->events->assertDispatched(JWTInvalidate::class, 1);
}
- /**
- * @test
- * @group laravel-5.2
- * @expectedException \Tymon\JWTAuth\Exceptions\JWTException
- * @expectedExceptionMessage Token could not be parsed from the request.
- */
+ /** @test */
public function it_should_throw_an_exception_if_there_is_no_token_present_when_required()
{
+ $this->expectException(JWTException::class);
+ $this->expectExceptionMessage('Token could not be parsed from the request.');
+
$this->jwt->shouldReceive('setRequest')->andReturn($this->jwt);
- $this->jwt->shouldReceive('getToken')->once()->andReturn(false);
+ $this->jwt->shouldReceive('getToken')
+ ->once()
+ ->andReturn(null);
$this->jwt->shouldReceive('refresh')->never();
$this->guard->refresh();
}
- /**
- * @test
- * @group laravel-5.2
- */
+ /** @test */
public function it_should_generate_a_token_by_id()
{
- $user = new LaravelUserStub;
+ $user = new LaravelUserStub();
$this->provider->shouldReceive('retrieveById')
- ->once()
- ->with(1)
- ->andReturn($user);
+ ->once()
+ ->with(1)
+ ->andReturn($user);
$this->jwt->shouldReceive('fromUser')
- ->once()
- ->with($user)
- ->andReturn('foo.bar.baz');
+ ->once()
+ ->with($user)
+ ->andReturn($token = new Token('foo.bar.baz'));
- $this->assertSame('foo.bar.baz', $this->guard->tokenById(1));
+ $this->assertSame($token, $this->guard->tokenById(1));
}
- /**
- * @test
- * @group laravel-5.2
- */
+ /** @test */
public function it_should_not_generate_a_token_by_id()
{
$this->provider->shouldReceive('retrieveById')
- ->once()
- ->with(1)
- ->andReturn(null);
+ ->once()
+ ->with(1)
+ ->andReturn(null);
$this->assertNull($this->guard->tokenById(1));
}
- /**
- * @test
- * @group laravel-5.2
- */
+ /** @test */
public function it_should_authenticate_the_user_by_credentials_and_return_true_if_valid()
{
$credentials = ['foo' => 'bar', 'baz' => 'bob'];
- $user = new LaravelUserStub;
+ $user = new LaravelUserStub();
$this->provider->shouldReceive('retrieveByCredentials')
- ->once()
- ->with($credentials)
- ->andReturn($user);
+ ->once()
+ ->with($credentials)
+ ->andReturn($user);
$this->provider->shouldReceive('validateCredentials')
- ->once()
- ->with($user, $credentials)
- ->andReturn(true);
+ ->once()
+ ->with($user, $credentials)
+ ->andReturn(true);
+
+ $this->events->shouldReceive('dispatch')->once();
+ $this->events->shouldReceive('assertDispatched')->once();
$this->assertTrue($this->guard->once($credentials));
+ $this->events->assertDispatched(JWTAttempt::class, 1);
}
- /**
- * @test
- * @group laravel-5.2
- */
+ /** @test */
public function it_should_attempt_to_authenticate_the_user_by_credentials_and_return_false_if_invalid()
{
$credentials = ['foo' => 'bar', 'baz' => 'bob'];
- $user = new LaravelUserStub;
+ $user = new LaravelUserStub();
$this->provider->shouldReceive('retrieveByCredentials')
- ->once()
- ->with($credentials)
- ->andReturn($user);
+ ->once()
+ ->with($credentials)
+ ->andReturn($user);
$this->provider->shouldReceive('validateCredentials')
- ->once()
- ->with($user, $credentials)
- ->andReturn(false);
+ ->once()
+ ->with($user, $credentials)
+ ->andReturn(false);
+
+ $this->events->shouldReceive('dispatch')->once();
+ $this->events->shouldReceive('assertDispatched')->once();
$this->assertFalse($this->guard->once($credentials));
+ $this->events->assertDispatched(JWTAttempt::class, 1);
}
- /**
- * @test
- * @group laravel-5.2
- */
+ /** @test */
public function it_should_authenticate_the_user_by_id_and_return_boolean()
{
- $user = new LaravelUserStub;
+ $user = new LaravelUserStub();
$this->provider->shouldReceive('retrieveById')
- ->twice()
- ->with(1)
- ->andReturn($user);
+ ->twice()
+ ->with(1)
+ ->andReturn($user);
$this->assertTrue($this->guard->onceUsingId(1)); // once
$this->assertTrue($this->guard->byId(1)); // twice
}
- /**
- * @test
- * @group laravel-5.2
- */
+ /** @test */
public function it_should_not_authenticate_the_user_by_id_and_return_false()
{
$this->provider->shouldReceive('retrieveById')
- ->twice()
- ->with(1)
- ->andReturn(null);
+ ->twice()
+ ->with(1)
+ ->andReturn(null);
$this->assertFalse($this->guard->onceUsingId(1)); // once
$this->assertFalse($this->guard->byId(1)); // twice
}
- /**
- * @test
- * @group laravel-5.2
- */
+ /** @test */
public function it_should_create_a_token_from_a_user_object()
{
- $user = new LaravelUserStub;
+ $user = new LaravelUserStub();
$this->jwt->shouldReceive('fromUser')
- ->once()
- ->with($user)
- ->andReturn('foo.bar.baz');
+ ->once()
+ ->with($user)
+ ->andReturn($token = new Token('foo.bar.baz'));
$this->jwt->shouldReceive('setToken')
- ->once()
- ->with('foo.bar.baz')
- ->andReturnSelf();
+ ->once()
+ ->with($token)
+ ->andReturnSelf();
- $token = $this->guard->login($user);
+ $this->events->shouldReceive('dispatch')->once();
+ $this->events->shouldReceive('assertDispatched')->once();
- $this->assertSame('foo.bar.baz', $token);
+ $jwt = $this->guard->login($user);
+
+ $this->events->assertDispatched(JWTLogin::class, 1);
+ $this->assertTrue($jwt->matches($token));
+ $this->assertSame('foo.bar.baz', (string) $jwt);
}
- /**
- * @test
- * @group laravel-5.2
- */
+ /** @test */
public function it_should_get_the_payload()
{
- $this->jwt->shouldReceive('setRequest')->andReturn($this->jwt);
- $this->jwt->shouldReceive('getToken')->once()->andReturn('foo.bar.baz');
- $this->jwt->shouldReceive('getPayload')->once()->andReturn(Mockery::mock(Payload::class));
+ $this->jwt->shouldReceive('setRequest')->andReturnSelf();
+ $this->jwt->shouldReceive('getToken')
+ ->once()
+ ->andReturn(new Token('foo.bar.baz'));
+ $this->jwt->shouldReceive('payload')
+ ->once()
+ ->andReturn(Mockery::mock(Payload::class));
+
$this->assertInstanceOf(Payload::class, $this->guard->payload());
}
+
+ /** @test */
+ public function it_should_be_macroable()
+ {
+ $this->guard->macro('foo', fn () => 'bar');
+
+ $this->assertEquals('bar', $this->guard->foo());
+ }
}
diff --git a/tests/JWTTest.php b/tests/JWTTest.php
new file mode 100644
index 000000000..247be2be4
--- /dev/null
+++ b/tests/JWTTest.php
@@ -0,0 +1,283 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Tymon\JWTAuth\Test;
+
+use Illuminate\Http\Request;
+use Mockery;
+use Tymon\JWTAuth\Blacklist;
+use Tymon\JWTAuth\Builder;
+use Tymon\JWTAuth\Claims\HashedSubject;
+use Tymon\JWTAuth\Claims\Subject;
+use Tymon\JWTAuth\Exceptions\JWTException;
+use Tymon\JWTAuth\Exceptions\TokenInvalidException;
+use Tymon\JWTAuth\Http\Parser\Parser;
+use Tymon\JWTAuth\JWT;
+use Tymon\JWTAuth\Manager;
+use Tymon\JWTAuth\Payload;
+use Tymon\JWTAuth\Test\Stubs\UserStub;
+use Tymon\JWTAuth\Token;
+
+class JWTTest extends AbstractTestCase
+{
+ /**
+ * @var \Mockery\MockInterface|\Tymon\JWTAuth\Builder
+ */
+ protected $builder;
+
+ /**
+ * @var \Mockery\MockInterface|\Tymon\JWTAuth\Manager
+ */
+ protected $manager;
+
+ /**
+ * @var \Mockery\MockInterface|\Tymon\JWTAuth\Http\Parser\Parser
+ */
+ protected $parser;
+
+ /**
+ * @var \Tymon\JWTAuth\JWT
+ */
+ protected $jwt;
+
+ public function setUp(): void
+ {
+ $this->builder = Mockery::spy(Builder::class);
+ $this->manager = Mockery::spy(Manager::class);
+ $this->parser = Mockery::spy(Parser::class);
+ $this->jwt = new JWT($this->builder, $this->manager, $this->parser);
+ }
+
+ /** @test */
+ public function it_should_return_a_token_when_passing_a_user()
+ {
+ $this->manager->shouldReceive('tokenForSubject')
+ ->once()
+ ->with($user = new UserStub, ['foo' => 'bar'])
+ ->andReturn($token = new Token('foo.bar.baz'));
+
+ $jwt = $this->jwt->claims(['foo' => 'bar'])->fromUser($user);
+
+ $this->assertSame($jwt, $token);
+ $this->assertSame((string) $jwt, 'foo.bar.baz');
+ }
+
+ /** @test */
+ public function it_should_pass_hash_check_if_hash_matches()
+ {
+ $hash = sha1(UserStub::class);
+
+ $payload = Mockery::mock(Payload::class)->shouldReceive('offsetExists')
+ ->with(HashedSubject::NAME)
+ ->andReturn(true)
+ ->getMock();
+
+ $payload->shouldReceive('get')
+ ->with(HashedSubject::NAME)
+ ->andReturn($hash);
+
+ $this->builder->shouldReceive('hashSubjectModel')
+ ->once()
+ ->with(UserStub::class)
+ ->andReturn($hash);
+
+ $this->assertTrue(
+ $this->jwt->setToken('foo.bar.baz')->checkSubjectModel(UserStub::class, $payload)
+ );
+ }
+
+ /** @test */
+ public function it_should_pass_provider_check_if_hash_matches_when_hashed_subject_is_null()
+ {
+ $payload = Mockery::mock(Payload::class)->shouldReceive('get')
+ ->with(HashedSubject::NAME)
+ ->andReturn(null)
+ ->getMock();
+
+ $this->assertTrue(
+ $this->jwt->setToken('foo.bar.baz')
+ ->checkSubjectModel('Tymon\JWTAuth\Test\Stubs\UserStub', $payload)
+ );
+ }
+
+ /** @test */
+ public function it_should_not_pass_provider_check_if_hash_not_match()
+ {
+ $payload = Mockery::mock(Payload::class)->shouldReceive('get')
+ ->with(HashedSubject::NAME)
+ ->andReturn(sha1('Tymon\JWTAuth\Test\Stubs\UserStub1'))
+ ->getMock();
+
+ $this->assertFalse(
+ $this->jwt->setToken('foo.bar.baz')
+ ->checkSubjectModel('Tymon\JWTAuth\Test\Stubs\UserStub', $payload)
+ );
+ }
+
+ /** @test */
+ public function it_should_refresh_a_token()
+ {
+ $this->manager->shouldReceive('refresh', 60)
+ ->once()
+ ->andReturn($token = new Token('baz.bar.foo'));
+
+ $result = $this->jwt->setToken('foo.bar.baz')->refresh();
+
+ $this->assertSame($result, $token);
+ $this->assertSame((string) $result, 'baz.bar.foo');
+ }
+
+ /** @test */
+ public function it_should_invalidate_a_token()
+ {
+ $token = new Token('foo.bar.baz');
+
+ $this->manager->shouldReceive('invalidate')
+ ->once()
+ ->with($token)->andReturn(true);
+
+ $this->jwt->setToken($token)->invalidate();
+ }
+
+ /** @test */
+ public function it_should_retrieve_the_token_from_the_request()
+ {
+ $this->parser->shouldReceive('parseToken')->andReturn('foo.bar.baz');
+
+ $this->assertInstanceOf(Token::class, $this->jwt->parseToken()->getToken());
+ $this->assertEquals($this->jwt->getToken(), 'foo.bar.baz');
+ }
+
+ /** @test */
+ public function it_should_get_the_authenticated_user()
+ {
+ $manager = $this->jwt->manager();
+ $this->assertInstanceOf(Manager::class, $manager);
+ }
+
+ /** @test */
+ public function it_should_return_false_if_the_token_is_invalid()
+ {
+ $this->parser->shouldReceive('parseToken')->andReturn('foo.bar.baz');
+ $this->manager->shouldReceive('decode')
+ ->once()
+ ->andThrow(new TokenInvalidException);
+
+ $this->assertFalse($this->jwt->parseToken()->check());
+ }
+
+ /** @test */
+ public function it_should_return_true_if_the_token_is_valid()
+ {
+ $payload = Mockery::mock(Payload::class);
+
+ $this->parser->shouldReceive('parseToken')->andReturn('foo.bar.baz');
+ $this->manager->shouldReceive('decode')
+ ->once()
+ ->andReturn($payload);
+
+ $this->assertTrue($this->jwt->parseToken()->check());
+ }
+
+ /** @test */
+ public function it_should_throw_an_exception_when_token_not_present_in_request()
+ {
+ $this->expectException(JWTException::class);
+ $this->expectExceptionMessage('The token could not be parsed from the request');
+
+ $this->parser->shouldReceive('parseToken')->andReturn(false);
+
+ $this->jwt->parseToken();
+ }
+
+ /** @test */
+ public function it_should_return_false_when_no_token_is_set()
+ {
+ $this->parser->shouldReceive('parseToken')->andReturn(false);
+
+ $this->assertNull($this->jwt->getToken());
+ }
+
+ /** @test */
+ public function it_should_magically_call_the_manager()
+ {
+ $this->manager->shouldReceive('getBlacklist')->andReturn(Mockery::mock(Blacklist::class));
+
+ $blacklist = $this->jwt->manager()->getBlacklist();
+
+ $this->assertInstanceOf(Blacklist::class, $blacklist);
+ }
+
+ /** @test */
+ public function it_should_set_the_request()
+ {
+ $request = Request::create('/foo', 'GET', ['token' => 'some.random.token']);
+
+ $this->parser->shouldReceive('setRequest')
+ ->once()
+ ->with($request);
+ $this->parser->shouldReceive('parseToken')->andReturn('some.random.token');
+
+ $token = $this->jwt->setRequest($request)->getToken();
+
+ $this->assertEquals('some.random.token', $token);
+ }
+
+ /** @test */
+ public function it_should_unset_the_token()
+ {
+ $this->parser->shouldReceive('parseToken')->andThrow(new JWTException);
+ $token = new Token('foo.bar.baz');
+ $this->jwt->setToken($token);
+
+ $this->assertSame($this->jwt->getToken(), $token);
+ $this->jwt->unsetToken();
+ $this->assertNull($this->jwt->getToken());
+ }
+
+ /** @test */
+ public function it_should_register_a_custom_claim_validator()
+ {
+ $this->builder->shouldReceive('setCustomValidator')
+ ->with('foo', Mockery::type('callable'))
+ ->once();
+
+ $this->jwt->registerCustomValidator('foo', fn ($value) => $value !== 'bar');
+ }
+
+ /** @test */
+ public function it_should_get_the_manager_instance()
+ {
+ $this->assertInstanceOf(Manager::class, $this->jwt->manager());
+ }
+
+ /** @test */
+ public function it_should_get_the_parser_instance()
+ {
+ $this->assertInstanceOf(Parser::class, $this->jwt->parser());
+ }
+
+ /** @test */
+ public function it_should_get_a_claim_value()
+ {
+ $payload = Mockery::mock(Payload::class);
+ $payload->shouldReceive('get')
+ ->once()
+ ->with(Subject::NAME)
+ ->andReturn(1);
+
+ $this->manager->shouldReceive('decode')
+ ->once()
+ ->andReturn($payload);
+
+ $this->assertSame($this->jwt->setToken('foo.bar.baz')->getClaim(Subject::NAME), 1);
+ }
+}
diff --git a/tests/ManagerTest.php b/tests/ManagerTest.php
index ba1ce3dcd..3d95be32d 100644
--- a/tests/ManagerTest.php
+++ b/tests/ManagerTest.php
@@ -12,20 +12,22 @@
namespace Tymon\JWTAuth\Test;
use Mockery;
-use Tymon\JWTAuth\Token;
-use Tymon\JWTAuth\Factory;
-use Tymon\JWTAuth\Manager;
-use Tymon\JWTAuth\Payload;
use Tymon\JWTAuth\Blacklist;
-use Tymon\JWTAuth\Claims\JwtId;
-use Tymon\JWTAuth\Claims\Issuer;
-use Tymon\JWTAuth\Claims\Subject;
+use Tymon\JWTAuth\Builder;
+use Tymon\JWTAuth\Claims\Expiration;
use Tymon\JWTAuth\Claims\IssuedAt;
+use Tymon\JWTAuth\Claims\Issuer;
+use Tymon\JWTAuth\Claims\JwtId;
use Tymon\JWTAuth\Claims\NotBefore;
-use Tymon\JWTAuth\Claims\Collection;
-use Tymon\JWTAuth\Claims\Expiration;
+use Tymon\JWTAuth\Claims\Subject;
use Tymon\JWTAuth\Contracts\Providers\JWT;
-use Tymon\JWTAuth\Validators\PayloadValidator;
+use Tymon\JWTAuth\Exceptions\JWTException;
+use Tymon\JWTAuth\Exceptions\TokenBlacklistedException;
+use Tymon\JWTAuth\Factory;
+use Tymon\JWTAuth\Manager;
+use Tymon\JWTAuth\Options;
+use Tymon\JWTAuth\Payload;
+use Tymon\JWTAuth\Token;
class ManagerTest extends AbstractTestCase
{
@@ -45,44 +47,40 @@ class ManagerTest extends AbstractTestCase
protected $factory;
/**
- * @var \Tymon\JWTAuth\Manager
+ * @var \Mockery\MockInterface|\Tymon\JWTAuth\Builder
*/
- protected $manager;
+ protected $builder;
/**
- * @var \Mockery\MockInterface
+ * @var \Tymon\JWTAuth\Manager
*/
- protected $validator;
+ protected $manager;
- public function setUp()
+ public function setUp(): void
{
parent::setUp();
$this->jwt = Mockery::mock(JWT::class);
$this->blacklist = Mockery::mock(Blacklist::class);
- $this->factory = Mockery::mock(Factory::class);
- $this->manager = new Manager($this->jwt, $this->blacklist, $this->factory);
- $this->validator = Mockery::mock(PayloadValidator::class);
+ $this->builder = Mockery::mock(Builder::class);
+ $this->manager = new Manager($this->jwt, $this->blacklist, $this->builder);
}
/** @test */
public function it_should_encode_a_payload()
{
- $claims = [
+ $payload = Factory::make([
new Subject(1),
new Issuer('http://example.com'),
new Expiration($this->testNowTimestamp + 3600),
new NotBefore($this->testNowTimestamp),
new IssuedAt($this->testNowTimestamp),
new JwtId('foo'),
- ];
-
- $collection = Collection::make($claims);
+ ]);
- $this->validator->shouldReceive('setRefreshFlow->check')->andReturn($collection);
- $payload = new Payload($collection, $this->validator);
-
- $this->jwt->shouldReceive('encode')->with($payload->toArray())->andReturn('foo.bar.baz');
+ $this->jwt->shouldReceive('token')
+ ->with($payload)
+ ->andReturn(new Token('foo.bar.baz'));
$token = $this->manager->encode($payload);
@@ -92,28 +90,30 @@ public function it_should_encode_a_payload()
/** @test */
public function it_should_decode_a_token()
{
- $claims = [
+ $payload = Factory::make([
new Subject(1),
new Issuer('http://example.com'),
new Expiration($this->testNowTimestamp + 3600),
new NotBefore($this->testNowTimestamp),
new IssuedAt($this->testNowTimestamp),
new JwtId('foo'),
- ];
- $collection = Collection::make($claims);
-
- $this->validator->shouldReceive('setRefreshFlow->check')->andReturn($collection);
- $payload = new Payload($collection, $this->validator);
+ ]);
$token = new Token('foo.bar.baz');
+ $options = new Options();
- $this->jwt->shouldReceive('decode')->once()->with('foo.bar.baz')->andReturn($payload->toArray());
+ $this->jwt->shouldReceive('payload')
+ ->once()
+ ->with($token, $options)
+ ->andReturn($payload);
- $this->factory->shouldReceive('setRefreshFlow')->andReturn($this->factory);
- $this->factory->shouldReceive('customClaims')->andReturn($this->factory);
- $this->factory->shouldReceive('make')->andReturn($payload);
+ $this->blacklist->shouldReceive('has')
+ ->with($payload)
+ ->andReturn(false);
- $this->blacklist->shouldReceive('has')->with($payload)->andReturn(false);
+ $this->builder->shouldReceive('getOptions')
+ ->once()
+ ->andReturn($options);
$payload = $this->manager->decode($token);
@@ -121,34 +121,36 @@ public function it_should_decode_a_token()
$this->assertSame($payload->count(), 6);
}
- /**
- * @test
- * @expectedException \Tymon\JWTAuth\Exceptions\TokenBlacklistedException
- * @expectedExceptionMessage The token has been blacklisted
- */
+ /** @test */
public function it_should_throw_exception_when_token_is_blacklisted()
{
- $claims = [
+ $this->expectException(TokenBlacklistedException::class);
+ $this->expectExceptionMessage('The token has been blacklisted');
+
+ $payload = Factory::make([
new Subject(1),
new Issuer('http://example.com'),
new Expiration($this->testNowTimestamp + 3600),
new NotBefore($this->testNowTimestamp),
new IssuedAt($this->testNowTimestamp),
new JwtId('foo'),
- ];
- $collection = Collection::make($claims);
+ ]);
- $this->validator->shouldReceive('setRefreshFlow->check')->andReturn($collection);
- $payload = new Payload($collection, $this->validator);
$token = new Token('foo.bar.baz');
+ $options = new Options();
- $this->jwt->shouldReceive('decode')->once()->with('foo.bar.baz')->andReturn($payload->toArray());
+ $this->jwt->shouldReceive('payload')
+ ->once()
+ ->with($token, $options)
+ ->andReturn($payload);
- $this->factory->shouldReceive('setRefreshFlow')->andReturn($this->factory);
- $this->factory->shouldReceive('customClaims')->with($payload->toArray())->andReturn($this->factory);
- $this->factory->shouldReceive('make')->andReturn($payload);
+ $this->blacklist->shouldReceive('has')
+ ->with($payload)
+ ->andReturn(true);
- $this->blacklist->shouldReceive('has')->with($payload)->andReturn(true);
+ $this->builder->shouldReceive('getOptions')
+ ->once()
+ ->andReturn($options);
$this->manager->decode($token);
}
@@ -156,33 +158,50 @@ public function it_should_throw_exception_when_token_is_blacklisted()
/** @test */
public function it_should_refresh_a_token()
{
- $claims = [
+ $payload = Factory::make([
new Subject(1),
- new Issuer('http://example.com'),
- new Expiration($this->testNowTimestamp - 3600),
- new NotBefore($this->testNowTimestamp),
+ new Issuer('example.com'),
+ new Expiration($this->testNowTimestamp + 3600),
new IssuedAt($this->testNowTimestamp),
new JwtId('foo'),
- ];
- $collection = Collection::make($claims);
+ ]);
- $this->validator->shouldReceive('setRefreshFlow->check')->andReturn($collection);
- $payload = new Payload($collection, $this->validator);
$token = new Token('foo.bar.baz');
-
- $this->jwt->shouldReceive('decode')->twice()->with('foo.bar.baz')->andReturn($payload->toArray());
- $this->jwt->shouldReceive('encode')->with($payload->toArray())->andReturn('baz.bar.foo');
-
- $this->factory->shouldReceive('setRefreshFlow')->with(true)->andReturn($this->factory);
- $this->factory->shouldReceive('customClaims')->andReturn($this->factory);
- $this->factory->shouldReceive('make')->andReturn($payload);
-
- $this->blacklist->shouldReceive('has')->with($payload)->andReturn(false);
- $this->blacklist->shouldReceive('add')->once()->with($payload);
+ $options = new Options();
+
+ $this->jwt->shouldReceive('payload')
+ ->twice()
+ ->with($token, $options)
+ ->andReturn($payload);
+
+ $this->jwt->shouldReceive('token')
+ ->once()
+ ->with(Mockery::type(Payload::class))
+ ->andReturn(new Token('baz.bar.foo'));
+
+ $this->blacklist->shouldReceive('has')
+ ->with($payload)
+ ->andReturn(false);
+ $this->blacklist->shouldReceive('add')
+ ->once()
+ ->with($payload);
+
+ $this->builder->shouldReceive('getOptions')
+ ->twice()
+ ->andReturn($options);
+
+ $this->builder->shouldReceive('buildRefreshClaims')
+ ->once()
+ ->with($payload)
+ ->andReturn($claims = $payload->toArray());
+
+ $this->builder->shouldReceive('make')
+ ->once()
+ ->with($claims)
+ ->andReturn($payload);
$token = $this->manager->refresh($token);
- // $this->assertArrayHasKey('ref', $payload);
$this->assertInstanceOf(Token::class, $token);
$this->assertEquals('baz.bar.foo', $token);
}
@@ -190,81 +209,49 @@ public function it_should_refresh_a_token()
/** @test */
public function it_should_invalidate_a_token()
{
- $claims = [
+ $payload = Factory::make([
new Subject(1),
new Issuer('http://example.com'),
new Expiration($this->testNowTimestamp + 3600),
new NotBefore($this->testNowTimestamp),
new IssuedAt($this->testNowTimestamp),
new JwtId('foo'),
- ];
- $collection = Collection::make($claims);
+ ]);
- $this->validator->shouldReceive('setRefreshFlow->check')->andReturn($collection);
- $payload = new Payload($collection, $this->validator);
$token = new Token('foo.bar.baz');
+ $options = new Options();
- $this->jwt->shouldReceive('decode')->once()->with('foo.bar.baz')->andReturn($payload->toArray());
+ $this->jwt->shouldReceive('payload')
+ ->once()
+ ->with($token, $options)
+ ->andReturn($payload);
- $this->factory->shouldReceive('setRefreshFlow')->andReturn($this->factory);
- $this->factory->shouldReceive('customClaims')->with($payload->toArray())->andReturn($this->factory);
- $this->factory->shouldReceive('make')->andReturn($payload);
+ $this->blacklist->shouldReceive('has')
+ ->with($payload)
+ ->andReturn(false);
- $this->blacklist->shouldReceive('has')->with($payload)->andReturn(false);
+ $this->blacklist->shouldReceive('add')
+ ->with($payload)
+ ->andReturn(true);
- $this->blacklist->shouldReceive('add')->with($payload)->andReturn(true);
+ $this->builder->shouldReceive('getOptions')
+ ->once()
+ ->andReturn($options);
$this->manager->invalidate($token);
}
/** @test */
- public function it_should_force_invalidate_a_token_forever()
- {
- $claims = [
- new Subject(1),
- new Issuer('http://example.com'),
- new Expiration($this->testNowTimestamp + 3600),
- new NotBefore($this->testNowTimestamp),
- new IssuedAt($this->testNowTimestamp),
- new JwtId('foo'),
- ];
- $collection = Collection::make($claims);
-
- $this->validator->shouldReceive('setRefreshFlow->check')->andReturn($collection);
- $payload = new Payload($collection, $this->validator);
- $token = new Token('foo.bar.baz');
-
- $this->jwt->shouldReceive('decode')->once()->with('foo.bar.baz')->andReturn($payload->toArray());
-
- $this->factory->shouldReceive('setRefreshFlow')->andReturn($this->factory);
- $this->factory->shouldReceive('customClaims')->with($payload->toArray())->andReturn($this->factory);
- $this->factory->shouldReceive('make')->andReturn($payload);
-
- $this->blacklist->shouldReceive('has')->with($payload)->andReturn(false);
-
- $this->blacklist->shouldReceive('addForever')->with($payload)->andReturn(true);
-
- $this->manager->invalidate($token, true);
- }
-
- /**
- * @test
- * @expectedException \Tymon\JWTAuth\Exceptions\JWTException
- * @expectedExceptionMessage You must have the blacklist enabled to invalidate a token.
- */
public function it_should_throw_an_exception_when_enable_blacklist_is_set_to_false()
{
+ $this->expectException(JWTException::class);
+ $this->expectExceptionMessage('You must have the blacklist enabled to invalidate a token.');
+
$token = new Token('foo.bar.baz');
$this->manager->setBlacklistEnabled(false)->invalidate($token);
}
- /** @test */
- public function it_should_get_the_payload_factory()
- {
- $this->assertInstanceOf(Factory::class, $this->manager->getPayloadFactory());
- }
-
/** @test */
public function it_should_get_the_jwt_provider()
{
@@ -276,4 +263,10 @@ public function it_should_get_the_blacklist()
{
$this->assertInstanceOf(Blacklist::class, $this->manager->getBlacklist());
}
+
+ /** @test */
+ public function it_should_get_the_builder()
+ {
+ $this->assertInstanceOf(Builder::class, $this->manager->builder());
+ }
}
diff --git a/tests/Middleware/AbstractMiddlewareTest.php b/tests/Middleware/AbstractMiddlewareTest.php
deleted file mode 100644
index 3b9f290cc..000000000
--- a/tests/Middleware/AbstractMiddlewareTest.php
+++ /dev/null
@@ -1,38 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Tymon\JWTAuth\Test\Middleware;
-
-use Mockery;
-use Tymon\JWTAuth\JWTAuth;
-use Illuminate\Http\Request;
-use Tymon\JWTAuth\Test\AbstractTestCase;
-
-abstract class AbstractMiddlewareTest extends AbstractTestCase
-{
- /**
- * @var \Mockery\MockInterface|\Tymon\JWTAuth\JWTAuth
- */
- protected $auth;
-
- /**
- * @var \Mockery\MockInterface|\Illuminate\Http\Request
- */
- protected $request;
-
- public function setUp()
- {
- parent::setUp();
-
- $this->auth = Mockery::mock(JWTAuth::class);
- $this->request = Mockery::mock(Request::class);
- }
-}
diff --git a/tests/Middleware/AuthenticateAndRenewTest.php b/tests/Middleware/AuthenticateAndRenewTest.php
deleted file mode 100644
index 5a17a4d78..000000000
--- a/tests/Middleware/AuthenticateAndRenewTest.php
+++ /dev/null
@@ -1,89 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Tymon\JWTAuth\Test\Middleware;
-
-use Mockery;
-use Illuminate\Http\Response;
-use Tymon\JWTAuth\Http\Parser\Parser;
-use Tymon\JWTAuth\Test\Stubs\UserStub;
-use Tymon\JWTAuth\Exceptions\TokenInvalidException;
-use Tymon\JWTAuth\Http\Middleware\AuthenticateAndRenew;
-
-class AuthenticateAndRenewTest extends AbstractMiddlewareTest
-{
- /**
- * @var \Tymon\JWTAuth\Http\Middleware\Authenticate|\Tymon\JWTAuth\Http\Middleware\AuthenticateAndRenew
- */
- protected $middleware;
-
- public function setUp()
- {
- parent::setUp();
-
- $this->middleware = new AuthenticateAndRenew($this->auth);
- }
-
- /** @test */
- public function it_should_authenticate_a_user_and_return_a_new_token()
- {
- $parser = Mockery::mock(Parser::class);
- $parser->shouldReceive('hasToken')->once()->andReturn(true);
- $this->auth->shouldReceive('parser')->andReturn($parser);
- $this->auth->parser()->shouldReceive('setRequest')->once()->with($this->request)->andReturn($this->auth->parser());
-
- $this->auth->shouldReceive('parseToken->authenticate')->once()->andReturn(new UserStub);
-
- $this->auth->shouldReceive('refresh')->once()->andReturn('foo.bar.baz');
-
- $response = $this->middleware->handle($this->request, function () {
- return new Response;
- });
-
- $this->assertSame($response->headers->get('authorization'), 'Bearer foo.bar.baz');
- }
-
- /**
- * @test
- * @expectedException \Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException
- */
- public function it_should_throw_an_unauthorized_exception_if_token_not_provided()
- {
- $parser = Mockery::mock(Parser::class);
- $parser->shouldReceive('hasToken')->once()->andReturn(false);
-
- $this->auth->shouldReceive('parser')->andReturn($parser);
- $this->auth->parser()->shouldReceive('setRequest')->once()->with($this->request)->andReturn($this->auth->parser());
-
- $this->middleware->handle($this->request, function () {
- //
- });
- }
-
- /**
- * @test
- * @expectedException \Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException
- */
- public function it_should_throw_an_unauthorized_exception_if_token_invalid()
- {
- $parser = Mockery::mock(Parser::class);
- $parser->shouldReceive('hasToken')->once()->andReturn(true);
-
- $this->auth->shouldReceive('parser')->andReturn($parser);
-
- $this->auth->parser()->shouldReceive('setRequest')->once()->with($this->request)->andReturn($this->auth->parser());
- $this->auth->shouldReceive('parseToken->authenticate')->once()->andThrow(new TokenInvalidException);
-
- $this->middleware->handle($this->request, function () {
- //
- });
- }
-}
diff --git a/tests/Middleware/AuthenticateTest.php b/tests/Middleware/AuthenticateTest.php
deleted file mode 100644
index 30b3cc27b..000000000
--- a/tests/Middleware/AuthenticateTest.php
+++ /dev/null
@@ -1,104 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Tymon\JWTAuth\Test\Middleware;
-
-use Mockery;
-use Tymon\JWTAuth\Http\Parser\Parser;
-use Tymon\JWTAuth\Test\Stubs\UserStub;
-use Tymon\JWTAuth\Http\Middleware\Authenticate;
-use Tymon\JWTAuth\Exceptions\TokenInvalidException;
-
-class AuthenticateTest extends AbstractMiddlewareTest
-{
- /**
- * @var \Tymon\JWTAuth\Http\Middleware\Authenticate
- */
- protected $middleware;
-
- public function setUp()
- {
- parent::setUp();
-
- $this->middleware = new Authenticate($this->auth);
- }
-
- /** @test */
- public function it_should_authenticate_a_user()
- {
- $parser = Mockery::mock(Parser::class);
- $parser->shouldReceive('hasToken')->once()->andReturn(true);
-
- $this->auth->shouldReceive('parser')->andReturn($parser);
-
- $this->auth->parser()->shouldReceive('setRequest')->once()->with($this->request)->andReturn($this->auth->parser());
- $this->auth->shouldReceive('parseToken->authenticate')->once()->andReturn(new UserStub);
-
- $this->middleware->handle($this->request, function () {
- //
- });
- }
-
- /**
- * @test
- * @expectedException \Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException
- */
- public function it_should_throw_an_unauthorized_exception_if_token_not_provided()
- {
- $parser = Mockery::mock(Parser::class);
- $parser->shouldReceive('hasToken')->once()->andReturn(false);
-
- $this->auth->shouldReceive('parser')->andReturn($parser);
- $this->auth->parser()->shouldReceive('setRequest')->once()->with($this->request)->andReturn($this->auth->parser());
-
- $this->middleware->handle($this->request, function () {
- //
- });
- }
-
- /**
- * @test
- * @expectedException \Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException
- */
- public function it_should_throw_an_unauthorized_exception_if_token_invalid()
- {
- $parser = Mockery::mock(Parser::class);
- $parser->shouldReceive('hasToken')->once()->andReturn(true);
-
- $this->auth->shouldReceive('parser')->andReturn($parser);
-
- $this->auth->parser()->shouldReceive('setRequest')->once()->with($this->request)->andReturn($this->auth->parser());
- $this->auth->shouldReceive('parseToken->authenticate')->once()->andThrow(new TokenInvalidException);
-
- $this->middleware->handle($this->request, function () {
- //
- });
- }
-
- /**
- * @test
- * @expectedException \Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException
- */
- public function it_should_throw_an_unauthorized_exception_if_user_not_found()
- {
- $parser = Mockery::mock(Parser::class);
- $parser->shouldReceive('hasToken')->once()->andReturn(true);
-
- $this->auth->shouldReceive('parser')->andReturn($parser);
-
- $this->auth->parser()->shouldReceive('setRequest')->once()->with($this->request)->andReturn($this->auth->parser());
- $this->auth->shouldReceive('parseToken->authenticate')->once()->andReturn(false);
-
- $this->middleware->handle($this->request, function () {
- //
- });
- }
-}
diff --git a/tests/Middleware/CheckTest.php b/tests/Middleware/CheckTest.php
deleted file mode 100644
index 232beb8f5..000000000
--- a/tests/Middleware/CheckTest.php
+++ /dev/null
@@ -1,81 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Tymon\JWTAuth\Test\Middleware;
-
-use Mockery;
-use Tymon\JWTAuth\Http\Parser\Parser;
-use Tymon\JWTAuth\Test\Stubs\UserStub;
-use Tymon\JWTAuth\Http\Middleware\Check;
-use Tymon\JWTAuth\Exceptions\TokenInvalidException;
-
-class CheckTest extends AbstractMiddlewareTest
-{
- /**
- * @var \Tymon\JWTAuth\Http\Middleware\Check
- */
- protected $middleware;
-
- public function setUp()
- {
- parent::setUp();
-
- $this->middleware = new Check($this->auth);
- }
-
- /** @test */
- public function it_should_authenticate_a_user_if_a_token_is_present()
- {
- $parser = Mockery::mock(Parser::class);
- $parser->shouldReceive('hasToken')->once()->andReturn(true);
-
- $this->auth->shouldReceive('parser')->andReturn($parser);
-
- $this->auth->parser()->shouldReceive('setRequest')->once()->with($this->request)->andReturn($this->auth->parser());
- $this->auth->shouldReceive('parseToken->authenticate')->once()->andReturn(new UserStub);
-
- $this->middleware->handle($this->request, function () {
- //
- });
- }
-
- /** @test */
- public function it_should_unset_the_exception_if_a_token_is_present()
- {
- $parser = Mockery::mock(Parser::class);
- $parser->shouldReceive('hasToken')->once()->andReturn(true);
-
- $this->auth->shouldReceive('parser')->andReturn($parser);
-
- $this->auth->parser()->shouldReceive('setRequest')->once()->with($this->request)->andReturn($this->auth->parser());
- $this->auth->shouldReceive('parseToken->authenticate')->once()->andThrow(new TokenInvalidException);
-
- $this->middleware->handle($this->request, function () {
- //
- });
- }
-
- /** @test */
- public function it_should_do_nothing_if_a_token_is_not_present()
- {
- $parser = Mockery::mock(Parser::class);
- $parser->shouldReceive('hasToken')->once()->andReturn(false);
-
- $this->auth->shouldReceive('parser')->andReturn($parser);
-
- $this->auth->parser()->shouldReceive('setRequest')->once()->with($this->request)->andReturn($this->auth->parser());
- $this->auth->shouldReceive('parseToken->authenticate')->never();
-
- $this->middleware->handle($this->request, function () {
- //
- });
- }
-}
diff --git a/tests/Middleware/RefreshTokenTest.php b/tests/Middleware/RefreshTokenTest.php
deleted file mode 100644
index d5e46a159..000000000
--- a/tests/Middleware/RefreshTokenTest.php
+++ /dev/null
@@ -1,87 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Tymon\JWTAuth\Test\Middleware;
-
-use Mockery;
-use Illuminate\Http\Response;
-use Tymon\JWTAuth\Http\Parser\Parser;
-use Tymon\JWTAuth\Http\Middleware\RefreshToken;
-use Tymon\JWTAuth\Exceptions\TokenInvalidException;
-
-class RefreshTokenTest extends AbstractMiddlewareTest
-{
- /**
- * @var \Tymon\JWTAuth\Http\Middleware\RefreshToken
- */
- protected $middleware;
-
- public function setUp()
- {
- parent::setUp();
-
- $this->middleware = new RefreshToken($this->auth);
- }
-
- /** @test */
- public function it_should_refresh_a_token()
- {
- $parser = Mockery::mock(Parser::class);
- $parser->shouldReceive('hasToken')->once()->andReturn(true);
-
- $this->auth->shouldReceive('parser')->andReturn($parser);
-
- $this->auth->parser()->shouldReceive('setRequest')->once()->with($this->request)->andReturn($this->auth->parser());
- $this->auth->shouldReceive('parseToken->refresh')->once()->andReturn('foo.bar.baz');
-
- $response = $this->middleware->handle($this->request, function () {
- return new Response;
- });
-
- $this->assertSame($response->headers->get('authorization'), 'Bearer foo.bar.baz');
- }
-
- /**
- * @test
- * @expectedException \Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException
- */
- public function it_should_throw_an_unauthorized_exception_if_token_not_provided()
- {
- $parser = Mockery::mock(Parser::class);
- $parser->shouldReceive('hasToken')->once()->andReturn(false);
-
- $this->auth->shouldReceive('parser')->andReturn($parser);
- $this->auth->parser()->shouldReceive('setRequest')->once()->with($this->request)->andReturn($this->auth->parser());
-
- $this->middleware->handle($this->request, function () {
- //
- });
- }
-
- /**
- * @test
- * @expectedException \Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException
- */
- public function it_should_throw_an_unauthorized_exception_if_token_invalid()
- {
- $parser = Mockery::mock(Parser::class);
- $parser->shouldReceive('hasToken')->once()->andReturn(true);
-
- $this->auth->shouldReceive('parser')->andReturn($parser);
-
- $this->auth->parser()->shouldReceive('setRequest')->once()->with($this->request)->andReturn($this->auth->parser());
- $this->auth->shouldReceive('parseToken->refresh')->once()->andThrow(new TokenInvalidException);
-
- $this->middleware->handle($this->request, function () {
- //
- });
- }
-}
diff --git a/tests/PayloadTest.php b/tests/PayloadTest.php
index ac8b13cfe..9da1c9ed5 100644
--- a/tests/PayloadTest.php
+++ b/tests/PayloadTest.php
@@ -11,18 +11,18 @@
namespace Tymon\JWTAuth\Test;
-use Mockery;
-use Tymon\JWTAuth\Payload;
-use Tymon\JWTAuth\Claims\Claim;
-use Tymon\JWTAuth\Claims\JwtId;
-use Tymon\JWTAuth\Claims\Issuer;
-use Tymon\JWTAuth\Claims\Subject;
+use BadMethodCallException;
use Tymon\JWTAuth\Claims\Audience;
-use Tymon\JWTAuth\Claims\IssuedAt;
-use Tymon\JWTAuth\Claims\NotBefore;
+use Tymon\JWTAuth\Claims\Claim;
use Tymon\JWTAuth\Claims\Collection;
use Tymon\JWTAuth\Claims\Expiration;
-use Tymon\JWTAuth\Validators\PayloadValidator;
+use Tymon\JWTAuth\Claims\IssuedAt;
+use Tymon\JWTAuth\Claims\Issuer;
+use Tymon\JWTAuth\Claims\JwtId;
+use Tymon\JWTAuth\Claims\NotBefore;
+use Tymon\JWTAuth\Claims\Subject;
+use Tymon\JWTAuth\Exceptions\PayloadException;
+use Tymon\JWTAuth\Payload;
class PayloadTest extends AbstractTestCase
{
@@ -36,7 +36,7 @@ class PayloadTest extends AbstractTestCase
*/
protected $payload;
- public function setUp()
+ public function setUp(): void
{
parent::setUp();
@@ -57,77 +57,69 @@ private function getTestPayload(array $extraClaims = [])
new NotBefore($this->testNowTimestamp),
new IssuedAt($this->testNowTimestamp),
new JwtId('foo'),
+ ...$extraClaims,
];
- if ($extraClaims) {
- $claims = array_merge($claims, $extraClaims);
- }
-
$collection = Collection::make($claims);
- $this->validator = Mockery::mock(PayloadValidator::class);
- $this->validator->shouldReceive('setRefreshFlow->check')->andReturn($collection);
-
- return new Payload($collection, $this->validator);
+ return new Payload($collection);
}
- /**
- * @test
- * @expectedException \Tymon\JWTAuth\Exceptions\PayloadException
- * @expectedExceptionMessage The payload is immutable
- */
+ /** @test */
public function it_should_throw_an_exception_when_trying_to_add_to_the_payload()
{
+ $this->expectException(PayloadException::class);
+ $this->expectExceptionMessage('The payload is immutable');
+
$this->payload['foo'] = 'bar';
}
- /**
- * @test
- * @expectedException \Tymon\JWTAuth\Exceptions\PayloadException
- * @expectedExceptionMessage The payload is immutable
- */
+ /** @test */
public function it_should_throw_an_exception_when_trying_to_remove_a_key_from_the_payload()
{
+ $this->expectException(PayloadException::class);
+ $this->expectExceptionMessage('The payload is immutable');
+
unset($this->payload['foo']);
}
/** @test */
public function it_should_cast_the_payload_to_a_string_as_json()
{
- $this->assertSame((string) $this->payload, json_encode($this->payload->get(), JSON_UNESCAPED_SLASHES));
- $this->assertJsonStringEqualsJsonString((string) $this->payload, json_encode($this->payload->get()));
+ $this->assertSame(
+ (string) $this->payload,
+ json_encode($this->payload->get(), JSON_UNESCAPED_SLASHES)
+ );
+ $this->assertJsonStringEqualsJsonString(
+ (string) $this->payload,
+ json_encode($this->payload->get())
+ );
}
/** @test */
public function it_should_allow_array_access_on_the_payload()
{
- $this->assertTrue(isset($this->payload['iat']));
- $this->assertSame($this->payload['sub'], 1);
- $this->assertArrayHasKey('exp', $this->payload);
+ $this->assertTrue(isset($this->payload[IssuedAt::NAME]));
+ $this->assertSame($this->payload[Subject::NAME], 1);
+ $this->assertArrayHasKey(Expiration::NAME, $this->payload);
}
/** @test */
public function it_should_get_properties_of_payload_via_get_method()
{
- $this->assertInternalType('array', $this->payload->get());
- $this->assertSame($this->payload->get('sub'), 1);
-
- $this->assertSame(
- $this->payload->get(function () {
- return 'jti';
- }),
- 'foo'
- );
+ $this->assertIsArray($this->payload->get());
+ $this->assertSame($this->payload->get(Subject::NAME), 1);
+ $this->assertSame($this->payload->get(JwtId::NAME), 'foo');
}
/** @test */
public function it_should_get_multiple_properties_when_passing_an_array_to_the_get_method()
{
- $values = $this->payload->get(['sub', 'jti']);
+ $values = $this->payload->get([Subject::NAME, JwtId::NAME]);
- list($sub, $jti) = $values;
+ [$sub, $jti] = $values;
- $this->assertInternalType('array', $values);
+ $this->assertIsArray($values);
$this->assertSame($sub, 1);
$this->assertSame($jti, 'foo');
}
@@ -156,9 +148,9 @@ public function it_should_invoke_the_instance_as_a_callable()
{
$payload = $this->payload;
- $sub = $payload('sub');
- $jti = $payload('jti');
- $iss = $payload('iss');
+ $sub = $payload(Subject::NAME);
+ $jti = $payload(JwtId::NAME);
+ $iss = $payload(Issuer::NAME);
$this->assertSame($sub, 1);
$this->assertSame($jti, 'foo');
@@ -167,13 +159,12 @@ public function it_should_invoke_the_instance_as_a_callable()
$this->assertSame($payload(), $this->payload->toArray());
}
- /**
- * @test
- * @expectedException \BadMethodCallException
- * @expectedExceptionMessage The claim [getFoo] does not exist on the payload.
- */
+ /** @test */
public function it_should_throw_an_exception_when_magically_getting_a_property_that_does_not_exist()
{
+ $this->expectException(BadMethodCallException::class);
+ $this->expectExceptionMessage('The claim [Foo] does not exist on the payload.');
+
$this->payload->getFoo();
}
@@ -182,9 +173,9 @@ public function it_should_get_the_claims()
{
$claims = $this->payload->getClaims();
- $this->assertInstanceOf(Expiration::class, $claims['exp']);
- $this->assertInstanceOf(JwtId::class, $claims['jti']);
- $this->assertInstanceOf(Subject::class, $claims['sub']);
+ $this->assertInstanceOf(Expiration::class, $claims[Expiration::NAME]);
+ $this->assertInstanceOf(JwtId::class, $claims[JwtId::NAME]);
+ $this->assertInstanceOf(Subject::class, $claims[Subject::NAME]);
$this->assertContainsOnlyInstancesOf(Claim::class, $claims);
}
@@ -192,7 +183,10 @@ public function it_should_get_the_claims()
/** @test */
public function it_should_get_the_object_as_json()
{
- $this->assertJsonStringEqualsJsonString(json_encode($this->payload), $this->payload->toJson());
+ $this->assertJsonStringEqualsJsonString(
+ json_encode($this->payload),
+ $this->payload->toJson()
+ );
}
/** @test */
@@ -206,7 +200,7 @@ public function it_should_count_the_claims()
public function it_should_match_values()
{
$values = $this->payload->toArray();
- $values['sub'] = (string) $values['sub'];
+ $values[Subject::NAME] = (string) $values[Subject::NAME];
$this->assertTrue($this->payload->matches($values));
}
@@ -230,7 +224,7 @@ public function it_should_not_match_empty_values()
public function it_should_not_match_values()
{
$values = $this->payload->toArray();
- $values['sub'] = 'dummy_subject';
+ $values[Subject::NAME] = 'dummy_subject';
$this->assertFalse($this->payload->matches($values));
}
@@ -239,7 +233,7 @@ public function it_should_not_match_values()
public function it_should_not_match_strict_values()
{
$values = $this->payload->toArray();
- $values['sub'] = (string) $values['sub'];
+ $values[Subject::NAME] = (string) $values[Subject::NAME];
$this->assertFalse($this->payload->matchesStrict($values));
$this->assertFalse($this->payload->matches($values, true));
diff --git a/tests/Providers/Auth/IlluminateTest.php b/tests/Providers/Auth/IlluminateTest.php
deleted file mode 100644
index 1ffad8a8b..000000000
--- a/tests/Providers/Auth/IlluminateTest.php
+++ /dev/null
@@ -1,66 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Tymon\JWTAuth\Test\Providers\Auth;
-
-use Mockery;
-use Illuminate\Contracts\Auth\Guard;
-use Tymon\JWTAuth\Test\AbstractTestCase;
-use Tymon\JWTAuth\Providers\Auth\Illuminate as Auth;
-
-class IlluminateTest extends AbstractTestCase
-{
- /**
- * @var \Mockery\MockInterface|\Illuminate\Contracts\Auth\Guard
- */
- protected $authManager;
-
- /**
- * @var \Tymon\JWTAuth\Providers\Auth\Illuminate
- */
- protected $auth;
-
- public function setUp()
- {
- parent::setUp();
-
- $this->authManager = Mockery::mock(Guard::class);
- $this->auth = new Auth($this->authManager);
- }
-
- /** @test */
- public function it_should_return_true_if_credentials_are_valid()
- {
- $this->authManager->shouldReceive('once')->once()->with(['email' => 'foo@bar.com', 'password' => 'foobar'])->andReturn(true);
- $this->assertTrue($this->auth->byCredentials(['email' => 'foo@bar.com', 'password' => 'foobar']));
- }
-
- /** @test */
- public function it_should_return_true_if_user_is_found()
- {
- $this->authManager->shouldReceive('onceUsingId')->once()->with(123)->andReturn(true);
- $this->assertTrue($this->auth->byId(123));
- }
-
- /** @test */
- public function it_should_return_false_if_user_is_not_found()
- {
- $this->authManager->shouldReceive('onceUsingId')->once()->with(123)->andReturn(false);
- $this->assertFalse($this->auth->byId(123));
- }
-
- /** @test */
- public function it_should_return_the_currently_authenticated_user()
- {
- $this->authManager->shouldReceive('user')->once()->andReturn((object) ['id' => 1]);
- $this->assertSame($this->auth->user()->id, 1);
- }
-}
diff --git a/tests/Providers/Auth/SentinelTest.php b/tests/Providers/Auth/SentinelTest.php
deleted file mode 100644
index 9b2702a39..000000000
--- a/tests/Providers/Auth/SentinelTest.php
+++ /dev/null
@@ -1,72 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Tymon\JWTAuth\Test\Providers\Auth;
-
-use Mockery;
-use Cartalyst\Sentinel\Sentinel;
-use Tymon\JWTAuth\Test\AbstractTestCase;
-use Tymon\JWTAuth\Test\Stubs\SentinelStub;
-use Tymon\JWTAuth\Providers\Auth\Sentinel as Auth;
-
-class SentinelTest extends AbstractTestCase
-{
- /**
- * @var \Mockery\MockInterface|\Cartalyst\Sentinel\Sentinel
- */
- protected $sentinel;
-
- /**
- * @var \Tymon\JWTAuth\Providers\Auth\Sentinel
- */
- protected $auth;
-
- public function setUp()
- {
- parent::setUp();
-
- $this->sentinel = Mockery::mock(Sentinel::class);
- $this->auth = new Auth($this->sentinel);
- }
-
- /** @test */
- public function it_should_return_true_if_credentials_are_valid()
- {
- $this->sentinel->shouldReceive('stateless')->once()->with(['email' => 'foo@bar.com', 'password' => 'foobar'])->andReturn(true);
- $this->assertTrue($this->auth->byCredentials(['email' => 'foo@bar.com', 'password' => 'foobar']));
- }
-
- /** @test */
- public function it_should_return_true_if_user_is_found()
- {
- $stub = new SentinelStub;
- $this->sentinel->shouldReceive('getUserRepository->findById')->once()->with(123)->andReturn($stub);
- $this->sentinel->shouldReceive('setUser')->once()->with($stub);
-
- $this->assertTrue($this->auth->byId(123));
- }
-
- /** @test */
- public function it_should_return_false_if_user_is_not_found()
- {
- $this->sentinel->shouldReceive('getUserRepository->findById')->once()->with(321)->andReturn(false);
- $this->sentinel->shouldReceive('setUser')->never();
-
- $this->assertFalse($this->auth->byId(321));
- }
-
- /** @test */
- public function it_should_return_the_currently_authenticated_user()
- {
- $this->sentinel->shouldReceive('getUser')->once()->andReturn(new SentinelStub);
- $this->assertSame($this->auth->user()->getUserId(), 123);
- }
-}
diff --git a/tests/Providers/JWT/LcobucciTest.php b/tests/Providers/JWT/LcobucciTest.php
index 2ecb5d1eb..2ba4c5bf2 100644
--- a/tests/Providers/JWT/LcobucciTest.php
+++ b/tests/Providers/JWT/LcobucciTest.php
@@ -11,14 +11,20 @@
namespace Tymon\JWTAuth\Test\Providers\JWT;
-use Mockery;
use Exception;
-use Lcobucci\JWT\Parser;
+use InvalidArgumentException;
use Lcobucci\JWT\Builder;
+use Lcobucci\JWT\Parser;
use Lcobucci\JWT\Signer\Key;
-use InvalidArgumentException;
-use Tymon\JWTAuth\Test\AbstractTestCase;
+use Mockery;
+use Tymon\JWTAuth\Claims\Expiration;
+use Tymon\JWTAuth\Claims\IssuedAt;
+use Tymon\JWTAuth\Claims\Issuer;
+use Tymon\JWTAuth\Claims\Subject;
+use Tymon\JWTAuth\Exceptions\JWTException;
+use Tymon\JWTAuth\Exceptions\TokenInvalidException;
use Tymon\JWTAuth\Providers\JWT\Lcobucci;
+use Tymon\JWTAuth\Test\AbstractTestCase;
class LcobucciTest extends AbstractTestCase
{
@@ -33,11 +39,11 @@ class LcobucciTest extends AbstractTestCase
protected $builder;
/**
- * @var \Tymon\JWTAuth\Providers\JWT\Namshi
+ * @var \Tymon\JWTAuth\Providers\JWT\Lcobucci
*/
protected $provider;
- public function setUp()
+ public function setUp(): void
{
parent::setUp();
@@ -48,30 +54,50 @@ public function setUp()
/** @test */
public function it_should_return_the_token_when_passing_a_valid_payload_to_encode()
{
- $payload = ['sub' => 1, 'exp' => $this->testNowTimestamp + 3600, 'iat' => $this->testNowTimestamp, 'iss' => '/foo'];
-
- $this->builder->shouldReceive('unsign')->once()->andReturnSelf();
+ $payload = [
+ Subject::NAME => 1,
+ Expiration::NAME => $this->testNowTimestamp + 3600,
+ IssuedAt::NAME => $this->testNowTimestamp,
+ Issuer::NAME => '/foo',
+ ];
+
+ $this->builder->shouldReceive('unsign')
+ ->once()
+ ->andReturnSelf();
$this->builder->shouldReceive('set')->times(count($payload));
- $this->builder->shouldReceive('sign')->once()->with(Mockery::any(), 'secret');
- $this->builder->shouldReceive('getToken')->once()->andReturn('foo.bar.baz');
+ $this->builder->shouldReceive('sign')
+ ->once()
+ ->with(Mockery::any(), 'secret');
+ $this->builder->shouldReceive('getToken')
+ ->once()
+ ->andReturn('foo.bar.baz');
$token = $this->getProvider('secret', 'HS256')->encode($payload);
$this->assertSame('foo.bar.baz', $token);
}
- /**
- * @test
- * @expectedException \Tymon\JWTAuth\Exceptions\JWTException
- * @expectedExceptionMessage Could not create token:
- */
+ /** @test */
public function it_should_throw_an_invalid_exception_when_the_payload_could_not_be_encoded()
{
- $payload = ['sub' => 1, 'exp' => $this->testNowTimestamp, 'iat' => $this->testNowTimestamp, 'iss' => '/foo'];
-
- $this->builder->shouldReceive('unsign')->once()->andReturnSelf();
+ $this->expectException(JWTException::class);
+ $this->expectExceptionMessage('Could not create token:');
+
+ $payload = [
+ Subject::NAME => 1,
+ Expiration::NAME => $this->testNowTimestamp,
+ IssuedAt::NAME => $this->testNowTimestamp,
+ Issuer::NAME => '/foo',
+ ];
+
+ $this->builder->shouldReceive('unsign')
+ ->once()
+ ->andReturnSelf();
$this->builder->shouldReceive('set')->times(count($payload));
- $this->builder->shouldReceive('sign')->once()->with(Mockery::any(), 'secret')->andThrow(new Exception);
+ $this->builder->shouldReceive('sign')
+ ->once()
+ ->with(Mockery::any(), 'secret')
+ ->andThrow(new Exception());
$this->getProvider('secret', 'HS256')->encode($payload);
}
@@ -79,37 +105,57 @@ public function it_should_throw_an_invalid_exception_when_the_payload_could_not_
/** @test */
public function it_should_return_the_payload_when_passing_a_valid_token_to_decode()
{
- $payload = ['sub' => 1, 'exp' => $this->testNowTimestamp + 3600, 'iat' => $this->testNowTimestamp, 'iss' => '/foo'];
-
- $this->parser->shouldReceive('parse')->once()->with('foo.bar.baz')->andReturn(Mockery::self());
- $this->parser->shouldReceive('verify')->once()->with(Mockery::any(), 'secret')->andReturn(true);
- $this->parser->shouldReceive('getClaims')->once()->andReturn($payload);
+ $payload = [
+ Subject::NAME => 1,
+ Expiration::NAME => $this->testNowTimestamp + 3600,
+ IssuedAt::NAME => $this->testNowTimestamp,
+ Issuer::NAME => '/foo',
+ ];
+
+ $this->parser->shouldReceive('parse')
+ ->once()
+ ->with('foo.bar.baz')
+ ->andReturn(Mockery::self());
+ $this->parser->shouldReceive('verify')
+ ->once()
+ ->with(Mockery::any(), 'secret')
+ ->andReturn(true);
+ $this->parser->shouldReceive('getClaims')
+ ->once()
+ ->andReturn($payload);
$this->assertSame($payload, $this->getProvider('secret', 'HS256')->decode('foo.bar.baz'));
}
- /**
- * @test
- * @expectedException \Tymon\JWTAuth\Exceptions\TokenInvalidException
- * @expectedExceptionMessage Token Signature could not be verified.
- */
+ /** @test */
public function it_should_throw_a_token_invalid_exception_when_the_token_could_not_be_decoded_due_to_a_bad_signature()
{
- $this->parser->shouldReceive('parse')->once()->with('foo.bar.baz')->andReturn(Mockery::self());
- $this->parser->shouldReceive('verify')->once()->with(Mockery::any(), 'secret')->andReturn(false);
+ $this->expectException(TokenInvalidException::class);
+ $this->expectExceptionMessage('Token Signature could not be verified.');
+
+ $this->parser->shouldReceive('parse')
+ ->once()
+ ->with('foo.bar.baz')
+ ->andReturn(Mockery::self());
+ $this->parser->shouldReceive('verify')
+ ->once()
+ ->with(Mockery::any(), 'secret')
+ ->andReturn(false);
$this->parser->shouldReceive('getClaims')->never();
$this->getProvider('secret', 'HS256')->decode('foo.bar.baz');
}
- /**
- * @test
- * @expectedException \Tymon\JWTAuth\Exceptions\TokenInvalidException
- * @expectedExceptionMessage Could not decode token:
- */
+ /** @test */
public function it_should_throw_a_token_invalid_exception_when_the_token_could_not_be_decoded()
{
- $this->parser->shouldReceive('parse')->once()->with('foo.bar.baz')->andThrow(new InvalidArgumentException);
+ $this->expectException(TokenInvalidException::class);
+ $this->expectExceptionMessage('Could not decode token:');
+
+ $this->parser->shouldReceive('parse')
+ ->once()
+ ->with('foo.bar.baz')
+ ->andThrow(new InvalidArgumentException());
$this->parser->shouldReceive('verify')->never();
$this->parser->shouldReceive('getClaims')->never();
@@ -119,60 +165,71 @@ public function it_should_throw_a_token_invalid_exception_when_the_token_could_n
/** @test */
public function it_should_generate_a_token_when_using_an_rsa_algorithm()
{
- $provider = $this->getProvider(
- 'does_not_matter',
- 'RS256',
- ['private' => $this->getDummyPrivateKey(), 'public' => $this->getDummyPublicKey()]
- );
-
- $payload = ['sub' => 1, 'exp' => $this->testNowTimestamp + 3600, 'iat' => $this->testNowTimestamp, 'iss' => '/foo'];
-
- $this->builder->shouldReceive('unsign')->once()->andReturnSelf();
+ $provider = $this->getProvider('does_not_matter', 'RS256', [
+ 'private' => $this->getDummyPrivateKey(),
+ 'public' => $this->getDummyPublicKey(),
+ ]);
+
+ $payload = [
+ Subject::NAME => 1,
+ Expiration::NAME => $this->testNowTimestamp + 3600,
+ IssuedAt::NAME => $this->testNowTimestamp,
+ Issuer::NAME => '/foo',
+ ];
+
+ $this->builder->shouldReceive('unsign')
+ ->once()
+ ->andReturnSelf();
$this->builder->shouldReceive('set')->times(count($payload));
- $this->builder->shouldReceive('sign')->once()->with(Mockery::any(), Mockery::type(Key::class));
- $this->builder->shouldReceive('getToken')->once()->andReturn('foo.bar.baz');
+ $this->builder->shouldReceive('sign')
+ ->once()
+ ->with(Mockery::any(), Mockery::type(Key::class));
+ $this->builder->shouldReceive('getToken')
+ ->once()
+ ->andReturn('foo.bar.baz');
$token = $provider->encode($payload);
$this->assertSame('foo.bar.baz', $token);
}
- /**
- * @test
- * @expectedException \Tymon\JWTAuth\Exceptions\JWTException
- * @expectedExceptionMessage The given algorithm could not be found
- */
+ /** @test */
public function it_should_throw_a_exception_when_the_algorithm_passed_is_invalid()
{
+ $this->expectException(JWTException::class);
+ $this->expectExceptionMessage('The given algorithm could not be found');
+
$this->parser->shouldReceive('parse')->never();
$this->parser->shouldReceive('verify')->never();
$this->getProvider('secret', 'AlgorithmWrong')->decode('foo.bar.baz');
}
- /**
- * @test
- */
+ /** @test */
public function it_should_return_the_public_key()
{
$provider = $this->getProvider(
'does_not_matter',
'RS256',
- $keys = ['private' => $this->getDummyPrivateKey(), 'public' => $this->getDummyPublicKey()]
+ $keys = [
+ 'private' => $this->getDummyPrivateKey(),
+ 'public' => $this->getDummyPublicKey(),
+ ]
);
$this->assertSame($keys['public'], $provider->getPublicKey());
}
- /**
- * @test
- */
+ /** @test */
public function it_should_return_the_keys()
{
$provider = $this->getProvider(
'does_not_matter',
'RS256',
- $keys = ['private' => $this->getDummyPrivateKey(), 'public' => $this->getDummyPublicKey()]
+ $keys = [
+ 'private' => $this->getDummyPrivateKey(),
+ 'public' => $this->getDummyPublicKey(),
+ ]
);
$this->assertSame($keys, $provider->getKeys());
diff --git a/tests/Providers/JWT/NamshiTest.php b/tests/Providers/JWT/NamshiTest.php
deleted file mode 100644
index d0d8f9be9..000000000
--- a/tests/Providers/JWT/NamshiTest.php
+++ /dev/null
@@ -1,224 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Tymon\JWTAuth\Test\Providers\JWT;
-
-use Mockery;
-use Exception;
-use Namshi\JOSE\JWS;
-use InvalidArgumentException;
-use Tymon\JWTAuth\Providers\JWT\Namshi;
-use Tymon\JWTAuth\Test\AbstractTestCase;
-
-class NamshiTest extends AbstractTestCase
-{
- /**
- * @var \Mockery\MockInterface
- */
- protected $jws;
-
- /**
- * @var \Tymon\JWTAuth\Providers\JWT\Namshi
- */
- protected $provider;
-
- public function setUp()
- {
- parent::setUp();
-
- $this->jws = Mockery::mock(JWS::class);
- }
-
- /** @test */
- public function it_should_return_the_token_when_passing_a_valid_payload_to_encode()
- {
- $payload = ['sub' => 1, 'exp' => $this->testNowTimestamp + 3600, 'iat' => $this->testNowTimestamp, 'iss' => '/foo'];
-
- $this->jws->shouldReceive('setPayload')->once()->with($payload)->andReturn(Mockery::self());
- $this->jws->shouldReceive('sign')->once()->with('secret', null)->andReturn(Mockery::self());
- $this->jws->shouldReceive('getTokenString')->once()->andReturn('foo.bar.baz');
-
- $token = $this->getProvider('secret', 'HS256')->encode($payload);
-
- $this->assertSame('foo.bar.baz', $token);
- }
-
- /**
- * @test
- * @expectedException \Tymon\JWTAuth\Exceptions\JWTException
- * @expectedExceptionMessage Could not create token:
- */
- public function it_should_throw_an_invalid_exception_when_the_payload_could_not_be_encoded()
- {
- $payload = ['sub' => 1, 'exp' => $this->testNowTimestamp, 'iat' => $this->testNowTimestamp, 'iss' => '/foo'];
-
- $this->jws->shouldReceive('setPayload')->once()->with($payload)->andReturn(Mockery::self());
- $this->jws->shouldReceive('sign')->andThrow(new Exception);
-
- $this->getProvider('secret', 'HS256')->encode($payload);
- }
-
- /** @test */
- public function it_should_return_the_payload_when_passing_a_valid_token_to_decode()
- {
- $payload = ['sub' => 1, 'exp' => $this->testNowTimestamp + 3600, 'iat' => $this->testNowTimestamp, 'iss' => '/foo'];
-
- $this->jws->shouldReceive('load')->once()->with('foo.bar.baz', false)->andReturn(Mockery::self());
- $this->jws->shouldReceive('verify')->once()->with('secret', 'HS256')->andReturn(true);
- $this->jws->shouldReceive('getPayload')->andReturn($payload);
-
- $this->assertSame($payload, $this->getProvider('secret', 'HS256')->decode('foo.bar.baz'));
- }
-
- /**
- * @test
- * @expectedException \Tymon\JWTAuth\Exceptions\TokenInvalidException
- * @expectedExceptionMessage Token Signature could not be verified.
- */
- public function it_should_throw_a_token_invalid_exception_when_the_token_could_not_be_decoded_due_to_a_bad_signature()
- {
- $this->jws->shouldReceive('load')->once()->with('foo.bar.baz', false)->andReturn(Mockery::self());
- $this->jws->shouldReceive('verify')->once()->with('secret', 'HS256')->andReturn(false);
- $this->jws->shouldReceive('getPayload')->never();
-
- $this->getProvider('secret', 'HS256')->decode('foo.bar.baz');
- }
-
- /**
- * @test
- * @expectedException \Tymon\JWTAuth\Exceptions\TokenInvalidException
- * @expectedExceptionMessage Could not decode token:
- */
- public function it_should_throw_a_token_invalid_exception_when_the_token_could_not_be_decoded()
- {
- $this->jws->shouldReceive('load')->once()->with('foo.bar.baz', false)->andThrow(new InvalidArgumentException);
- $this->jws->shouldReceive('verify')->never();
- $this->jws->shouldReceive('getPayload')->never();
-
- $this->getProvider('secret', 'HS256')->decode('foo.bar.baz');
- }
-
- /** @test */
- public function it_should_generate_a_token_when_using_an_rsa_algorithm()
- {
- $provider = $this->getProvider(
- 'does_not_matter',
- 'RS256',
- ['private' => $this->getDummyPrivateKey(), 'public' => $this->getDummyPublicKey()]
- );
-
- $payload = ['sub' => 1, 'exp' => $this->testNowTimestamp + 3600, 'iat' => $this->testNowTimestamp, 'iss' => '/foo'];
-
- $this->jws->shouldReceive('setPayload')->once()->with($payload)->andReturn(Mockery::self());
- $this->jws->shouldReceive('sign')->once()->with($this->getDummyPrivateKey(), null)->andReturn(Mockery::self());
- $this->jws->shouldReceive('getTokenString')->once()->andReturn('foo.bar.baz');
-
- $token = $provider->encode($payload);
-
- $this->assertSame('foo.bar.baz', $token);
- }
-
- /** @test */
- public function it_should_generate_a_token_when_using_an_ecdsa_algorithm()
- {
- $provider = $this->getProvider(
- 'does_not_matter',
- 'ES256',
- ['private' => $this->getDummyPrivateKey(), 'public' => $this->getDummyPublicKey()]
- );
-
- $payload = ['sub' => 1, 'exp' => $this->testNowTimestamp + 3600, 'iat' => $this->testNowTimestamp, 'iss' => '/foo'];
-
- $this->jws->shouldReceive('setPayload')->once()->with($payload)->andReturn(Mockery::self());
- $this->jws->shouldReceive('sign')->once()->with($this->getDummyPrivateKey(), null)->andReturn(Mockery::self());
- $this->jws->shouldReceive('getTokenString')->once()->andReturn('foo.bar.baz');
-
- $token = $provider->encode($payload);
-
- $this->assertSame('foo.bar.baz', $token);
- }
-
- /** @test */
- public function it_should_decode_a_token_when_using_an_rsa_algorithm()
- {
- $provider = $this->getProvider(
- 'does_not_matter',
- 'RS256',
- ['private' => $this->getDummyPrivateKey(), 'public' => $this->getDummyPublicKey()]
- );
-
- $payload = ['sub' => 1, 'exp' => $this->testNowTimestamp + 3600, 'iat' => $this->testNowTimestamp, 'iss' => '/foo'];
-
- $this->jws->shouldReceive('setPayload')->once()->with($payload)->andReturn(Mockery::self());
- $this->jws->shouldReceive('sign')->once()->with($this->getDummyPrivateKey(), null)->andReturn(Mockery::self());
- $this->jws->shouldReceive('getTokenString')->once()->andReturn('foo.bar.baz');
-
- $token = $provider->encode($payload);
-
- $this->assertSame('foo.bar.baz', $token);
- }
-
- /**
- * @test
- * @expectedException \Tymon\JWTAuth\Exceptions\JWTException
- * @expectedExceptionMessage The given algorithm could not be found
- */
- public function it_should_throw_a_exception_when_the_algorithm_passed_is_invalid()
- {
- $this->jws->shouldReceive('load')->once()->with('foo.bar.baz', false)->andReturn(Mockery::self());
- $this->jws->shouldReceive('verify')->with('secret', 'AlgorithmWrong')->andReturn(true);
-
- $this->getProvider('secret', 'AlgorithmWrong')->decode('foo.bar.baz');
- }
-
- /**
- * @test
- */
- public function it_should_return_the_public_key()
- {
- $provider = $this->getProvider(
- 'does_not_matter',
- 'RS256',
- $keys = ['private' => $this->getDummyPrivateKey(), 'public' => $this->getDummyPublicKey()]
- );
-
- $this->assertSame($keys['public'], $provider->getPublicKey());
- }
-
- /**
- * @test
- */
- public function it_should_return_the_keys()
- {
- $provider = $this->getProvider(
- 'does_not_matter',
- 'RS256',
- $keys = ['private' => $this->getDummyPrivateKey(), 'public' => $this->getDummyPublicKey()]
- );
-
- $this->assertSame($keys, $provider->getKeys());
- }
-
- public function getProvider($secret, $algo, array $keys = [])
- {
- return new Namshi($this->jws, $secret, $algo, $keys);
- }
-
- public function getDummyPrivateKey()
- {
- return file_get_contents(__DIR__.'/../Keys/id_rsa');
- }
-
- public function getDummyPublicKey()
- {
- return file_get_contents(__DIR__.'/../Keys/id_rsa.pub');
- }
-}
diff --git a/tests/Providers/JWT/ProviderTest.php b/tests/Providers/JWT/ProviderTest.php
index 89a1018e9..be4942d05 100644
--- a/tests/Providers/JWT/ProviderTest.php
+++ b/tests/Providers/JWT/ProviderTest.php
@@ -21,7 +21,7 @@ class ProviderTest extends AbstractTestCase
*/
protected $provider;
- public function setUp()
+ public function setUp(): void
{
parent::setUp();
diff --git a/tests/Providers/Storage/IlluminateTest.php b/tests/Providers/Storage/IlluminateTest.php
index 80455be3c..e985f4f5a 100644
--- a/tests/Providers/Storage/IlluminateTest.php
+++ b/tests/Providers/Storage/IlluminateTest.php
@@ -11,11 +11,11 @@
namespace Tymon\JWTAuth\Test\Providers\Storage;
+use Illuminate\Contracts\Cache\Repository;
use Mockery;
+use Tymon\JWTAuth\Providers\Storage\Illuminate as Storage;
use Tymon\JWTAuth\Test\AbstractTestCase;
-use Illuminate\Contracts\Cache\Repository;
use Tymon\JWTAuth\Test\Stubs\TaggedStorage;
-use Tymon\JWTAuth\Providers\Storage\Illuminate as Storage;
class IlluminateTest extends AbstractTestCase
{
@@ -29,7 +29,7 @@ class IlluminateTest extends AbstractTestCase
*/
protected $storage;
- public function setUp()
+ public function setUp(): void
{
parent::setUp();
@@ -40,7 +40,9 @@ public function setUp()
/** @test */
public function it_should_add_the_item_to_storage()
{
- $this->cache->shouldReceive('put')->with('foo', 'bar', 10)->once();
+ $this->cache->shouldReceive('put')
+ ->with('foo', 'bar', 10)
+ ->once();
$this->storage->add('foo', 'bar', 10);
}
@@ -48,7 +50,9 @@ public function it_should_add_the_item_to_storage()
/** @test */
public function it_should_add_the_item_to_storage_forever()
{
- $this->cache->shouldReceive('forever')->with('foo', 'bar')->once();
+ $this->cache->shouldReceive('forever')
+ ->with('foo', 'bar')
+ ->once();
$this->storage->forever('foo', 'bar');
}
@@ -56,7 +60,10 @@ public function it_should_add_the_item_to_storage_forever()
/** @test */
public function it_should_get_an_item_from_storage()
{
- $this->cache->shouldReceive('get')->with('foo')->once()->andReturn(['foo' => 'bar']);
+ $this->cache->shouldReceive('get')
+ ->with('foo')
+ ->once()
+ ->andReturn(['foo' => 'bar']);
$this->assertSame(['foo' => 'bar'], $this->storage->get('foo'));
}
@@ -64,15 +71,19 @@ public function it_should_get_an_item_from_storage()
/** @test */
public function it_should_remove_the_item_from_storage()
{
- $this->cache->shouldReceive('forget')->with('foo')->once()->andReturn(true);
+ $this->cache->shouldReceive('forget')
+ ->with('foo')
+ ->once();
- $this->assertTrue($this->storage->destroy('foo'));
+ $this->storage->destroy('foo');
}
/** @test */
public function it_should_remove_all_items_from_storage()
{
- $this->cache->shouldReceive('flush')->withNoArgs()->once();
+ $this->cache->shouldReceive('flush')
+ ->withNoArgs()
+ ->once();
$this->storage->flush();
}
@@ -89,14 +100,19 @@ private function emulateTags()
{
$this->storage = new TaggedStorage($this->cache);
- $this->cache->shouldReceive('tags')->with('tymon.jwt')->once()->andReturn(Mockery::self());
+ $this->cache->shouldReceive('tags')
+ ->with('tymon.jwt')
+ ->once()
+ ->andReturn(Mockery::self());
}
/** @test */
public function it_should_add_the_item_to_tagged_storage()
{
$this->emulateTags();
- $this->cache->shouldReceive('put')->with('foo', 'bar', 10)->once();
+ $this->cache->shouldReceive('put')
+ ->with('foo', 'bar', 10)
+ ->once();
$this->storage->add('foo', 'bar', 10);
}
@@ -105,7 +121,9 @@ public function it_should_add_the_item_to_tagged_storage()
public function it_should_add_the_item_to_tagged_storage_forever()
{
$this->emulateTags();
- $this->cache->shouldReceive('forever')->with('foo', 'bar')->once();
+ $this->cache->shouldReceive('forever')
+ ->with('foo', 'bar')
+ ->once();
$this->storage->forever('foo', 'bar');
}
@@ -114,7 +132,10 @@ public function it_should_add_the_item_to_tagged_storage_forever()
public function it_should_get_an_item_from_tagged_storage()
{
$this->emulateTags();
- $this->cache->shouldReceive('get')->with('foo')->once()->andReturn(['foo' => 'bar']);
+ $this->cache->shouldReceive('get')
+ ->with('foo')
+ ->once()
+ ->andReturn(['foo' => 'bar']);
$this->assertSame(['foo' => 'bar'], $this->storage->get('foo'));
}
@@ -123,16 +144,20 @@ public function it_should_get_an_item_from_tagged_storage()
public function it_should_remove_the_item_from_tagged_storage()
{
$this->emulateTags();
- $this->cache->shouldReceive('forget')->with('foo')->once()->andReturn(true);
+ $this->cache->shouldReceive('forget')
+ ->with('foo')
+ ->once();
- $this->assertTrue($this->storage->destroy('foo'));
+ $this->storage->destroy('foo');
}
/** @test */
public function it_should_remove_all_tagged_items_from_storage()
{
$this->emulateTags();
- $this->cache->shouldReceive('flush')->withNoArgs()->once();
+ $this->cache->shouldReceive('flush')
+ ->withNoArgs()
+ ->once();
$this->storage->flush();
}
diff --git a/tests/Stubs/JWTProviderStub.php b/tests/Stubs/JWTProviderStub.php
index ef47d9136..58541240a 100644
--- a/tests/Stubs/JWTProviderStub.php
+++ b/tests/Stubs/JWTProviderStub.php
@@ -18,8 +18,24 @@ class JWTProviderStub extends Provider
/**
* {@inheritdoc}
*/
- protected function isAsymmetric()
+ protected function isAsymmetric(): bool
{
return false;
}
+
+ /**
+ * Create a JSON Web Token.
+ */
+ public function encode(array $payload): string
+ {
+ return 'foo.bar.baz';
+ }
+
+ /**
+ * Decode a JSON Web Token.
+ */
+ public function decode(string $token): array
+ {
+ return ['foo' => 'bar'];
+ }
}
diff --git a/tests/Stubs/LaravelUserStub.php b/tests/Stubs/LaravelUserStub.php
index 616ddbfd2..97ca3e9f9 100644
--- a/tests/Stubs/LaravelUserStub.php
+++ b/tests/Stubs/LaravelUserStub.php
@@ -11,8 +11,8 @@
namespace Tymon\JWTAuth\Test\Stubs;
-use Tymon\JWTAuth\Contracts\JWTSubject;
use Illuminate\Contracts\Auth\Authenticatable;
+use Tymon\JWTAuth\Contracts\JWTSubject;
class LaravelUserStub extends UserStub implements Authenticatable, JWTSubject
{
diff --git a/tests/Stubs/SentinelStub.php b/tests/Stubs/SentinelStub.php
deleted file mode 100644
index 51517db6d..000000000
--- a/tests/Stubs/SentinelStub.php
+++ /dev/null
@@ -1,37 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Tymon\JWTAuth\Test\Stubs;
-
-use Cartalyst\Sentinel\Users\UserInterface;
-
-class SentinelStub implements UserInterface
-{
- public function getUserId()
- {
- return 123;
- }
-
- public function getUserLogin()
- {
- return 'foo';
- }
-
- public function getUserLoginName()
- {
- return 'bar';
- }
-
- public function getUserPassword()
- {
- return 'baz';
- }
-}
diff --git a/tests/Stubs/TaggedStorage.php b/tests/Stubs/TaggedStorage.php
index e1b73c987..80671171a 100644
--- a/tests/Stubs/TaggedStorage.php
+++ b/tests/Stubs/TaggedStorage.php
@@ -20,5 +20,5 @@ class TaggedStorage extends Storage
// aren't defined in the interface. Getting those conditionals to behave as expected
// would be a lot of finicky work compared to verifying their functionality by hand.
// So instead we'll just set this value manually...
- protected $supportsTags = true;
+ protected ?bool $supportsTags = true;
}
diff --git a/tests/TokenTest.php b/tests/TokenTest.php
index ca44ce7fb..034f7b870 100644
--- a/tests/TokenTest.php
+++ b/tests/TokenTest.php
@@ -20,7 +20,7 @@ class TokenTest extends AbstractTestCase
*/
protected $token;
- public function setUp()
+ public function setUp(): void
{
parent::setUp();
@@ -36,6 +36,6 @@ public function it_should_return_the_token_when_casting_to_a_string()
/** @test */
public function it_should_return_the_token_when_calling_get_method()
{
- $this->assertInternalType('string', $this->token->get());
+ $this->assertIsString($this->token->get());
}
}
diff --git a/tests/Validators/PayloadValidatorTest.php b/tests/Validators/PayloadValidatorTest.php
index 3166de2b6..8c038e666 100644
--- a/tests/Validators/PayloadValidatorTest.php
+++ b/tests/Validators/PayloadValidatorTest.php
@@ -11,36 +11,28 @@
namespace Tymon\JWTAuth\Test\Validators;
-use Tymon\JWTAuth\Claims\JwtId;
-use Tymon\JWTAuth\Claims\Issuer;
-use Tymon\JWTAuth\Claims\Subject;
-use Tymon\JWTAuth\Claims\IssuedAt;
-use Tymon\JWTAuth\Claims\NotBefore;
use Tymon\JWTAuth\Claims\Collection;
use Tymon\JWTAuth\Claims\Expiration;
+use Tymon\JWTAuth\Claims\IssuedAt;
+use Tymon\JWTAuth\Claims\Issuer;
+use Tymon\JWTAuth\Claims\JwtId;
+use Tymon\JWTAuth\Claims\NotBefore;
+use Tymon\JWTAuth\Claims\Subject;
+use Tymon\JWTAuth\Exceptions\InvalidClaimException;
+use Tymon\JWTAuth\Exceptions\TokenExpiredException;
+use Tymon\JWTAuth\Exceptions\TokenInvalidException;
+use Tymon\JWTAuth\Options;
use Tymon\JWTAuth\Test\AbstractTestCase;
use Tymon\JWTAuth\Validators\PayloadValidator;
class PayloadValidatorTest extends AbstractTestCase
{
- /**
- * @var \Tymon\JWTAuth\Validators\PayloadValidator
- */
- protected $validator;
-
- public function setUp()
- {
- parent::setUp();
-
- $this->validator = new PayloadValidator;
- }
-
/** @test */
public function it_should_return_true_when_providing_a_valid_payload()
{
$claims = [
new Subject(1),
- new Issuer('http://example.com'),
+ new Issuer('example.com'),
new Expiration($this->testNowTimestamp + 3600),
new NotBefore($this->testNowTimestamp),
new IssuedAt($this->testNowTimestamp),
@@ -49,19 +41,18 @@ public function it_should_return_true_when_providing_a_valid_payload()
$collection = Collection::make($claims);
- $this->assertTrue($this->validator->isValid($collection));
+ $this->assertTrue(PayloadValidator::isValid($collection));
}
- /**
- * @test
- * @expectedException \Tymon\JWTAuth\Exceptions\TokenExpiredException
- * @expectedExceptionMessage Token has expired
- */
+ /** @test */
public function it_should_throw_an_exception_when_providing_an_expired_payload()
{
+ $this->expectException(TokenExpiredException::class);
+ $this->expectExceptionMessage('Token has expired');
+
$claims = [
new Subject(1),
- new Issuer('http://example.com'),
+ new Issuer('example.com'),
new Expiration($this->testNowTimestamp - 1440),
new NotBefore($this->testNowTimestamp - 3660),
new IssuedAt($this->testNowTimestamp - 3660),
@@ -70,19 +61,18 @@ public function it_should_throw_an_exception_when_providing_an_expired_payload()
$collection = Collection::make($claims);
- $this->validator->check($collection);
+ PayloadValidator::check($collection);
}
- /**
- * @test
- * @expectedException \Tymon\JWTAuth\Exceptions\InvalidClaimException
- * @expectedExceptionMessage Invalid value provided for claim [nbf]
- */
+ /** @test */
public function it_should_throw_an_exception_when_providing_an_invalid_nbf_claim()
{
+ $this->expectException(TokenInvalidException::class);
+ $this->expectExceptionMessage('Not Before (nbf) timestamp cannot be in the future');
+
$claims = [
new Subject(1),
- new Issuer('http://example.com'),
+ new Issuer('example.com'),
new Expiration($this->testNowTimestamp + 1440),
new NotBefore($this->testNowTimestamp + 3660),
new IssuedAt($this->testNowTimestamp - 3660),
@@ -91,19 +81,18 @@ public function it_should_throw_an_exception_when_providing_an_invalid_nbf_claim
$collection = Collection::make($claims);
- $this->validator->check($collection);
+ PayloadValidator::check($collection);
}
- /**
- * @test
- * @expectedException \Tymon\JWTAuth\Exceptions\InvalidClaimException
- * @expectedExceptionMessage Invalid value provided for claim [iat]
- */
+ /** @test */
public function it_should_throw_an_exception_when_providing_an_invalid_iat_claim()
{
+ $this->expectException(InvalidClaimException::class);
+ $this->expectExceptionMessage('Invalid value provided for claim [iat]');
+
$claims = [
new Subject(1),
- new Issuer('http://example.com'),
+ new Issuer('example.com'),
new Expiration($this->testNowTimestamp + 1440),
new NotBefore($this->testNowTimestamp - 3660),
new IssuedAt($this->testNowTimestamp + 3660),
@@ -112,33 +101,28 @@ public function it_should_throw_an_exception_when_providing_an_invalid_iat_claim
$collection = Collection::make($claims);
- $this->validator->check($collection);
+ PayloadValidator::check($collection);
}
- /**
- * @test
- * @expectedException \Tymon\JWTAuth\Exceptions\TokenInvalidException
- * @expectedExceptionMessage JWT payload does not contain the required claims
- */
+ /** @test */
public function it_should_throw_an_exception_when_providing_an_invalid_payload()
{
- $claims = [
- new Subject(1),
- new Issuer('http://example.com'),
- ];
+ $this->expectException(TokenInvalidException::class);
+ $this->expectExceptionMessage('JWT does not contain the required claims');
+
+ $claims = [new Subject(1), new Issuer('http://example.com')];
$collection = Collection::make($claims);
- $this->validator->check($collection);
+ PayloadValidator::check($collection, new Options(['required_claims' => ['foo']]));
}
- /**
- * @test
- * @expectedException \Tymon\JWTAuth\Exceptions\InvalidClaimException
- * @expectedExceptionMessage Invalid value provided for claim [exp]
- */
+ /** @test */
public function it_should_throw_an_exception_when_providing_an_invalid_expiry()
{
+ $this->expectException(InvalidClaimException::class);
+ $this->expectExceptionMessage('Invalid value provided for claim [exp]');
+
$claims = [
new Subject(1),
new Issuer('http://example.com'),
@@ -150,78 +134,20 @@ public function it_should_throw_an_exception_when_providing_an_invalid_expiry()
$collection = Collection::make($claims);
- $this->validator->check($collection);
+ PayloadValidator::check($collection);
}
/** @test */
public function it_should_set_the_required_claims()
{
- $claims = [
- new Subject(1),
- new Issuer('http://example.com'),
- ];
-
- $collection = Collection::make($claims);
-
- $this->assertTrue($this->validator->setRequiredClaims(['iss', 'sub'])->isValid($collection));
- }
-
- /** @test */
- public function it_should_check_the_token_in_the_refresh_context()
- {
- $claims = [
- new Subject(1),
- new Issuer('http://example.com'),
- new Expiration($this->testNowTimestamp - 1000),
- new NotBefore($this->testNowTimestamp),
- new IssuedAt($this->testNowTimestamp - 2600), // this is LESS than the refresh ttl at 1 hour
- new JwtId('foo'),
- ];
-
- $collection = Collection::make($claims);
-
- $this->assertTrue(
- $this->validator->setRefreshFlow()->setRefreshTTL(60)->isValid($collection)
- );
- }
-
- /** @test */
- public function it_should_return_true_if_the_refresh_ttl_is_null()
- {
- $claims = [
- new Subject(1),
- new Issuer('http://example.com'),
- new Expiration($this->testNowTimestamp - 1000),
- new NotBefore($this->testNowTimestamp),
- new IssuedAt($this->testNowTimestamp - 2600), // this is LESS than the refresh ttl at 1 hour
- new JwtId('foo'),
- ];
+ $claims = [new Subject(1), new Issuer('http://example.com')];
$collection = Collection::make($claims);
$this->assertTrue(
- $this->validator->setRefreshFlow()->setRefreshTTL(null)->isValid($collection)
+ PayloadValidator::isValid($collection, new Options([
+ 'required_claims' => [Issuer::NAME, Subject::NAME],
+ ]))
);
}
-
- /**
- * @test
- * @expectedException \Tymon\JWTAuth\Exceptions\TokenExpiredException
- * @expectedExceptionMessage Token has expired and can no longer be refreshed
- */
- public function it_should_throw_an_exception_if_the_token_cannot_be_refreshed()
- {
- $claims = [
- new Subject(1),
- new Issuer('http://example.com'),
- new Expiration($this->testNowTimestamp),
- new NotBefore($this->testNowTimestamp),
- new IssuedAt($this->testNowTimestamp - 5000), // this is MORE than the refresh ttl at 1 hour, so is invalid
- new JwtId('foo'),
- ];
-
- $collection = Collection::make($claims);
-
- $this->validator->setRefreshFlow()->setRefreshTTL(60)->check($collection);
- }
}
diff --git a/tests/Validators/TokenValidatorTest.php b/tests/Validators/TokenValidatorTest.php
index 657fd6198..2842888e5 100644
--- a/tests/Validators/TokenValidatorTest.php
+++ b/tests/Validators/TokenValidatorTest.php
@@ -11,40 +11,16 @@
namespace Tymon\JWTAuth\Test\Validators;
+use Tymon\JWTAuth\Exceptions\TokenInvalidException;
use Tymon\JWTAuth\Test\AbstractTestCase;
use Tymon\JWTAuth\Validators\TokenValidator;
class TokenValidatorTest extends AbstractTestCase
{
- /**
- * @var \Tymon\JWTAuth\Validators\TokenValidator
- */
- protected $validator;
-
- public function setUp()
- {
- parent::setUp();
-
- $this->validator = new TokenValidator;
- }
-
/** @test */
public function it_should_return_true_when_providing_a_well_formed_token()
{
- $this->assertTrue($this->validator->isValid('one.two.three'));
- }
-
- public function dataProviderMalformedTokens()
- {
- return [
- ['one.two.'],
- ['.two.'],
- ['.two.three'],
- ['one..three'],
- ['..'],
- [' . . '],
- [' one . two . three '],
- ];
+ $this->assertTrue(TokenValidator::isValid('one.two.three'));
}
/**
@@ -55,7 +31,7 @@ public function dataProviderMalformedTokens()
*/
public function it_should_return_false_when_providing_a_malformed_token($token)
{
- $this->assertFalse($this->validator->isValid($token));
+ $this->assertFalse(TokenValidator::isValid($token));
}
/**
@@ -63,21 +39,18 @@ public function it_should_return_false_when_providing_a_malformed_token($token)
* @dataProvider \Tymon\JWTAuth\Test\Validators\TokenValidatorTest::dataProviderMalformedTokens
*
* @param string $token
- * @expectedException \Tymon\JWTAuth\Exceptions\TokenInvalidException
- * @expectedExceptionMessage Malformed token
*/
public function it_should_throw_an_exception_when_providing_a_malformed_token($token)
{
- $this->validator->check($token);
+ $this->expectException(TokenInvalidException::class);
+ $this->expectExceptionMessage('Malformed token');
+
+ TokenValidator::check($token);
}
public function dataProviderTokensWithWrongSegmentsNumber()
{
- return [
- ['one.two'],
- ['one.two.three.four'],
- ['one.two.three.four.five'],
- ];
+ return [['one.two'], ['one.two.three.four'], ['one.two.three.four.five']];
}
/**
@@ -86,9 +59,10 @@ public function dataProviderTokensWithWrongSegmentsNumber()
*
* @param string $token
*/
- public function it_should_return_false_when_providing_a_token_with_wrong_segments_number($token)
- {
- $this->assertFalse($this->validator->isValid($token));
+ public function it_should_return_false_when_providing_a_token_with_wrong_segments_number(
+ $token
+ ) {
+ $this->assertFalse(TokenValidator::isValid($token));
}
/**
@@ -96,11 +70,26 @@ public function it_should_return_false_when_providing_a_token_with_wrong_segment
* @dataProvider \Tymon\JWTAuth\Test\Validators\TokenValidatorTest::dataProviderTokensWithWrongSegmentsNumber
*
* @param string $token
- * @expectedException \Tymon\JWTAuth\Exceptions\TokenInvalidException
- * @expectedExceptionMessage Wrong number of segments
*/
- public function it_should_throw_an_exception_when_providing_a_malformed_token_with_wrong_segments_number($token)
+ public function it_should_throw_an_exception_when_providing_a_malformed_token_with_wrong_segments_number(
+ $token
+ ) {
+ $this->expectException(TokenInvalidException::class);
+ $this->expectExceptionMessage('Wrong number of segments');
+
+ TokenValidator::check($token);
+ }
+
+ public function dataProviderMalformedTokens()
{
- $this->validator->check($token);
+ return [
+ ['one.two.'],
+ ['.two.'],
+ ['.two.three'],
+ ['one..three'],
+ ['..'],
+ [' . . '],
+ [' one . two . three '],
+ ];
}
}