Initial commit
This commit is contained in:
commit
13d763a2d4
21
LICENSE
Normal file
21
LICENSE
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2014 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
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
40
README.md
Normal file
40
README.md
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
# jwt-auth
|
||||||
|
|
||||||
|
> JSON Web Token Authentication for Laravel
|
||||||
|
|
||||||
|
[![Build Status](http://img.shields.io/travis/tymondesigns/jwt-auth/master.svg?style=flat-square)](https://travis-ci.org/tymondesigns/jwt-auth)
|
||||||
|
[![Scrutinizer Code Quality](http://img.shields.io/scrutinizer/g/tymondesigns/jwt-auth.svg?style=flat-square)](https://scrutinizer-ci.com/g/tymondesigns/jwt-auth/)
|
||||||
|
[![Coverage Status](https://img.shields.io/scrutinizer/coverage/g/tymondesigns/jwt-auth.svg?style=flat-square)](https://scrutinizer-ci.com/g/tymondesigns/jwt-auth/code-structure)
|
||||||
|
[![StyleCI](https://styleci.io/repos/23680678/shield?style=flat-square)](https://styleci.io/repos/23680678)
|
||||||
|
[![HHVM](https://img.shields.io/hhvm/tymon/jwt-auth.svg?style=flat-square)](http://hhvm.h4cc.de/package/tymon/jwt-auth)
|
||||||
|
[![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)
|
||||||
|
|
||||||
|
See the [WIKI](https://github.com/tymondesigns/jwt-auth/wiki) for documentation
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2014 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
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
|
||||||
|
[![Gratipay](https://img.shields.io/gratipay/tymondesigns.svg?style=flat-square)](https://gratipay.com/~tymondesigns)
|
51
composer.json
Normal file
51
composer.json
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
{
|
||||||
|
"name": "tymon/jwt-auth",
|
||||||
|
"description": "JSON Web Token Authentication for Laravel 4 and 5",
|
||||||
|
"keywords": [
|
||||||
|
"jwt",
|
||||||
|
"auth",
|
||||||
|
"authentication",
|
||||||
|
"tymon",
|
||||||
|
"laravel",
|
||||||
|
"json web token"
|
||||||
|
],
|
||||||
|
"homepage": "https://github.com/tymondesigns/jwt-auth",
|
||||||
|
"license": "MIT",
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Sean Tymon",
|
||||||
|
"email": "tymon148@gmail.com",
|
||||||
|
"homepage": "http://tymondesigns.com",
|
||||||
|
"role": "Developer"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"require": {
|
||||||
|
"php": ">=5.4.0",
|
||||||
|
"illuminate/support": "~5.0",
|
||||||
|
"illuminate/http": "~5.0",
|
||||||
|
"namshi/jose": "^5.0 || ^7.0",
|
||||||
|
"nesbot/carbon": "~1.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"phpunit/phpunit": "4.*",
|
||||||
|
"mockery/mockery": "0.9.*",
|
||||||
|
"illuminate/auth": "~5.0",
|
||||||
|
"illuminate/database": "~5.0",
|
||||||
|
"illuminate/console" : "~5.0"
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Tymon\\JWTAuth\\": "src"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload-dev": {
|
||||||
|
"psr-4": {
|
||||||
|
"Tymon\\JWTAuth\\Test\\": "tests"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"extra": {
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-develop": "0.5-dev"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
34
phpunit.xml.dist
Normal file
34
phpunit.xml.dist
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<phpunit bootstrap="vendor/autoload.php"
|
||||||
|
backupGlobals="false"
|
||||||
|
backupStaticAttributes="false"
|
||||||
|
colors="true"
|
||||||
|
verbose="true"
|
||||||
|
convertErrorsToExceptions="true"
|
||||||
|
convertNoticesToExceptions="true"
|
||||||
|
convertWarningsToExceptions="true"
|
||||||
|
processIsolation="false"
|
||||||
|
stopOnFailure="false">
|
||||||
|
<testsuites>
|
||||||
|
<testsuite name="League Test Suite">
|
||||||
|
<directory>tests</directory>
|
||||||
|
</testsuite>
|
||||||
|
</testsuites>
|
||||||
|
<filter>
|
||||||
|
<whitelist>
|
||||||
|
<directory suffix=".php">src/</directory>
|
||||||
|
<exclude>
|
||||||
|
<file>src/Providers/JWTAuthServiceProvider.php</file>
|
||||||
|
<directory suffix=".php">src/config/</directory>
|
||||||
|
<directory suffix=".php">src/Facades/</directory>
|
||||||
|
</exclude>
|
||||||
|
</whitelist>
|
||||||
|
</filter>
|
||||||
|
<logging>
|
||||||
|
<log type="tap" target="build/report.tap"/>
|
||||||
|
<log type="junit" target="build/report.junit.xml"/>
|
||||||
|
<log type="coverage-html" target="build/coverage" charset="UTF-8" yui="true" highlight="true"/>
|
||||||
|
<log type="coverage-text" target="build/coverage.txt"/>
|
||||||
|
<log type="coverage-clover" target="build/logs/clover.xml"/>
|
||||||
|
</logging>
|
||||||
|
</phpunit>
|
113
src/Blacklist.php
Normal file
113
src/Blacklist.php
Normal file
|
@ -0,0 +1,113 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of jwt-auth.
|
||||||
|
*
|
||||||
|
* (c) Sean Tymon <tymon148@gmail.com>
|
||||||
|
*
|
||||||
|
* 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\Providers\Storage\StorageInterface;
|
||||||
|
|
||||||
|
class Blacklist
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var \Tymon\JWTAuth\Providers\Storage\StorageInterface
|
||||||
|
*/
|
||||||
|
protected $storage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Number of minutes from issue date in which a JWT can be refreshed.
|
||||||
|
*
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
protected $refreshTTL = 20160;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param \Tymon\JWTAuth\Providers\Storage\StorageInterface $storage
|
||||||
|
*/
|
||||||
|
public function __construct(StorageInterface $storage)
|
||||||
|
{
|
||||||
|
$this->storage = $storage;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the token (jti claim) to the blacklist.
|
||||||
|
*
|
||||||
|
* @param \Tymon\JWTAuth\Payload $payload
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function add(Payload $payload)
|
||||||
|
{
|
||||||
|
$exp = Utils::timestamp($payload['exp']);
|
||||||
|
$refreshExp = Utils::timestamp($payload['iat'])->addMinutes($this->refreshTTL);
|
||||||
|
|
||||||
|
// there is no need to add the token to the blacklist
|
||||||
|
// if the token has already expired AND the refresh_ttl
|
||||||
|
// has gone by
|
||||||
|
if ($exp->isPast() && $refreshExp->isPast()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the cache entry's lifetime to be equal to the amount
|
||||||
|
// of refreshable time it has remaining (which is the larger
|
||||||
|
// of `exp` and `iat+refresh_ttl`), rounded up a minute
|
||||||
|
$cacheLifetime = $exp->max($refreshExp)->addMinute()->diffInMinutes();
|
||||||
|
|
||||||
|
$this->storage->add($payload['jti'], [], $cacheLifetime);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine whether the token has been blacklisted.
|
||||||
|
*
|
||||||
|
* @param \Tymon\JWTAuth\Payload $payload
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function has(Payload $payload)
|
||||||
|
{
|
||||||
|
return $this->storage->has($payload['jti']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the token (jti claim) from the blacklist.
|
||||||
|
*
|
||||||
|
* @param \Tymon\JWTAuth\Payload $payload
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function remove(Payload $payload)
|
||||||
|
{
|
||||||
|
return $this->storage->destroy($payload['jti']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove all tokens from the blacklist.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function clear()
|
||||||
|
{
|
||||||
|
$this->storage->flush();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the refresh time limit.
|
||||||
|
*
|
||||||
|
* @param int
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setRefreshTTL($ttl)
|
||||||
|
{
|
||||||
|
$this->refreshTTL = (int) $ttl;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
}
|
22
src/Claims/Audience.php
Normal file
22
src/Claims/Audience.php
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of jwt-auth.
|
||||||
|
*
|
||||||
|
* (c) Sean Tymon <tymon148@gmail.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Tymon\JWTAuth\Claims;
|
||||||
|
|
||||||
|
class Audience extends Claim
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The claim name.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $name = 'aud';
|
||||||
|
}
|
121
src/Claims/Claim.php
Normal file
121
src/Claims/Claim.php
Normal file
|
@ -0,0 +1,121 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of jwt-auth.
|
||||||
|
*
|
||||||
|
* (c) Sean Tymon <tymon148@gmail.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Tymon\JWTAuth\Claims;
|
||||||
|
|
||||||
|
use Tymon\JWTAuth\Exceptions\InvalidClaimException;
|
||||||
|
|
||||||
|
abstract class Claim implements ClaimInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The claim name.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The claim value.
|
||||||
|
*
|
||||||
|
* @var mixed
|
||||||
|
*/
|
||||||
|
private $value;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param mixed $value
|
||||||
|
*/
|
||||||
|
public function __construct($value)
|
||||||
|
{
|
||||||
|
$this->setValue($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the claim value, and call a validate method if available.
|
||||||
|
*
|
||||||
|
* @param $value
|
||||||
|
* @throws \Tymon\JWTAuth\Exceptions\InvalidClaimException
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setValue($value)
|
||||||
|
{
|
||||||
|
if (! $this->validate($value)) {
|
||||||
|
throw new InvalidClaimException('Invalid value provided for claim "'.$this->getName().'": '.$value);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->value = $value;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the claim value.
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function getValue()
|
||||||
|
{
|
||||||
|
return $this->value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the claim name.
|
||||||
|
*
|
||||||
|
* @param string $name
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setName($name)
|
||||||
|
{
|
||||||
|
$this->name = $name;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the claim name.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getName()
|
||||||
|
{
|
||||||
|
return $this->name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate the Claim value.
|
||||||
|
*
|
||||||
|
* @param $value
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
protected function validate($value)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build a key value array comprising of the claim name and value.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function toArray()
|
||||||
|
{
|
||||||
|
return [$this->getName() => $this->getValue()];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the claim as a string.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function __toString()
|
||||||
|
{
|
||||||
|
return json_encode($this->toArray(), JSON_UNESCAPED_SLASHES);
|
||||||
|
}
|
||||||
|
}
|
45
src/Claims/ClaimInterface.php
Normal file
45
src/Claims/ClaimInterface.php
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of jwt-auth.
|
||||||
|
*
|
||||||
|
* (c) Sean Tymon <tymon148@gmail.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Tymon\JWTAuth\Claims;
|
||||||
|
|
||||||
|
interface ClaimInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Set the claim value, and call a validate method if available.
|
||||||
|
*
|
||||||
|
* @param mixed
|
||||||
|
* @return Claim
|
||||||
|
*/
|
||||||
|
public function setValue($value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the claim value.
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function getValue();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the claim name.
|
||||||
|
*
|
||||||
|
* @param string $name
|
||||||
|
* @return Claim
|
||||||
|
*/
|
||||||
|
public function setName($name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the claim name.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getName();
|
||||||
|
}
|
25
src/Claims/Custom.php
Normal file
25
src/Claims/Custom.php
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of jwt-auth.
|
||||||
|
*
|
||||||
|
* (c) Sean Tymon <tymon148@gmail.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Tymon\JWTAuth\Claims;
|
||||||
|
|
||||||
|
class Custom extends Claim
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param string $name
|
||||||
|
* @param mixed $value
|
||||||
|
*/
|
||||||
|
public function __construct($name, $value)
|
||||||
|
{
|
||||||
|
parent::__construct($value);
|
||||||
|
$this->setName($name);
|
||||||
|
}
|
||||||
|
}
|
33
src/Claims/Expiration.php
Normal file
33
src/Claims/Expiration.php
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of jwt-auth.
|
||||||
|
*
|
||||||
|
* (c) Sean Tymon <tymon148@gmail.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Tymon\JWTAuth\Claims;
|
||||||
|
|
||||||
|
class Expiration extends Claim
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The claim name.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $name = 'exp';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate the expiry claim.
|
||||||
|
*
|
||||||
|
* @param mixed $value
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
protected function validate($value)
|
||||||
|
{
|
||||||
|
return is_numeric($value);
|
||||||
|
}
|
||||||
|
}
|
55
src/Claims/Factory.php
Normal file
55
src/Claims/Factory.php
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of jwt-auth.
|
||||||
|
*
|
||||||
|
* (c) Sean Tymon <tymon148@gmail.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Tymon\JWTAuth\Claims;
|
||||||
|
|
||||||
|
class Factory
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private static $classMap = [
|
||||||
|
'aud' => 'Tymon\JWTAuth\Claims\Audience',
|
||||||
|
'exp' => 'Tymon\JWTAuth\Claims\Expiration',
|
||||||
|
'iat' => 'Tymon\JWTAuth\Claims\IssuedAt',
|
||||||
|
'iss' => 'Tymon\JWTAuth\Claims\Issuer',
|
||||||
|
'jti' => 'Tymon\JWTAuth\Claims\JwtId',
|
||||||
|
'nbf' => 'Tymon\JWTAuth\Claims\NotBefore',
|
||||||
|
'sub' => 'Tymon\JWTAuth\Claims\Subject',
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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)
|
||||||
|
{
|
||||||
|
if ($this->has($name)) {
|
||||||
|
return new self::$classMap[$name]($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Custom($name, $value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether the claim exists.
|
||||||
|
*
|
||||||
|
* @param string $name
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function has($name)
|
||||||
|
{
|
||||||
|
return array_key_exists($name, self::$classMap);
|
||||||
|
}
|
||||||
|
}
|
33
src/Claims/IssuedAt.php
Normal file
33
src/Claims/IssuedAt.php
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of jwt-auth.
|
||||||
|
*
|
||||||
|
* (c) Sean Tymon <tymon148@gmail.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Tymon\JWTAuth\Claims;
|
||||||
|
|
||||||
|
class IssuedAt extends Claim
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The claim name.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $name = 'iat';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate the issued at claim.
|
||||||
|
*
|
||||||
|
* @param mixed $value
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
protected function validate($value)
|
||||||
|
{
|
||||||
|
return is_numeric($value);
|
||||||
|
}
|
||||||
|
}
|
22
src/Claims/Issuer.php
Normal file
22
src/Claims/Issuer.php
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of jwt-auth.
|
||||||
|
*
|
||||||
|
* (c) Sean Tymon <tymon148@gmail.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Tymon\JWTAuth\Claims;
|
||||||
|
|
||||||
|
class Issuer extends Claim
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The claim name.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $name = 'iss';
|
||||||
|
}
|
22
src/Claims/JwtId.php
Normal file
22
src/Claims/JwtId.php
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of jwt-auth.
|
||||||
|
*
|
||||||
|
* (c) Sean Tymon <tymon148@gmail.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Tymon\JWTAuth\Claims;
|
||||||
|
|
||||||
|
class JwtId extends Claim
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The claim name.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $name = 'jti';
|
||||||
|
}
|
33
src/Claims/NotBefore.php
Normal file
33
src/Claims/NotBefore.php
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of jwt-auth.
|
||||||
|
*
|
||||||
|
* (c) Sean Tymon <tymon148@gmail.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Tymon\JWTAuth\Claims;
|
||||||
|
|
||||||
|
class NotBefore extends Claim
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The claim name.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $name = 'nbf';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate the not before claim.
|
||||||
|
*
|
||||||
|
* @param mixed $value
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
protected function validate($value)
|
||||||
|
{
|
||||||
|
return is_numeric($value);
|
||||||
|
}
|
||||||
|
}
|
22
src/Claims/Subject.php
Normal file
22
src/Claims/Subject.php
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of jwt-auth.
|
||||||
|
*
|
||||||
|
* (c) Sean Tymon <tymon148@gmail.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Tymon\JWTAuth\Claims;
|
||||||
|
|
||||||
|
class Subject extends Claim
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The claim name.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $name = 'sub';
|
||||||
|
}
|
81
src/Commands/JWTGenerateCommand.php
Normal file
81
src/Commands/JWTGenerateCommand.php
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of jwt-auth.
|
||||||
|
*
|
||||||
|
* (c) Sean Tymon <tymon148@gmail.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Tymon\JWTAuth\Commands;
|
||||||
|
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
use Symfony\Component\Console\Input\InputOption;
|
||||||
|
|
||||||
|
class JWTGenerateCommand extends Command
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The console command name.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $name = 'jwt:generate';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The console command description.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $description = 'Set the JWTAuth secret key used to sign the tokens';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the console command.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function fire()
|
||||||
|
{
|
||||||
|
$key = $this->getRandomKey();
|
||||||
|
|
||||||
|
if ($this->option('show')) {
|
||||||
|
return $this->line('<comment>'.$key.'</comment>');
|
||||||
|
}
|
||||||
|
|
||||||
|
$path = config_path('jwt.php');
|
||||||
|
|
||||||
|
if (file_exists($path)) {
|
||||||
|
file_put_contents($path, str_replace(
|
||||||
|
$this->laravel['config']['jwt.secret'], $key, file_get_contents($path)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->laravel['config']['jwt.secret'] = $key;
|
||||||
|
|
||||||
|
$this->info("jwt-auth secret [$key] set successfully.");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a random key for the JWT Auth secret.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected function getRandomKey()
|
||||||
|
{
|
||||||
|
return Str::random(32);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the console command options.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
protected function getOptions()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
['show', null, InputOption::VALUE_NONE, 'Simply display the key instead of modifying files.'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
20
src/Exceptions/InvalidClaimException.php
Normal file
20
src/Exceptions/InvalidClaimException.php
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of jwt-auth.
|
||||||
|
*
|
||||||
|
* (c) Sean Tymon <tymon148@gmail.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Tymon\JWTAuth\Exceptions;
|
||||||
|
|
||||||
|
class InvalidClaimException extends JWTException
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
protected $statusCode = 400;
|
||||||
|
}
|
49
src/Exceptions/JWTException.php
Normal file
49
src/Exceptions/JWTException.php
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of jwt-auth.
|
||||||
|
*
|
||||||
|
* (c) Sean Tymon <tymon148@gmail.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Tymon\JWTAuth\Exceptions;
|
||||||
|
|
||||||
|
class JWTException extends \Exception
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
protected $statusCode = 500;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $message
|
||||||
|
* @param int $statusCode
|
||||||
|
*/
|
||||||
|
public function __construct($message = 'An error occurred', $statusCode = null)
|
||||||
|
{
|
||||||
|
parent::__construct($message);
|
||||||
|
|
||||||
|
if (! is_null($statusCode)) {
|
||||||
|
$this->setStatusCode($statusCode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param int $statusCode
|
||||||
|
*/
|
||||||
|
public function setStatusCode($statusCode)
|
||||||
|
{
|
||||||
|
$this->statusCode = $statusCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return int the status code
|
||||||
|
*/
|
||||||
|
public function getStatusCode()
|
||||||
|
{
|
||||||
|
return $this->statusCode;
|
||||||
|
}
|
||||||
|
}
|
20
src/Exceptions/PayloadException.php
Normal file
20
src/Exceptions/PayloadException.php
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of jwt-auth.
|
||||||
|
*
|
||||||
|
* (c) Sean Tymon <tymon148@gmail.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Tymon\JWTAuth\Exceptions;
|
||||||
|
|
||||||
|
class PayloadException extends JWTException
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
protected $statusCode = 500;
|
||||||
|
}
|
20
src/Exceptions/TokenBlacklistedException.php
Normal file
20
src/Exceptions/TokenBlacklistedException.php
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of jwt-auth.
|
||||||
|
*
|
||||||
|
* (c) Sean Tymon <tymon148@gmail.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Tymon\JWTAuth\Exceptions;
|
||||||
|
|
||||||
|
class TokenBlacklistedException extends TokenInvalidException
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
protected $statusCode = 401;
|
||||||
|
}
|
20
src/Exceptions/TokenExpiredException.php
Normal file
20
src/Exceptions/TokenExpiredException.php
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of jwt-auth.
|
||||||
|
*
|
||||||
|
* (c) Sean Tymon <tymon148@gmail.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Tymon\JWTAuth\Exceptions;
|
||||||
|
|
||||||
|
class TokenExpiredException extends JWTException
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
protected $statusCode = 401;
|
||||||
|
}
|
20
src/Exceptions/TokenInvalidException.php
Normal file
20
src/Exceptions/TokenInvalidException.php
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of jwt-auth.
|
||||||
|
*
|
||||||
|
* (c) Sean Tymon <tymon148@gmail.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Tymon\JWTAuth\Exceptions;
|
||||||
|
|
||||||
|
class TokenInvalidException extends JWTException
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
protected $statusCode = 400;
|
||||||
|
}
|
27
src/Facades/JWTAuth.php
Normal file
27
src/Facades/JWTAuth.php
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of jwt-auth.
|
||||||
|
*
|
||||||
|
* (c) Sean Tymon <tymon148@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 JWTAuth extends Facade
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Get the registered name of the component.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected static function getFacadeAccessor()
|
||||||
|
{
|
||||||
|
return 'tymon.jwt.auth';
|
||||||
|
}
|
||||||
|
}
|
27
src/Facades/JWTFactory.php
Normal file
27
src/Facades/JWTFactory.php
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of jwt-auth.
|
||||||
|
*
|
||||||
|
* (c) Sean Tymon <tymon148@gmail.com>
|
||||||
|
*
|
||||||
|
* 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';
|
||||||
|
}
|
||||||
|
}
|
343
src/JWTAuth.php
Normal file
343
src/JWTAuth.php
Normal file
|
@ -0,0 +1,343 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of jwt-auth.
|
||||||
|
*
|
||||||
|
* (c) Sean Tymon <tymon148@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 Tymon\JWTAuth\Exceptions\JWTException;
|
||||||
|
use Tymon\JWTAuth\Providers\Auth\AuthInterface;
|
||||||
|
use Tymon\JWTAuth\Providers\User\UserInterface;
|
||||||
|
|
||||||
|
class JWTAuth
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var \Tymon\JWTAuth\JWTManager
|
||||||
|
*/
|
||||||
|
protected $manager;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \Tymon\JWTAuth\Providers\User\UserInterface
|
||||||
|
*/
|
||||||
|
protected $user;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \Tymon\JWTAuth\Providers\Auth\AuthInterface
|
||||||
|
*/
|
||||||
|
protected $auth;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \Illuminate\Http\Request
|
||||||
|
*/
|
||||||
|
protected $request;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $identifier = 'id';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \Tymon\JWTAuth\Token
|
||||||
|
*/
|
||||||
|
protected $token;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param \Tymon\JWTAuth\JWTManager $manager
|
||||||
|
* @param \Tymon\JWTAuth\Providers\User\UserInterface $user
|
||||||
|
* @param \Tymon\JWTAuth\Providers\Auth\AuthInterface $auth
|
||||||
|
* @param \Illuminate\Http\Request $request
|
||||||
|
*/
|
||||||
|
public function __construct(JWTManager $manager, UserInterface $user, AuthInterface $auth, Request $request)
|
||||||
|
{
|
||||||
|
$this->manager = $manager;
|
||||||
|
$this->user = $user;
|
||||||
|
$this->auth = $auth;
|
||||||
|
$this->request = $request;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find a user using the user identifier in the subject claim.
|
||||||
|
*
|
||||||
|
* @param bool|string $token
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function toUser($token = false)
|
||||||
|
{
|
||||||
|
$payload = $this->getPayload($token);
|
||||||
|
|
||||||
|
if (! $user = $this->user->getBy($this->identifier, $payload['sub'])) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $user;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a token using the user identifier as the subject claim.
|
||||||
|
*
|
||||||
|
* @param mixed $user
|
||||||
|
* @param array $customClaims
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function fromUser($user, array $customClaims = [])
|
||||||
|
{
|
||||||
|
$payload = $this->makePayload($user->{$this->identifier}, $customClaims);
|
||||||
|
|
||||||
|
return $this->manager->encode($payload)->get();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempt to authenticate the user and return the token.
|
||||||
|
*
|
||||||
|
* @param array $credentials
|
||||||
|
* @param array $customClaims
|
||||||
|
*
|
||||||
|
* @return false|string
|
||||||
|
*/
|
||||||
|
public function attempt(array $credentials = [], array $customClaims = [])
|
||||||
|
{
|
||||||
|
if (! $this->auth->byCredentials($credentials)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->fromUser($this->auth->user(), $customClaims);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Authenticate a user via a token.
|
||||||
|
*
|
||||||
|
* @param mixed $token
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function authenticate($token = false)
|
||||||
|
{
|
||||||
|
$id = $this->getPayload($token)->get('sub');
|
||||||
|
|
||||||
|
if (! $this->auth->byId($id)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->auth->user();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Refresh an expired token.
|
||||||
|
*
|
||||||
|
* @param mixed $token
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function refresh($token = false)
|
||||||
|
{
|
||||||
|
$this->requireToken($token);
|
||||||
|
|
||||||
|
return $this->manager->refresh($this->token)->get();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invalidate a token (add it to the blacklist).
|
||||||
|
*
|
||||||
|
* @param mixed $token
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function invalidate($token = false)
|
||||||
|
{
|
||||||
|
$this->requireToken($token);
|
||||||
|
|
||||||
|
return $this->manager->invalidate($this->token);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the token.
|
||||||
|
*
|
||||||
|
* @return bool|string
|
||||||
|
*/
|
||||||
|
public function getToken()
|
||||||
|
{
|
||||||
|
if (! $this->token) {
|
||||||
|
try {
|
||||||
|
$this->parseToken();
|
||||||
|
} catch (JWTException $e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->token;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the raw Payload instance.
|
||||||
|
*
|
||||||
|
* @param mixed $token
|
||||||
|
*
|
||||||
|
* @return \Tymon\JWTAuth\Payload
|
||||||
|
*/
|
||||||
|
public function getPayload($token = false)
|
||||||
|
{
|
||||||
|
$this->requireToken($token);
|
||||||
|
|
||||||
|
return $this->manager->decode($this->token);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse the token from the request.
|
||||||
|
*
|
||||||
|
* @param string $query
|
||||||
|
*
|
||||||
|
* @return JWTAuth
|
||||||
|
*/
|
||||||
|
public function parseToken($method = 'bearer', $header = 'authorization', $query = 'token')
|
||||||
|
{
|
||||||
|
if (! $token = $this->parseAuthHeader($header, $method)) {
|
||||||
|
if (! $token = $this->request->query($query, false)) {
|
||||||
|
throw new JWTException('The token could not be parsed from the request', 400);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->setToken($token);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse token from the authorization header.
|
||||||
|
*
|
||||||
|
* @param string $header
|
||||||
|
* @param string $method
|
||||||
|
*
|
||||||
|
* @return false|string
|
||||||
|
*/
|
||||||
|
protected function parseAuthHeader($header = 'authorization', $method = 'bearer')
|
||||||
|
{
|
||||||
|
$header = $this->request->headers->get($header);
|
||||||
|
|
||||||
|
if (! starts_with(strtolower($header), $method)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return trim(str_ireplace($method, '', $header));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a Payload instance.
|
||||||
|
*
|
||||||
|
* @param mixed $subject
|
||||||
|
* @param array $customClaims
|
||||||
|
*
|
||||||
|
* @return \Tymon\JWTAuth\Payload
|
||||||
|
*/
|
||||||
|
protected function makePayload($subject, array $customClaims = [])
|
||||||
|
{
|
||||||
|
return $this->manager->getPayloadFactory()->make(
|
||||||
|
array_merge($customClaims, ['sub' => $subject])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the identifier.
|
||||||
|
*
|
||||||
|
* @param string $identifier
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setIdentifier($identifier)
|
||||||
|
{
|
||||||
|
$this->identifier = $identifier;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the identifier.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getIdentifier()
|
||||||
|
{
|
||||||
|
return $this->identifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the token.
|
||||||
|
*
|
||||||
|
* @param string $token
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setToken($token)
|
||||||
|
{
|
||||||
|
$this->token = new Token($token);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensure that a token is available.
|
||||||
|
*
|
||||||
|
* @param mixed $token
|
||||||
|
*
|
||||||
|
* @return JWTAuth
|
||||||
|
*
|
||||||
|
* @throws \Tymon\JWTAuth\Exceptions\JWTException
|
||||||
|
*/
|
||||||
|
protected function requireToken($token)
|
||||||
|
{
|
||||||
|
if ($token) {
|
||||||
|
return $this->setToken($token);
|
||||||
|
} elseif ($this->token) {
|
||||||
|
return $this;
|
||||||
|
} else {
|
||||||
|
throw new JWTException('A token is required', 400);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the request instance.
|
||||||
|
*
|
||||||
|
* @param Request $request
|
||||||
|
*/
|
||||||
|
public function setRequest(Request $request)
|
||||||
|
{
|
||||||
|
$this->request = $request;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the JWTManager instance.
|
||||||
|
*
|
||||||
|
* @return \Tymon\JWTAuth\JWTManager
|
||||||
|
*/
|
||||||
|
public function manager()
|
||||||
|
{
|
||||||
|
return $this->manager;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Magically call the JWT Manager.
|
||||||
|
*
|
||||||
|
* @param string $method
|
||||||
|
* @param array $parameters
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*
|
||||||
|
* @throws \BadMethodCallException
|
||||||
|
*/
|
||||||
|
public function __call($method, $parameters)
|
||||||
|
{
|
||||||
|
if (method_exists($this->manager, $method)) {
|
||||||
|
return call_user_func_array([$this->manager, $method], $parameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new \BadMethodCallException("Method [$method] does not exist.");
|
||||||
|
}
|
||||||
|
}
|
183
src/JWTManager.php
Normal file
183
src/JWTManager.php
Normal file
|
@ -0,0 +1,183 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of jwt-auth.
|
||||||
|
*
|
||||||
|
* (c) Sean Tymon <tymon148@gmail.com>
|
||||||
|
*
|
||||||
|
* 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\Exceptions\JWTException;
|
||||||
|
use Tymon\JWTAuth\Providers\JWT\JWTInterface;
|
||||||
|
use Tymon\JWTAuth\Exceptions\TokenBlacklistedException;
|
||||||
|
|
||||||
|
class JWTManager
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var \Tymon\JWTAuth\Providers\JWT\JWTInterface
|
||||||
|
*/
|
||||||
|
protected $jwt;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \Tymon\JWTAuth\Blacklist
|
||||||
|
*/
|
||||||
|
protected $blacklist;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \Tymon\JWTAuth\PayloadFactory
|
||||||
|
*/
|
||||||
|
protected $payloadFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
protected $blacklistEnabled = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
protected $refreshFlow = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param \Tymon\JWTAuth\Providers\JWT\JWTInterface $jwt
|
||||||
|
* @param \Tymon\JWTAuth\Blacklist $blacklist
|
||||||
|
* @param \Tymon\JWTAuth\PayloadFactory $payloadFactory
|
||||||
|
*/
|
||||||
|
public function __construct(JWTInterface $jwt, Blacklist $blacklist, PayloadFactory $payloadFactory)
|
||||||
|
{
|
||||||
|
$this->jwt = $jwt;
|
||||||
|
$this->blacklist = $blacklist;
|
||||||
|
$this->payloadFactory = $payloadFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encode a Payload and return the Token.
|
||||||
|
*
|
||||||
|
* @param \Tymon\JWTAuth\Payload $payload
|
||||||
|
* @return \Tymon\JWTAuth\Token
|
||||||
|
*/
|
||||||
|
public function encode(Payload $payload)
|
||||||
|
{
|
||||||
|
$token = $this->jwt->encode($payload->get());
|
||||||
|
|
||||||
|
return new Token($token);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decode a Token and return the Payload.
|
||||||
|
*
|
||||||
|
* @param \Tymon\JWTAuth\Token $token
|
||||||
|
* @return Payload
|
||||||
|
* @throws TokenBlacklistedException
|
||||||
|
*/
|
||||||
|
public function decode(Token $token)
|
||||||
|
{
|
||||||
|
$payloadArray = $this->jwt->decode($token->get());
|
||||||
|
|
||||||
|
$payload = $this->payloadFactory->setRefreshFlow($this->refreshFlow)->make($payloadArray);
|
||||||
|
|
||||||
|
if ($this->blacklistEnabled && $this->blacklist->has($payload)) {
|
||||||
|
throw new TokenBlacklistedException('The token has been blacklisted');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $payload;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Refresh a Token and return a new Token.
|
||||||
|
*
|
||||||
|
* @param \Tymon\JWTAuth\Token $token
|
||||||
|
* @return \Tymon\JWTAuth\Token
|
||||||
|
*/
|
||||||
|
public function refresh(Token $token)
|
||||||
|
{
|
||||||
|
$payload = $this->setRefreshFlow()->decode($token);
|
||||||
|
|
||||||
|
if ($this->blacklistEnabled) {
|
||||||
|
// invalidate old token
|
||||||
|
$this->blacklist->add($payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
// return the new token
|
||||||
|
return $this->encode(
|
||||||
|
$this->payloadFactory->make([
|
||||||
|
'sub' => $payload['sub'],
|
||||||
|
'iat' => $payload['iat'],
|
||||||
|
])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invalidate a Token by adding it to the blacklist.
|
||||||
|
*
|
||||||
|
* @param Token $token
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function invalidate(Token $token)
|
||||||
|
{
|
||||||
|
if (! $this->blacklistEnabled) {
|
||||||
|
throw new JWTException('You must have the blacklist enabled to invalidate a token.');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->blacklist->add($this->decode($token));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the PayloadFactory instance.
|
||||||
|
*
|
||||||
|
* @return \Tymon\JWTAuth\PayloadFactory
|
||||||
|
*/
|
||||||
|
public function getPayloadFactory()
|
||||||
|
{
|
||||||
|
return $this->payloadFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the JWTProvider instance.
|
||||||
|
*
|
||||||
|
* @return \Tymon\JWTAuth\Providers\JWT\JWTInterface
|
||||||
|
*/
|
||||||
|
public function getJWTProvider()
|
||||||
|
{
|
||||||
|
return $this->jwt;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the Blacklist instance.
|
||||||
|
*
|
||||||
|
* @return \Tymon\JWTAuth\Blacklist
|
||||||
|
*/
|
||||||
|
public function getBlacklist()
|
||||||
|
{
|
||||||
|
return $this->blacklist;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set whether the blacklist is enabled.
|
||||||
|
*
|
||||||
|
* @param bool $enabled
|
||||||
|
*/
|
||||||
|
public function setBlacklistEnabled($enabled)
|
||||||
|
{
|
||||||
|
$this->blacklistEnabled = $enabled;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the refresh flow.
|
||||||
|
*
|
||||||
|
* @param bool $refreshFlow
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setRefreshFlow($refreshFlow = true)
|
||||||
|
{
|
||||||
|
$this->refreshFlow = $refreshFlow;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
}
|
64
src/Middleware/BaseMiddleware.php
Normal file
64
src/Middleware/BaseMiddleware.php
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of jwt-auth.
|
||||||
|
*
|
||||||
|
* (c) Sean Tymon <tymon148@gmail.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Tymon\JWTAuth\Middleware;
|
||||||
|
|
||||||
|
use Tymon\JWTAuth\JWTAuth;
|
||||||
|
use Illuminate\Contracts\Events\Dispatcher;
|
||||||
|
use Illuminate\Contracts\Routing\ResponseFactory;
|
||||||
|
|
||||||
|
abstract class BaseMiddleware
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var \Illuminate\Contracts\Routing\ResponseFactory
|
||||||
|
*/
|
||||||
|
protected $response;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \Illuminate\Contracts\Events\Dispatcher
|
||||||
|
*/
|
||||||
|
protected $events;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \Tymon\JWTAuth\JWTAuth
|
||||||
|
*/
|
||||||
|
protected $auth;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new BaseMiddleware instance.
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Contracts\Routing\ResponseFactory $response
|
||||||
|
* @param \Illuminate\Contracts\Events\Dispatcher $events
|
||||||
|
* @param \Tymon\JWTAuth\JWTAuth $auth
|
||||||
|
*/
|
||||||
|
public function __construct(ResponseFactory $response, Dispatcher $events, JWTAuth $auth)
|
||||||
|
{
|
||||||
|
$this->response = $response;
|
||||||
|
$this->events = $events;
|
||||||
|
$this->auth = $auth;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fire event and return the response.
|
||||||
|
*
|
||||||
|
* @param string $event
|
||||||
|
* @param string $error
|
||||||
|
* @param int $status
|
||||||
|
* @param array $payload
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
protected function respond($event, $error, $status, $payload = [])
|
||||||
|
{
|
||||||
|
$response = $this->events->fire($event, $payload, true);
|
||||||
|
|
||||||
|
return $response ?: $this->response->json(['error' => $error], $status);
|
||||||
|
}
|
||||||
|
}
|
48
src/Middleware/GetUserFromToken.php
Normal file
48
src/Middleware/GetUserFromToken.php
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of jwt-auth.
|
||||||
|
*
|
||||||
|
* (c) Sean Tymon <tymon148@gmail.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Tymon\JWTAuth\Middleware;
|
||||||
|
|
||||||
|
use Tymon\JWTAuth\Exceptions\JWTException;
|
||||||
|
use Tymon\JWTAuth\Exceptions\TokenExpiredException;
|
||||||
|
|
||||||
|
class GetUserFromToken extends BaseMiddleware
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Handle an incoming request.
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Http\Request $request
|
||||||
|
* @param \Closure $next
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function handle($request, \Closure $next)
|
||||||
|
{
|
||||||
|
if (! $token = $this->auth->setRequest($request)->getToken()) {
|
||||||
|
return $this->respond('tymon.jwt.absent', 'token_not_provided', 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$user = $this->auth->authenticate($token);
|
||||||
|
} catch (TokenExpiredException $e) {
|
||||||
|
return $this->respond('tymon.jwt.expired', 'token_expired', $e->getStatusCode(), [$e]);
|
||||||
|
} catch (JWTException $e) {
|
||||||
|
return $this->respond('tymon.jwt.invalid', 'token_invalid', $e->getStatusCode(), [$e]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! $user) {
|
||||||
|
return $this->respond('tymon.jwt.user_not_found', 'user_not_found', 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->events->fire('tymon.jwt.valid', $user);
|
||||||
|
|
||||||
|
return $next($request);
|
||||||
|
}
|
||||||
|
}
|
43
src/Middleware/RefreshToken.php
Normal file
43
src/Middleware/RefreshToken.php
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of jwt-auth.
|
||||||
|
*
|
||||||
|
* (c) Sean Tymon <tymon148@gmail.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Tymon\JWTAuth\Middleware;
|
||||||
|
|
||||||
|
use Tymon\JWTAuth\Exceptions\JWTException;
|
||||||
|
use Tymon\JWTAuth\Exceptions\TokenExpiredException;
|
||||||
|
|
||||||
|
class RefreshToken extends BaseMiddleware
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Handle an incoming request.
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Http\Request $request
|
||||||
|
* @param \Closure $next
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function handle($request, \Closure $next)
|
||||||
|
{
|
||||||
|
$response = $next($request);
|
||||||
|
|
||||||
|
try {
|
||||||
|
$newToken = $this->auth->setRequest($request)->parseToken()->refresh();
|
||||||
|
} catch (TokenExpiredException $e) {
|
||||||
|
return $this->respond('tymon.jwt.expired', 'token_expired', $e->getStatusCode(), [$e]);
|
||||||
|
} catch (JWTException $e) {
|
||||||
|
return $this->respond('tymon.jwt.invalid', 'token_invalid', $e->getStatusCode(), [$e]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// send the refreshed token back to the client
|
||||||
|
$response->headers->set('Authorization', 'Bearer '.$newToken);
|
||||||
|
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
}
|
175
src/Payload.php
Normal file
175
src/Payload.php
Normal file
|
@ -0,0 +1,175 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of jwt-auth.
|
||||||
|
*
|
||||||
|
* (c) Sean Tymon <tymon148@gmail.com>
|
||||||
|
*
|
||||||
|
* 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\Claims\Claim;
|
||||||
|
use Tymon\JWTAuth\Exceptions\PayloadException;
|
||||||
|
use Tymon\JWTAuth\Validators\PayloadValidator;
|
||||||
|
|
||||||
|
class Payload implements \ArrayAccess
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The array of claims.
|
||||||
|
*
|
||||||
|
* @var \Tymon\JWTAuth\Claims\Claim[]
|
||||||
|
*/
|
||||||
|
private $claims = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build the Payload.
|
||||||
|
*
|
||||||
|
* @param array $claims
|
||||||
|
* @param \Tymon\JWTAuth\Validators\PayloadValidator $validator
|
||||||
|
* @param bool $refreshFlow
|
||||||
|
*/
|
||||||
|
public function __construct(array $claims, PayloadValidator $validator, $refreshFlow = false)
|
||||||
|
{
|
||||||
|
$this->claims = $claims;
|
||||||
|
|
||||||
|
$validator->setRefreshFlow($refreshFlow)->check($this->toArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the array of claim instances.
|
||||||
|
*
|
||||||
|
* @return \Tymon\JWTAuth\Claims\Claim[]
|
||||||
|
*/
|
||||||
|
public function getClaims()
|
||||||
|
{
|
||||||
|
return $this->claims;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the array of claims.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function toArray()
|
||||||
|
{
|
||||||
|
$results = [];
|
||||||
|
foreach ($this->claims as $claim) {
|
||||||
|
$results[$claim->getName()] = $claim->getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $results;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the payload.
|
||||||
|
*
|
||||||
|
* @param string $claim
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function get($claim = null)
|
||||||
|
{
|
||||||
|
if (! is_null($claim)) {
|
||||||
|
if (is_array($claim)) {
|
||||||
|
return array_map([$this, 'get'], $claim);
|
||||||
|
}
|
||||||
|
|
||||||
|
return array_get($this->toArray(), $claim, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine whether the payload has the claim.
|
||||||
|
*
|
||||||
|
* @param \Tymon\JWTAuth\Claims\Claim $claim
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function has(Claim $claim)
|
||||||
|
{
|
||||||
|
return in_array($claim, $this->claims);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the payload as a string.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function __toString()
|
||||||
|
{
|
||||||
|
return json_encode($this->toArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if an item exists at an offset.
|
||||||
|
*
|
||||||
|
* @param mixed $key
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function offsetExists($key)
|
||||||
|
{
|
||||||
|
return array_key_exists($key, $this->toArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an item at a given offset.
|
||||||
|
*
|
||||||
|
* @param mixed $key
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function offsetGet($key)
|
||||||
|
{
|
||||||
|
return array_get($this->toArray(), $key, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Don't allow changing the payload as it should be immutable.
|
||||||
|
*
|
||||||
|
* @param mixed $key
|
||||||
|
* @param mixed $value
|
||||||
|
* @throws Exceptions\PayloadException
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function offsetSet($key, $value)
|
||||||
|
{
|
||||||
|
throw new PayloadException('The payload is immutable');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Don't allow changing the payload as it should be immutable.
|
||||||
|
*
|
||||||
|
* @param string $key
|
||||||
|
* @throws Exceptions\PayloadException
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function offsetUnset($key)
|
||||||
|
{
|
||||||
|
throw new PayloadException('The payload is immutable');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Magically get a claim value.
|
||||||
|
*
|
||||||
|
* @param string $method
|
||||||
|
* @param array $parameters
|
||||||
|
* @return mixed
|
||||||
|
* @throws \BadMethodCallException
|
||||||
|
*/
|
||||||
|
public function __call($method, $parameters)
|
||||||
|
{
|
||||||
|
if (! method_exists($this, $method) && starts_with($method, 'get')) {
|
||||||
|
$class = sprintf('Tymon\\JWTAuth\\Claims\\%s', substr($method, 3));
|
||||||
|
|
||||||
|
foreach ($this->claims as $claim) {
|
||||||
|
if (get_class($claim) === $class) {
|
||||||
|
return $claim->getValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new \BadMethodCallException(sprintf('The claim [%s] does not exist on the payload.', $method));
|
||||||
|
}
|
||||||
|
}
|
245
src/PayloadFactory.php
Normal file
245
src/PayloadFactory.php
Normal file
|
@ -0,0 +1,245 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of jwt-auth.
|
||||||
|
*
|
||||||
|
* (c) Sean Tymon <tymon148@gmail.com>
|
||||||
|
*
|
||||||
|
* 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\Str;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Tymon\JWTAuth\Claims\Factory;
|
||||||
|
use Tymon\JWTAuth\Validators\PayloadValidator;
|
||||||
|
|
||||||
|
class PayloadFactory
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var \Tymon\JWTAuth\Claims\Factory
|
||||||
|
*/
|
||||||
|
protected $claimFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \Illuminate\Http\Request
|
||||||
|
*/
|
||||||
|
protected $request;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \Tymon\JWTAuth\Validators\PayloadValidator
|
||||||
|
*/
|
||||||
|
protected $validator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
protected $ttl = 60;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
protected $refreshFlow = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $defaultClaims = ['iss', 'iat', 'exp', 'nbf', 'jti'];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $claims = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param \Tymon\JWTAuth\Claims\Factory $claimFactory
|
||||||
|
* @param \Illuminate\Http\Request $request
|
||||||
|
* @param \Tymon\JWTAuth\Validators\PayloadValidator $validator
|
||||||
|
*/
|
||||||
|
public function __construct(Factory $claimFactory, Request $request, PayloadValidator $validator)
|
||||||
|
{
|
||||||
|
$this->claimFactory = $claimFactory;
|
||||||
|
$this->request = $request;
|
||||||
|
$this->validator = $validator;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create the Payload instance.
|
||||||
|
*
|
||||||
|
* @param array $customClaims
|
||||||
|
* @return \Tymon\JWTAuth\Payload
|
||||||
|
*/
|
||||||
|
public function make(array $customClaims = [])
|
||||||
|
{
|
||||||
|
$claims = $this->buildClaims($customClaims)->resolveClaims();
|
||||||
|
|
||||||
|
return new Payload($claims, $this->validator, $this->refreshFlow);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add an array of claims to the Payload.
|
||||||
|
*
|
||||||
|
* @param array $claims
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public 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
|
||||||
|
*/
|
||||||
|
public function addClaim($name, $value)
|
||||||
|
{
|
||||||
|
$this->claims[$name] = $value;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build the default claims.
|
||||||
|
*
|
||||||
|
* @param array $customClaims
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
protected function buildClaims(array $customClaims)
|
||||||
|
{
|
||||||
|
// add any custom claims first
|
||||||
|
$this->addClaims($customClaims);
|
||||||
|
|
||||||
|
foreach ($this->defaultClaims as $claim) {
|
||||||
|
if (! array_key_exists($claim, $customClaims)) {
|
||||||
|
$this->addClaim($claim, $this->$claim());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build out the Claim DTO's.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function resolveClaims()
|
||||||
|
{
|
||||||
|
$resolved = [];
|
||||||
|
foreach ($this->claims as $name => $value) {
|
||||||
|
$resolved[] = $this->claimFactory->get($name, $value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $resolved;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the Issuer (iss) claim.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function iss()
|
||||||
|
{
|
||||||
|
return $this->request->url();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the Issued At (iat) claim.
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function iat()
|
||||||
|
{
|
||||||
|
return Utils::now()->timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the Expiration (exp) claim.
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function exp()
|
||||||
|
{
|
||||||
|
return Utils::now()->addMinutes($this->ttl)->timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the Not Before (nbf) claim.
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function nbf()
|
||||||
|
{
|
||||||
|
return Utils::now()->timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a unique id (jti) for the token.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected function jti()
|
||||||
|
{
|
||||||
|
return Str::random();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 refresh flow.
|
||||||
|
*
|
||||||
|
* @param bool $refreshFlow
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setRefreshFlow($refreshFlow = true)
|
||||||
|
{
|
||||||
|
$this->refreshFlow = $refreshFlow;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Magically add a claim.
|
||||||
|
*
|
||||||
|
* @param string $method
|
||||||
|
* @param array $parameters
|
||||||
|
* @return PayloadFactory
|
||||||
|
* @throws \BadMethodCallException
|
||||||
|
*/
|
||||||
|
public function __call($method, $parameters)
|
||||||
|
{
|
||||||
|
$this->addClaim($method, $parameters[0]);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
}
|
38
src/Providers/Auth/AuthInterface.php
Normal file
38
src/Providers/Auth/AuthInterface.php
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of jwt-auth.
|
||||||
|
*
|
||||||
|
* (c) Sean Tymon <tymon148@gmail.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Tymon\JWTAuth\Providers\Auth;
|
||||||
|
|
||||||
|
interface AuthInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Check a user's credentials.
|
||||||
|
*
|
||||||
|
* @param array $credentials
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function byCredentials(array $credentials = []);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Authenticate a user via the id.
|
||||||
|
*
|
||||||
|
* @param mixed $id
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function byId($id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the currently authenticated user.
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function user();
|
||||||
|
}
|
62
src/Providers/Auth/IlluminateAuthAdapter.php
Normal file
62
src/Providers/Auth/IlluminateAuthAdapter.php
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of jwt-auth.
|
||||||
|
*
|
||||||
|
* (c) Sean Tymon <tymon148@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 Illuminate\Auth\AuthManager;
|
||||||
|
|
||||||
|
class IlluminateAuthAdapter implements AuthInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var \Illuminate\Auth\AuthManager
|
||||||
|
*/
|
||||||
|
protected $auth;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param \Illuminate\Auth\AuthManager $auth
|
||||||
|
*/
|
||||||
|
public function __construct(AuthManager $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();
|
||||||
|
}
|
||||||
|
}
|
27
src/Providers/JWT/JWTInterface.php
Normal file
27
src/Providers/JWT/JWTInterface.php
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of jwt-auth.
|
||||||
|
*
|
||||||
|
* (c) Sean Tymon <tymon148@gmail.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Tymon\JWTAuth\Providers\JWT;
|
||||||
|
|
||||||
|
interface JWTInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param array $payload
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function encode(array $payload);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $token
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function decode($token);
|
||||||
|
}
|
82
src/Providers/JWT/JWTProvider.php
Normal file
82
src/Providers/JWT/JWTProvider.php
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of jwt-auth.
|
||||||
|
*
|
||||||
|
* (c) Sean Tymon <tymon148@gmail.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Tymon\JWTAuth\Providers\JWT;
|
||||||
|
|
||||||
|
abstract class JWTProvider
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $secret;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $algo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $secret
|
||||||
|
* @param string $algo
|
||||||
|
*/
|
||||||
|
public function __construct($secret, $algo = 'HS256')
|
||||||
|
{
|
||||||
|
$this->secret = $secret;
|
||||||
|
$this->algo = $algo;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the algorithm used to sign the token.
|
||||||
|
*
|
||||||
|
* @param string $algo
|
||||||
|
* @return self
|
||||||
|
*/
|
||||||
|
public function setAlgo($algo)
|
||||||
|
{
|
||||||
|
$this->algo = $algo;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the algorithm used to sign the token.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getAlgo()
|
||||||
|
{
|
||||||
|
return $this->algo;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the secret used to sign the token.
|
||||||
|
*
|
||||||
|
* @param string $secret
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setSecret($secret)
|
||||||
|
{
|
||||||
|
$this->secret = $secret;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the secret used to sign the token.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getSecret()
|
||||||
|
{
|
||||||
|
return $this->secret;
|
||||||
|
}
|
||||||
|
}
|
76
src/Providers/JWT/NamshiAdapter.php
Normal file
76
src/Providers/JWT/NamshiAdapter.php
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of jwt-auth.
|
||||||
|
*
|
||||||
|
* (c) Sean Tymon <tymon148@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 Tymon\JWTAuth\Exceptions\JWTException;
|
||||||
|
use Tymon\JWTAuth\Exceptions\TokenInvalidException;
|
||||||
|
|
||||||
|
class NamshiAdapter extends JWTProvider implements JWTInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var \Namshi\JOSE\JWS
|
||||||
|
*/
|
||||||
|
protected $jws;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $secret
|
||||||
|
* @param string $algo
|
||||||
|
* @param null $driver
|
||||||
|
*/
|
||||||
|
public function __construct($secret, $algo, $driver = null)
|
||||||
|
{
|
||||||
|
parent::__construct($secret, $algo);
|
||||||
|
|
||||||
|
$this->jws = $driver ?: new JWS(['typ' => 'JWT', 'alg' => $algo]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a JSON Web Token.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
* @throws \Tymon\JWTAuth\Exceptions\JWTException
|
||||||
|
*/
|
||||||
|
public function encode(array $payload)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$this->jws->setPayload($payload)->sign($this->secret);
|
||||||
|
|
||||||
|
return $this->jws->getTokenString();
|
||||||
|
} catch (Exception $e) {
|
||||||
|
throw new JWTException('Could not create token: '.$e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decode a JSON Web Token.
|
||||||
|
*
|
||||||
|
* @param string $token
|
||||||
|
* @return array
|
||||||
|
* @throws \Tymon\JWTAuth\Exceptions\JWTException
|
||||||
|
*/
|
||||||
|
public function decode($token)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$jws = JWS::load($token);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
throw new TokenInvalidException('Could not decode token: '.$e->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! $jws->verify($this->secret, $this->algo)) {
|
||||||
|
throw new TokenInvalidException('Token Signature could not be verified.');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $jws->getPayload();
|
||||||
|
}
|
||||||
|
}
|
278
src/Providers/JWTAuthServiceProvider.php
Normal file
278
src/Providers/JWTAuthServiceProvider.php
Normal file
|
@ -0,0 +1,278 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of jwt-auth.
|
||||||
|
*
|
||||||
|
* (c) Sean Tymon <tymon148@gmail.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Tymon\JWTAuth\Providers;
|
||||||
|
|
||||||
|
use Tymon\JWTAuth\JWTAuth;
|
||||||
|
use Tymon\JWTAuth\Blacklist;
|
||||||
|
use Tymon\JWTAuth\JWTManager;
|
||||||
|
use Tymon\JWTAuth\Claims\Factory;
|
||||||
|
use Tymon\JWTAuth\PayloadFactory;
|
||||||
|
use Illuminate\Support\ServiceProvider;
|
||||||
|
use Tymon\JWTAuth\Commands\JWTGenerateCommand;
|
||||||
|
use Tymon\JWTAuth\Validators\PayloadValidator;
|
||||||
|
|
||||||
|
class JWTAuthServiceProvider extends ServiceProvider
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Indicates if loading of the provider is deferred.
|
||||||
|
*
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
protected $defer = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Boot the service provider.
|
||||||
|
*/
|
||||||
|
public function boot()
|
||||||
|
{
|
||||||
|
$this->publishes([
|
||||||
|
__DIR__.'/../config/config.php' => config_path('jwt.php'),
|
||||||
|
], 'config');
|
||||||
|
|
||||||
|
$this->bootBindings();
|
||||||
|
|
||||||
|
$this->commands('tymon.jwt.generate');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bind some Interfaces and implementations.
|
||||||
|
*/
|
||||||
|
protected function bootBindings()
|
||||||
|
{
|
||||||
|
$this->app->singleton('Tymon\JWTAuth\JWTAuth', function ($app) {
|
||||||
|
return $app['tymon.jwt.auth'];
|
||||||
|
});
|
||||||
|
|
||||||
|
$this->app->singleton('Tymon\JWTAuth\Providers\User\UserInterface', function ($app) {
|
||||||
|
return $app['tymon.jwt.provider.user'];
|
||||||
|
});
|
||||||
|
|
||||||
|
$this->app->singleton('Tymon\JWTAuth\Providers\JWT\JWTInterface', function ($app) {
|
||||||
|
return $app['tymon.jwt.provider.jwt'];
|
||||||
|
});
|
||||||
|
|
||||||
|
$this->app->singleton('Tymon\JWTAuth\Providers\Auth\AuthInterface', function ($app) {
|
||||||
|
return $app['tymon.jwt.provider.auth'];
|
||||||
|
});
|
||||||
|
|
||||||
|
$this->app->singleton('Tymon\JWTAuth\Providers\Storage\StorageInterface', function ($app) {
|
||||||
|
return $app['tymon.jwt.provider.storage'];
|
||||||
|
});
|
||||||
|
|
||||||
|
$this->app->singleton('Tymon\JWTAuth\JWTManager', function ($app) {
|
||||||
|
return $app['tymon.jwt.manager'];
|
||||||
|
});
|
||||||
|
|
||||||
|
$this->app->singleton('Tymon\JWTAuth\Blacklist', function ($app) {
|
||||||
|
return $app['tymon.jwt.blacklist'];
|
||||||
|
});
|
||||||
|
|
||||||
|
$this->app->singleton('Tymon\JWTAuth\PayloadFactory', function ($app) {
|
||||||
|
return $app['tymon.jwt.payload.factory'];
|
||||||
|
});
|
||||||
|
|
||||||
|
$this->app->singleton('Tymon\JWTAuth\Claims\Factory', function ($app) {
|
||||||
|
return $app['tymon.jwt.claim.factory'];
|
||||||
|
});
|
||||||
|
|
||||||
|
$this->app->singleton('Tymon\JWTAuth\Validators\PayloadValidator', function ($app) {
|
||||||
|
return $app['tymon.jwt.validators.payload'];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register the service provider.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function register()
|
||||||
|
{
|
||||||
|
// register providers
|
||||||
|
$this->registerUserProvider();
|
||||||
|
$this->registerJWTProvider();
|
||||||
|
$this->registerAuthProvider();
|
||||||
|
$this->registerStorageProvider();
|
||||||
|
$this->registerJWTBlacklist();
|
||||||
|
|
||||||
|
$this->registerClaimFactory();
|
||||||
|
$this->registerJWTManager();
|
||||||
|
|
||||||
|
$this->registerJWTAuth();
|
||||||
|
$this->registerPayloadValidator();
|
||||||
|
$this->registerPayloadFactory();
|
||||||
|
$this->registerJWTCommand();
|
||||||
|
|
||||||
|
$this->mergeConfigFrom(__DIR__.'/../config/config.php', 'jwt');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register the bindings for the User provider.
|
||||||
|
*/
|
||||||
|
protected function registerUserProvider()
|
||||||
|
{
|
||||||
|
$this->app->singleton('tymon.jwt.provider.user', function ($app) {
|
||||||
|
$provider = $this->config('providers.user');
|
||||||
|
$model = $app->make($this->config('user'));
|
||||||
|
|
||||||
|
return new $provider($model);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register the bindings for the JSON Web Token provider.
|
||||||
|
*/
|
||||||
|
protected function registerJWTProvider()
|
||||||
|
{
|
||||||
|
$this->app->singleton('tymon.jwt.provider.jwt', function ($app) {
|
||||||
|
$secret = $this->config('secret');
|
||||||
|
$algo = $this->config('algo');
|
||||||
|
$provider = $this->config('providers.jwt');
|
||||||
|
|
||||||
|
return new $provider($secret, $algo);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register the bindings for the Auth provider.
|
||||||
|
*/
|
||||||
|
protected function registerAuthProvider()
|
||||||
|
{
|
||||||
|
$this->app->singleton('tymon.jwt.provider.auth', function ($app) {
|
||||||
|
return $this->getConfigInstance($this->config('providers.auth'));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register the bindings for the Storage provider.
|
||||||
|
*/
|
||||||
|
protected function registerStorageProvider()
|
||||||
|
{
|
||||||
|
$this->app->singleton('tymon.jwt.provider.storage', function ($app) {
|
||||||
|
return $this->getConfigInstance($this->config('providers.storage'));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register the bindings for the Payload Factory.
|
||||||
|
*/
|
||||||
|
protected function registerClaimFactory()
|
||||||
|
{
|
||||||
|
$this->app->singleton('tymon.jwt.claim.factory', function () {
|
||||||
|
return new Factory();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register the bindings for the JWT Manager.
|
||||||
|
*/
|
||||||
|
protected function registerJWTManager()
|
||||||
|
{
|
||||||
|
$this->app->singleton('tymon.jwt.manager', function ($app) {
|
||||||
|
$instance = new JWTManager(
|
||||||
|
$app['tymon.jwt.provider.jwt'],
|
||||||
|
$app['tymon.jwt.blacklist'],
|
||||||
|
$app['tymon.jwt.payload.factory']
|
||||||
|
);
|
||||||
|
|
||||||
|
return $instance->setBlacklistEnabled((bool) $this->config('blacklist_enabled'));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register the bindings for the main JWTAuth class.
|
||||||
|
*/
|
||||||
|
protected function registerJWTAuth()
|
||||||
|
{
|
||||||
|
$this->app->singleton('tymon.jwt.auth', function ($app) {
|
||||||
|
$auth = new JWTAuth(
|
||||||
|
$app['tymon.jwt.manager'],
|
||||||
|
$app['tymon.jwt.provider.user'],
|
||||||
|
$app['tymon.jwt.provider.auth'],
|
||||||
|
$app['request']
|
||||||
|
);
|
||||||
|
|
||||||
|
return $auth->setIdentifier($this->config('identifier'));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register the bindings for the main JWTAuth class.
|
||||||
|
*/
|
||||||
|
protected function registerJWTBlacklist()
|
||||||
|
{
|
||||||
|
$this->app->singleton('tymon.jwt.blacklist', function ($app) {
|
||||||
|
$instance = new Blacklist($app['tymon.jwt.provider.storage']);
|
||||||
|
|
||||||
|
return $instance->setRefreshTTL($this->config('refresh_ttl'));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register the bindings for the payload validator.
|
||||||
|
*/
|
||||||
|
protected function registerPayloadValidator()
|
||||||
|
{
|
||||||
|
$this->app->singleton('tymon.jwt.validators.payload', function () {
|
||||||
|
return with(new PayloadValidator())->setRefreshTTL($this->config('refresh_ttl'))->setRequiredClaims($this->config('required_claims'));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register the bindings for the Payload Factory.
|
||||||
|
*/
|
||||||
|
protected function registerPayloadFactory()
|
||||||
|
{
|
||||||
|
$this->app->singleton('tymon.jwt.payload.factory', function ($app) {
|
||||||
|
$factory = new PayloadFactory($app['tymon.jwt.claim.factory'], $app['request'], $app['tymon.jwt.validators.payload']);
|
||||||
|
|
||||||
|
return $factory->setTTL($this->config('ttl'));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register the Artisan command.
|
||||||
|
*/
|
||||||
|
protected function registerJWTCommand()
|
||||||
|
{
|
||||||
|
$this->app->singleton('tymon.jwt.generate', function () {
|
||||||
|
return new JWTGenerateCommand();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper to get the config values.
|
||||||
|
*
|
||||||
|
* @param string $key
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected function config($key, $default = null)
|
||||||
|
{
|
||||||
|
return config("jwt.$key", $default);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an instantiable configuration instance. Pinched from dingo/api :).
|
||||||
|
*
|
||||||
|
* @param mixed $instance
|
||||||
|
* @return object
|
||||||
|
*/
|
||||||
|
protected function getConfigInstance($instance)
|
||||||
|
{
|
||||||
|
if (is_callable($instance)) {
|
||||||
|
return call_user_func($instance, $this->app);
|
||||||
|
} elseif (is_string($instance)) {
|
||||||
|
return $this->app->make($instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $instance;
|
||||||
|
}
|
||||||
|
}
|
94
src/Providers/Storage/IlluminateCacheAdapter.php
Normal file
94
src/Providers/Storage/IlluminateCacheAdapter.php
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of jwt-auth.
|
||||||
|
*
|
||||||
|
* (c) Sean Tymon <tymon148@gmail.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Tymon\JWTAuth\Providers\Storage;
|
||||||
|
|
||||||
|
use Illuminate\Cache\CacheManager;
|
||||||
|
|
||||||
|
class IlluminateCacheAdapter implements StorageInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var \Illuminate\Cache\CacheManager
|
||||||
|
*/
|
||||||
|
protected $cache;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $tag = 'tymon.jwt';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param \Illuminate\Cache\CacheManager $cache
|
||||||
|
*/
|
||||||
|
public function __construct(CacheManager $cache)
|
||||||
|
{
|
||||||
|
$this->cache = $cache;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a new item into storage.
|
||||||
|
*
|
||||||
|
* @param string $key
|
||||||
|
* @param mixed $value
|
||||||
|
* @param int $minutes
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function add($key, $value, $minutes)
|
||||||
|
{
|
||||||
|
$this->cache()->put($key, $value, $minutes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether a key exists in storage.
|
||||||
|
*
|
||||||
|
* @param string $key
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function has($key)
|
||||||
|
{
|
||||||
|
return $this->cache()->has($key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove an item from storage.
|
||||||
|
*
|
||||||
|
* @param string $key
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function destroy($key)
|
||||||
|
{
|
||||||
|
return $this->cache()->forget($key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove all items associated with the tag.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function flush()
|
||||||
|
{
|
||||||
|
$this->cache()->flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the cache instance with tags attached.
|
||||||
|
*
|
||||||
|
* @return \Illuminate\Cache\CacheManager
|
||||||
|
*/
|
||||||
|
protected function cache()
|
||||||
|
{
|
||||||
|
if (! method_exists($this->cache, 'tags')) {
|
||||||
|
return $this->cache;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->cache->tags($this->tag);
|
||||||
|
}
|
||||||
|
}
|
39
src/Providers/Storage/StorageInterface.php
Normal file
39
src/Providers/Storage/StorageInterface.php
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of jwt-auth.
|
||||||
|
*
|
||||||
|
* (c) Sean Tymon <tymon148@gmail.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Tymon\JWTAuth\Providers\Storage;
|
||||||
|
|
||||||
|
interface StorageInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param string $key
|
||||||
|
* @param int $minutes
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function add($key, $value, $minutes);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $key
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function has($key);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $key
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function destroy($key);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function flush();
|
||||||
|
}
|
44
src/Providers/User/EloquentUserAdapter.php
Normal file
44
src/Providers/User/EloquentUserAdapter.php
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of jwt-auth.
|
||||||
|
*
|
||||||
|
* (c) Sean Tymon <tymon148@gmail.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Tymon\JWTAuth\Providers\User;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
|
class EloquentUserAdapter implements UserInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var \Illuminate\Database\Eloquent\Model
|
||||||
|
*/
|
||||||
|
protected $user;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new User instance.
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Database\Eloquent\Model $user
|
||||||
|
*/
|
||||||
|
public function __construct(Model $user)
|
||||||
|
{
|
||||||
|
$this->user = $user;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the user by the given key, value.
|
||||||
|
*
|
||||||
|
* @param mixed $key
|
||||||
|
* @param mixed $value
|
||||||
|
* @return Illuminate\Database\Eloquent\Model
|
||||||
|
*/
|
||||||
|
public function getBy($key, $value)
|
||||||
|
{
|
||||||
|
return $this->user->where($key, $value)->first();
|
||||||
|
}
|
||||||
|
}
|
24
src/Providers/User/UserInterface.php
Normal file
24
src/Providers/User/UserInterface.php
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of jwt-auth.
|
||||||
|
*
|
||||||
|
* (c) Sean Tymon <tymon148@gmail.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Tymon\JWTAuth\Providers\User;
|
||||||
|
|
||||||
|
interface UserInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Get the user by the given key, value.
|
||||||
|
*
|
||||||
|
* @param string $key
|
||||||
|
* @param mixed $value
|
||||||
|
* @return Illuminate\Database\Eloquent\Model|null
|
||||||
|
*/
|
||||||
|
public function getBy($key, $value);
|
||||||
|
}
|
54
src/Token.php
Normal file
54
src/Token.php
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of jwt-auth.
|
||||||
|
*
|
||||||
|
* (c) Sean Tymon <tymon148@gmail.com>
|
||||||
|
*
|
||||||
|
* 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\Validators\TokenValidator;
|
||||||
|
|
||||||
|
class Token
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $value;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new JSON Web Token.
|
||||||
|
*
|
||||||
|
* @param string $value
|
||||||
|
*/
|
||||||
|
public function __construct($value)
|
||||||
|
{
|
||||||
|
with(new TokenValidator)->check($value);
|
||||||
|
|
||||||
|
$this->value = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the token.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function get()
|
||||||
|
{
|
||||||
|
return $this->value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the token when casting to string.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function __toString()
|
||||||
|
{
|
||||||
|
return (string) $this->value;
|
||||||
|
}
|
||||||
|
}
|
38
src/Utils.php
Normal file
38
src/Utils.php
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of jwt-auth.
|
||||||
|
*
|
||||||
|
* (c) Sean Tymon <tymon148@gmail.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Tymon\JWTAuth;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
|
|
||||||
|
class Utils
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Get the Carbon instance for the current time.
|
||||||
|
*
|
||||||
|
* @return \Carbon\Carbon
|
||||||
|
*/
|
||||||
|
public static function now()
|
||||||
|
{
|
||||||
|
return Carbon::now();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the Carbon instance for the timestamp.
|
||||||
|
*
|
||||||
|
* @param int $timestamp
|
||||||
|
* @return \Carbon\Carbon
|
||||||
|
*/
|
||||||
|
public static function timestamp($timestamp)
|
||||||
|
{
|
||||||
|
return Carbon::createFromTimeStampUTC($timestamp);
|
||||||
|
}
|
||||||
|
}
|
52
src/Validators/AbstractValidator.php
Normal file
52
src/Validators/AbstractValidator.php
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of jwt-auth.
|
||||||
|
*
|
||||||
|
* (c) Sean Tymon <tymon148@gmail.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Tymon\JWTAuth\Validators;
|
||||||
|
|
||||||
|
use Tymon\JWTAuth\Exceptions\JWTException;
|
||||||
|
|
||||||
|
abstract class AbstractValidator implements ValidatorInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
protected $refreshFlow = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function to return a boolean.
|
||||||
|
*
|
||||||
|
* @param array $value
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isValid($value)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$this->check($value);
|
||||||
|
} catch (JWTException $e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the refresh flow flag.
|
||||||
|
*
|
||||||
|
* @param bool $refreshFlow
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setRefreshFlow($refreshFlow = true)
|
||||||
|
{
|
||||||
|
$this->refreshFlow = $refreshFlow;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
}
|
127
src/Validators/PayloadValidator.php
Normal file
127
src/Validators/PayloadValidator.php
Normal file
|
@ -0,0 +1,127 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of jwt-auth.
|
||||||
|
*
|
||||||
|
* (c) Sean Tymon <tymon148@gmail.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Tymon\JWTAuth\Validators;
|
||||||
|
|
||||||
|
use Tymon\JWTAuth\Utils;
|
||||||
|
use Tymon\JWTAuth\Exceptions\TokenExpiredException;
|
||||||
|
use Tymon\JWTAuth\Exceptions\TokenInvalidException;
|
||||||
|
|
||||||
|
class PayloadValidator extends AbstractValidator
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $requiredClaims = ['iss', 'iat', 'exp', 'nbf', 'sub', 'jti'];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
protected $refreshTTL = 20160;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run the validations on the payload array.
|
||||||
|
*
|
||||||
|
* @param array $value
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function check($value)
|
||||||
|
{
|
||||||
|
$this->validateStructure($value);
|
||||||
|
|
||||||
|
if (! $this->refreshFlow) {
|
||||||
|
$this->validateTimestamps($value);
|
||||||
|
} else {
|
||||||
|
$this->validateRefresh($value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensure the payload contains the required claims and
|
||||||
|
* the claims have the relevant type.
|
||||||
|
*
|
||||||
|
* @param array $payload
|
||||||
|
* @throws \Tymon\JWTAuth\Exceptions\TokenInvalidException
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
protected function validateStructure(array $payload)
|
||||||
|
{
|
||||||
|
if (count(array_diff($this->requiredClaims, array_keys($payload))) !== 0) {
|
||||||
|
throw new TokenInvalidException('JWT payload does not contain the required claims');
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate the payload timestamps.
|
||||||
|
*
|
||||||
|
* @param array $payload
|
||||||
|
* @throws \Tymon\JWTAuth\Exceptions\TokenExpiredException
|
||||||
|
* @throws \Tymon\JWTAuth\Exceptions\TokenInvalidException
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
protected function validateTimestamps(array $payload)
|
||||||
|
{
|
||||||
|
if (isset($payload['nbf']) && Utils::timestamp($payload['nbf'])->isFuture()) {
|
||||||
|
throw new TokenInvalidException('Not Before (nbf) timestamp cannot be in the future', 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($payload['iat']) && Utils::timestamp($payload['iat'])->isFuture()) {
|
||||||
|
throw new TokenInvalidException('Issued At (iat) timestamp cannot be in the future', 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Utils::timestamp($payload['exp'])->isPast()) {
|
||||||
|
throw new TokenExpiredException('Token has expired');
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check the token in the refresh flow context.
|
||||||
|
*
|
||||||
|
* @param $payload
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
protected function validateRefresh(array $payload)
|
||||||
|
{
|
||||||
|
if (isset($payload['iat']) && Utils::timestamp($payload['iat'])->addMinutes($this->refreshTTL)->isPast()) {
|
||||||
|
throw new TokenExpiredException('Token has expired and can no longer be refreshed', 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the required claims.
|
||||||
|
*
|
||||||
|
* @param array $claims
|
||||||
|
*/
|
||||||
|
public function setRequiredClaims(array $claims)
|
||||||
|
{
|
||||||
|
$this->requiredClaims = $claims;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the refresh ttl.
|
||||||
|
*
|
||||||
|
* @param int $ttl
|
||||||
|
*/
|
||||||
|
public function setRefreshTTL($ttl)
|
||||||
|
{
|
||||||
|
$this->refreshTTL = $ttl;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
}
|
42
src/Validators/TokenValidator.php
Normal file
42
src/Validators/TokenValidator.php
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of jwt-auth.
|
||||||
|
*
|
||||||
|
* (c) Sean Tymon <tymon148@gmail.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Tymon\JWTAuth\Validators;
|
||||||
|
|
||||||
|
use Tymon\JWTAuth\Exceptions\TokenInvalidException;
|
||||||
|
|
||||||
|
class TokenValidator extends AbstractValidator
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Check the structure of the token.
|
||||||
|
*
|
||||||
|
* @param string $value
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function check($value)
|
||||||
|
{
|
||||||
|
$this->validateStructure($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $token
|
||||||
|
* @throws \Tymon\JWTAuth\Exceptions\TokenInvalidException
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
protected function validateStructure($token)
|
||||||
|
{
|
||||||
|
if (count(explode('.', $token)) !== 3) {
|
||||||
|
throw new TokenInvalidException('Wrong number of segments');
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
31
src/Validators/ValidatorInterface.php
Normal file
31
src/Validators/ValidatorInterface.php
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of jwt-auth.
|
||||||
|
*
|
||||||
|
* (c) Sean Tymon <tymon148@gmail.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Tymon\JWTAuth\Validators;
|
||||||
|
|
||||||
|
interface ValidatorInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Perform some checks on the value.
|
||||||
|
*
|
||||||
|
* @param mixed $value
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function check($value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function to return a boolean.
|
||||||
|
*
|
||||||
|
* @param array $value
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isValid($value);
|
||||||
|
}
|
173
src/config/config.php
Normal file
173
src/config/config.php
Normal file
|
@ -0,0 +1,173 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of jwt-auth.
|
||||||
|
*
|
||||||
|
* (c) Sean Tymon <tymon148@gmail.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
return [
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| JWT Authentication Secret
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Don't forget to set this, as it will be used to sign your tokens.
|
||||||
|
| A helper command is provided for this: `php artisan jwt:generate`
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'secret' => env('JWT_SECRET', 'changeme'),
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| JWT time to live
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Specify the length of time (in minutes) that the token will be valid for.
|
||||||
|
| Defaults to 1 hour
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'ttl' => 60,
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Refresh time to live
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| 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
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'refresh_ttl' => 20160,
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| JWT hashing algorithm
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Specify the hashing algorithm that will be used to sign the token.
|
||||||
|
|
|
||||||
|
| See here: https://github.com/namshi/jose/tree/2.2.0/src/Namshi/JOSE/Signer
|
||||||
|
| for possible values
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'algo' => 'HS256',
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| User Model namespace
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Specify the full namespace to your User model.
|
||||||
|
| e.g. 'Acme\Entities\User'
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'user' => 'App\User',
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| User identifier
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Specify a unique property of the user that will be added as the 'sub'
|
||||||
|
| claim of the token payload.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'identifier' => 'id',
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Required Claims
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Specify the required claims that must exist in any token.
|
||||||
|
| A TokenInvalidException will be thrown if any of these claims are not
|
||||||
|
| present in the payload.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'required_claims' => ['iss', 'iat', 'exp', 'nbf', 'sub', 'jti'],
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Blacklist Enabled
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| In order to invalidate tokens, you must have the blacklist enabled.
|
||||||
|
| If you do not want or need this functionality, then set this to false.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'blacklist_enabled' => env('JWT_BLACKLIST_ENABLED', true),
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Providers
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Specify the various providers used throughout the package.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'providers' => [
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| User Provider
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Specify the provider that is used to find the user based
|
||||||
|
| on the subject claim
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'user' => 'Tymon\JWTAuth\Providers\User\EloquentUserAdapter',
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| JWT Provider
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Specify the provider that is used to create and decode the tokens.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'jwt' => 'Tymon\JWTAuth\Providers\JWT\NamshiAdapter',
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Authentication Provider
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Specify the provider that is used to authenticate users.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'auth' => 'Tymon\JWTAuth\Providers\Auth\IlluminateAuthAdapter',
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Storage Provider
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Specify the provider that is used to store tokens in the blacklist
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'storage' => 'Tymon\JWTAuth\Providers\Storage\IlluminateCacheAdapter',
|
||||||
|
|
||||||
|
],
|
||||||
|
|
||||||
|
];
|
153
tests/BlacklistTest.php
Normal file
153
tests/BlacklistTest.php
Normal file
|
@ -0,0 +1,153 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of jwt-auth.
|
||||||
|
*
|
||||||
|
* (c) Sean Tymon <tymon148@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 Carbon\Carbon;
|
||||||
|
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\IssuedAt;
|
||||||
|
use Tymon\JWTAuth\Claims\NotBefore;
|
||||||
|
use Tymon\JWTAuth\Claims\Expiration;
|
||||||
|
|
||||||
|
class BlacklistTest extends \PHPUnit_Framework_TestCase
|
||||||
|
{
|
||||||
|
public function setUp()
|
||||||
|
{
|
||||||
|
Carbon::setTestNow(Carbon::createFromTimeStampUTC(123));
|
||||||
|
|
||||||
|
$this->storage = Mockery::mock('Tymon\JWTAuth\Providers\Storage\StorageInterface');
|
||||||
|
$this->blacklist = new Blacklist($this->storage);
|
||||||
|
$this->blacklist->setRefreshTTL(20160);
|
||||||
|
|
||||||
|
$this->validator = Mockery::mock('Tymon\JWTAuth\Validators\PayloadValidator');
|
||||||
|
$this->validator->shouldReceive('setRefreshFlow->check');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function tearDown()
|
||||||
|
{
|
||||||
|
Mockery::close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function it_should_add_a_valid_token_to_the_blacklist()
|
||||||
|
{
|
||||||
|
$claims = [
|
||||||
|
new Subject(1),
|
||||||
|
new Issuer('http://example.com'),
|
||||||
|
new Expiration(100 + 3600),
|
||||||
|
new NotBefore(100),
|
||||||
|
new IssuedAt(100),
|
||||||
|
new JwtId('foo'),
|
||||||
|
];
|
||||||
|
$payload = new Payload($claims, $this->validator);
|
||||||
|
|
||||||
|
$this->storage->shouldReceive('add')->once()->with('foo', [], 20160);
|
||||||
|
$this->assertTrue($this->blacklist->add($payload));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function it_should_return_true_when_adding_a_refreshable_expired_token_to_the_blacklist()
|
||||||
|
{
|
||||||
|
$claims = [
|
||||||
|
new Subject(1),
|
||||||
|
new Issuer('http://example.com'),
|
||||||
|
new Expiration(101),
|
||||||
|
new NotBefore(100),
|
||||||
|
new IssuedAt(100),
|
||||||
|
new JwtId('foo'),
|
||||||
|
];
|
||||||
|
$payload = new Payload($claims, $this->validator, true);
|
||||||
|
|
||||||
|
$this->storage->shouldReceive('add')->once()->with('foo', [], 20160);
|
||||||
|
$this->assertTrue($this->blacklist->add($payload));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function it_should_return_false_when_adding_an_unrefreshable_token_to_the_blacklist()
|
||||||
|
{
|
||||||
|
$claims = [
|
||||||
|
new Subject(1),
|
||||||
|
new Issuer('http://example.com'),
|
||||||
|
new Expiration(100), // default refresh_ttl
|
||||||
|
new NotBefore(100),
|
||||||
|
new IssuedAt(100 - 20160 * 60),
|
||||||
|
new JwtId('foo'),
|
||||||
|
];
|
||||||
|
$payload = new Payload($claims, $this->validator, true);
|
||||||
|
|
||||||
|
$this->storage->shouldReceive('add')->never();
|
||||||
|
$this->assertFalse($this->blacklist->add($payload));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function it_should_return_false_when_adding_a_unrefreshable_token_after_modifying_refresh_ttl()
|
||||||
|
{
|
||||||
|
$claims = [
|
||||||
|
new Subject(1),
|
||||||
|
new Issuer('http://example.com'),
|
||||||
|
new Expiration(101),
|
||||||
|
new NotBefore(100),
|
||||||
|
new IssuedAt(100),
|
||||||
|
new JwtId('foo'),
|
||||||
|
];
|
||||||
|
$payload = new Payload($claims, $this->validator, true);
|
||||||
|
|
||||||
|
$this->storage->shouldReceive('add')->never();
|
||||||
|
$this->blacklist->setRefreshTTL(0);
|
||||||
|
$this->assertFalse($this->blacklist->add($payload));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function it_should_check_whether_a_token_has_been_blacklisted()
|
||||||
|
{
|
||||||
|
$claims = [
|
||||||
|
new Subject(1),
|
||||||
|
new Issuer('http://example.com'),
|
||||||
|
new Expiration(123 + 3600),
|
||||||
|
new NotBefore(123),
|
||||||
|
new IssuedAt(123),
|
||||||
|
new JwtId('foobar'),
|
||||||
|
];
|
||||||
|
$payload = new Payload($claims, $this->validator);
|
||||||
|
|
||||||
|
$this->storage->shouldReceive('has')->once()->with('foobar')->andReturn(true);
|
||||||
|
$this->assertTrue($this->blacklist->has($payload));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function it_should_remove_a_token_from_the_blacklist()
|
||||||
|
{
|
||||||
|
$claims = [
|
||||||
|
new Subject(1),
|
||||||
|
new Issuer('http://example.com'),
|
||||||
|
new Expiration(123 + 3600),
|
||||||
|
new NotBefore(123),
|
||||||
|
new IssuedAt(123),
|
||||||
|
new JwtId('foobar'),
|
||||||
|
];
|
||||||
|
$payload = new Payload($claims, $this->validator);
|
||||||
|
|
||||||
|
$this->storage->shouldReceive('destroy')->once()->with('foobar')->andReturn(true);
|
||||||
|
$this->assertTrue($this->blacklist->remove($payload));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function it_should_empty_the_blacklist()
|
||||||
|
{
|
||||||
|
$this->storage->shouldReceive('flush')->once();
|
||||||
|
$this->assertTrue($this->blacklist->clear());
|
||||||
|
}
|
||||||
|
}
|
44
tests/Commands/JWTGenerateCommandTest.php
Normal file
44
tests/Commands/JWTGenerateCommandTest.php
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of jwt-auth.
|
||||||
|
*
|
||||||
|
* (c) Sean Tymon <tymon148@gmail.com>
|
||||||
|
*
|
||||||
|
* 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\Foundation\Application;
|
||||||
|
use Tymon\JWTAuth\Commands\JWTGenerateCommand;
|
||||||
|
use Symfony\Component\Console\Input\ArrayInput;
|
||||||
|
use Symfony\Component\Console\Output\NullOutput;
|
||||||
|
use Symfony\Component\Console\Tester\CommandTester;
|
||||||
|
|
||||||
|
class JWTGenerateCommandTest extends \PHPUnit_Framework_TestCase
|
||||||
|
{
|
||||||
|
public function setUp()
|
||||||
|
{
|
||||||
|
$this->command = new JWTGenerateCommand();
|
||||||
|
$this->tester = new CommandTester($this->command);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function it_shoud_generate_random_key()
|
||||||
|
{
|
||||||
|
// $app = new Application();
|
||||||
|
|
||||||
|
// $app['path.base'] = '';
|
||||||
|
|
||||||
|
// $this->command->setLaravel($app);
|
||||||
|
|
||||||
|
// $this->runCommand($this->command);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function runCommand($command, $input = [])
|
||||||
|
{
|
||||||
|
return $command->run(new ArrayInput($input), new NullOutput);
|
||||||
|
}
|
||||||
|
}
|
235
tests/JWTAuthTest.php
Normal file
235
tests/JWTAuthTest.php
Normal file
|
@ -0,0 +1,235 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of jwt-auth.
|
||||||
|
*
|
||||||
|
* (c) Sean Tymon <tymon148@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 Tymon\JWTAuth\Token;
|
||||||
|
use Tymon\JWTAuth\JWTAuth;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
class JWTAuthTest extends \PHPUnit_Framework_TestCase
|
||||||
|
{
|
||||||
|
public function setUp()
|
||||||
|
{
|
||||||
|
$this->user = Mockery::mock('Tymon\JWTAuth\Providers\User\UserInterface');
|
||||||
|
$this->manager = Mockery::mock('Tymon\JWTAuth\JWTManager');
|
||||||
|
$this->auth = Mockery::mock('Tymon\JWTAuth\Providers\Auth\AuthInterface');
|
||||||
|
|
||||||
|
$this->jwtAuth = new JWTAuth($this->manager, $this->user, $this->auth, Request::create('/foo', 'GET'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function tearDown()
|
||||||
|
{
|
||||||
|
Mockery::close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function it_should_return_a_user_when_passing_a_token_containing_a_valid_subject_claim()
|
||||||
|
{
|
||||||
|
$payload = Mockery::mock('Tymon\JWTAuth\Payload');
|
||||||
|
$payload->shouldReceive('offsetGet')->once()->andReturn(1);
|
||||||
|
|
||||||
|
$this->manager->shouldReceive('decode')->once()->andReturn($payload);
|
||||||
|
$this->user->shouldReceive('getBy')->once()->andReturn((object) ['id' => 1]);
|
||||||
|
|
||||||
|
$user = $this->jwtAuth->toUser('foo.bar.baz');
|
||||||
|
|
||||||
|
$this->assertEquals(1, $user->id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function it_should_return_false_when_passing_a_token_containing_an_invalid_subject_claim()
|
||||||
|
{
|
||||||
|
$payload = Mockery::mock('Tymon\JWTAuth\Payload');
|
||||||
|
$payload->shouldReceive('offsetGet')->once()->andReturn(1);
|
||||||
|
|
||||||
|
$this->manager->shouldReceive('decode')->once()->andReturn($payload);
|
||||||
|
$this->user->shouldReceive('getBy')->once()->andReturn(false);
|
||||||
|
|
||||||
|
$user = $this->jwtAuth->toUser('foo.bar.baz');
|
||||||
|
|
||||||
|
$this->assertFalse($user);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function it_should_return_a_token_when_passing_a_user()
|
||||||
|
{
|
||||||
|
$this->manager->shouldReceive('getPayloadFactory->make')->once()->andReturn(Mockery::mock('Tymon\JWTAuth\Payload'));
|
||||||
|
$this->manager->shouldReceive('encode->get')->once()->andReturn('foo.bar.baz');
|
||||||
|
|
||||||
|
$token = $this->jwtAuth->fromUser((object) ['id' => 1]);
|
||||||
|
|
||||||
|
$this->assertEquals($token, 'foo.bar.baz');
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function it_should_return_a_token_when_passing_valid_credentials_to_attempt_method()
|
||||||
|
{
|
||||||
|
$this->manager->shouldReceive('getPayloadFactory->make')->once()->andReturn(Mockery::mock('Tymon\JWTAuth\Payload'));
|
||||||
|
$this->manager->shouldReceive('encode->get')->once()->andReturn('foo.bar.baz');
|
||||||
|
|
||||||
|
$this->auth->shouldReceive('byCredentials')->once()->andReturn(true);
|
||||||
|
$this->auth->shouldReceive('user')->once()->andReturn((object) ['id' => 1]);
|
||||||
|
|
||||||
|
$token = $this->jwtAuth->attempt();
|
||||||
|
|
||||||
|
$this->assertEquals($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();
|
||||||
|
|
||||||
|
$this->assertFalse($token);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function it_should_throw_an_exception_when_not_providing_a_token()
|
||||||
|
{
|
||||||
|
$this->setExpectedException('Tymon\JWTAuth\Exceptions\JWTException');
|
||||||
|
|
||||||
|
$this->jwtAuth->toUser();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function it_should_return_the_owning_user_from_a_token_containing_an_existing_user()
|
||||||
|
{
|
||||||
|
$payload = Mockery::mock('Tymon\JWTAuth\Payload');
|
||||||
|
$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->authenticate('foo.bar.baz');
|
||||||
|
|
||||||
|
$this->assertEquals($user->id, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function it_should_return_false_when_passing_a_token_not_containing_an_existing_user()
|
||||||
|
{
|
||||||
|
$payload = Mockery::mock('Tymon\JWTAuth\Payload');
|
||||||
|
$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->authenticate('foo.bar.baz');
|
||||||
|
|
||||||
|
$this->assertFalse($user);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function it_should_refresh_a_token()
|
||||||
|
{
|
||||||
|
$newToken = Mockery::mock('Tymon\JWTAuth\Token');
|
||||||
|
$newToken->shouldReceive('get')->once()->andReturn('baz.bar.foo');
|
||||||
|
|
||||||
|
$this->manager->shouldReceive('refresh')->once()->andReturn($newToken);
|
||||||
|
|
||||||
|
$result = $this->jwtAuth->setToken('foo.bar.baz')->refresh();
|
||||||
|
|
||||||
|
$this->assertEquals($result, 'baz.bar.foo');
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function it_should_invalidate_a_token()
|
||||||
|
{
|
||||||
|
$this->manager->shouldReceive('invalidate')->once()->andReturn(true);
|
||||||
|
|
||||||
|
$result = $this->jwtAuth->invalidate('foo.bar.baz');
|
||||||
|
|
||||||
|
$this->assertTrue($result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function it_should_retrieve_the_token_from_the_auth_header()
|
||||||
|
{
|
||||||
|
$request = Request::create('/foo', 'GET');
|
||||||
|
$request->headers->set('authorization', 'Bearer foo.bar.baz');
|
||||||
|
$jwtAuth = new JWTAuth($this->manager, $this->user, $this->auth, $request);
|
||||||
|
|
||||||
|
$this->assertInstanceOf('Tymon\JWTAuth\Token', $jwtAuth->parseToken()->getToken());
|
||||||
|
$this->assertEquals($jwtAuth->getToken(), 'foo.bar.baz');
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function it_should_retrieve_the_token_from_the_query_string()
|
||||||
|
{
|
||||||
|
$request = Request::create('/foo', 'GET', ['token' => 'foo.bar.baz']);
|
||||||
|
$jwtAuth = new JWTAuth($this->manager, $this->user, $this->auth, $request);
|
||||||
|
|
||||||
|
$this->assertInstanceOf('Tymon\JWTAuth\Token', $jwtAuth->parseToken()->getToken());
|
||||||
|
$this->assertEquals($jwtAuth->getToken(), 'foo.bar.baz');
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function it_should_throw_an_exception_when_token_not_present_in_request()
|
||||||
|
{
|
||||||
|
$this->setExpectedException('Tymon\JWTAuth\Exceptions\JWTException');
|
||||||
|
|
||||||
|
$request = Request::create('/foo', 'GET');
|
||||||
|
$jwtAuth = new JWTAuth($this->manager, $this->user, $this->auth, $request);
|
||||||
|
|
||||||
|
$jwtAuth->parseToken();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function it_should_return_false_when_no_token_is_set()
|
||||||
|
{
|
||||||
|
$this->assertFalse($this->jwtAuth->getToken());
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function it_should_set_the_identifier()
|
||||||
|
{
|
||||||
|
$this->jwtAuth->setIdentifier('foo');
|
||||||
|
|
||||||
|
$this->assertEquals($this->jwtAuth->getIdentifier(), 'foo');
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function it_should_magically_call_the_manager()
|
||||||
|
{
|
||||||
|
$this->manager->shouldReceive('getBlacklist')->andReturn(new \StdClass);
|
||||||
|
|
||||||
|
$blacklist = $this->jwtAuth->getBlacklist();
|
||||||
|
|
||||||
|
$this->assertInstanceOf('StdClass', $blacklist);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function it_should_set_the_request()
|
||||||
|
{
|
||||||
|
$request = Request::create('/foo', 'GET', ['token' => 'some.random.token']);
|
||||||
|
|
||||||
|
$token = $this->jwtAuth->setRequest($request)->getToken();
|
||||||
|
|
||||||
|
$this->assertEquals('some.random.token', $token);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function it_should_get_the_manager_instance()
|
||||||
|
{
|
||||||
|
$manager = $this->jwtAuth->manager();
|
||||||
|
$this->assertInstanceOf('Tymon\JWTAuth\JWTManager', $manager);
|
||||||
|
}
|
||||||
|
}
|
188
tests/JWTManagerTest.php
Normal file
188
tests/JWTManagerTest.php
Normal file
|
@ -0,0 +1,188 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of jwt-auth.
|
||||||
|
*
|
||||||
|
* (c) Sean Tymon <tymon148@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 Tymon\JWTAuth\Token;
|
||||||
|
use Tymon\JWTAuth\Payload;
|
||||||
|
use Tymon\JWTAuth\JWTManager;
|
||||||
|
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\Expiration;
|
||||||
|
|
||||||
|
class JWTManagerTest extends \PHPUnit_Framework_TestCase
|
||||||
|
{
|
||||||
|
public function setUp()
|
||||||
|
{
|
||||||
|
$this->jwt = Mockery::mock('Tymon\JWTAuth\Providers\JWT\JWTInterface');
|
||||||
|
$this->blacklist = Mockery::mock('Tymon\JWTAuth\Blacklist');
|
||||||
|
$this->factory = Mockery::mock('Tymon\JWTAuth\PayloadFactory');
|
||||||
|
$this->manager = new JWTManager($this->jwt, $this->blacklist, $this->factory);
|
||||||
|
|
||||||
|
$this->validator = Mockery::mock('Tymon\JWTAuth\Validators\PayloadValidator');
|
||||||
|
$this->validator->shouldReceive('setRefreshFlow->check');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function tearDown()
|
||||||
|
{
|
||||||
|
Mockery::close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function it_should_encode_a_payload()
|
||||||
|
{
|
||||||
|
$claims = [
|
||||||
|
new Subject(1),
|
||||||
|
new Issuer('http://example.com'),
|
||||||
|
new Expiration(123 + 3600),
|
||||||
|
new NotBefore(123),
|
||||||
|
new IssuedAt(123),
|
||||||
|
new JwtId('foo'),
|
||||||
|
];
|
||||||
|
$payload = new Payload($claims, $this->validator);
|
||||||
|
|
||||||
|
$this->jwt->shouldReceive('encode')->with($payload->toArray())->andReturn('foo.bar.baz');
|
||||||
|
|
||||||
|
$token = $this->manager->encode($payload);
|
||||||
|
|
||||||
|
$this->assertEquals($token, 'foo.bar.baz');
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function it_should_decode_a_token()
|
||||||
|
{
|
||||||
|
$claims = [
|
||||||
|
new Subject(1),
|
||||||
|
new Issuer('http://example.com'),
|
||||||
|
new Expiration(123 + 3600),
|
||||||
|
new NotBefore(123),
|
||||||
|
new IssuedAt(123),
|
||||||
|
new JwtId('foo'),
|
||||||
|
];
|
||||||
|
$payload = new Payload($claims, $this->validator);
|
||||||
|
$token = new Token('foo.bar.baz');
|
||||||
|
|
||||||
|
$this->jwt->shouldReceive('decode')->once()->with('foo.bar.baz')->andReturn($payload->toArray());
|
||||||
|
$this->factory->shouldReceive('setRefreshFlow->make')->with($payload->toArray())->andReturn($payload);
|
||||||
|
$this->blacklist->shouldReceive('has')->with($payload)->andReturn(false);
|
||||||
|
|
||||||
|
$payload = $this->manager->decode($token);
|
||||||
|
|
||||||
|
$this->assertInstanceOf('Tymon\JWTAuth\Payload', $payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function it_should_throw_exception_when_token_is_blacklisted()
|
||||||
|
{
|
||||||
|
$this->setExpectedException('Tymon\JWTAuth\Exceptions\TokenBlacklistedException');
|
||||||
|
|
||||||
|
$claims = [
|
||||||
|
new Subject(1),
|
||||||
|
new Issuer('http://example.com'),
|
||||||
|
new Expiration(123 + 3600),
|
||||||
|
new NotBefore(123),
|
||||||
|
new IssuedAt(123),
|
||||||
|
new JwtId('foo'),
|
||||||
|
];
|
||||||
|
$payload = new Payload($claims, $this->validator);
|
||||||
|
$token = new Token('foo.bar.baz');
|
||||||
|
|
||||||
|
$this->jwt->shouldReceive('decode')->once()->with('foo.bar.baz')->andReturn($payload->toArray());
|
||||||
|
$this->factory->shouldReceive('setRefreshFlow->make')->with($payload->toArray())->andReturn($payload);
|
||||||
|
$this->blacklist->shouldReceive('has')->with($payload)->andReturn(true);
|
||||||
|
|
||||||
|
$this->manager->decode($token);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function it_should_refresh_a_token()
|
||||||
|
{
|
||||||
|
$claims = [
|
||||||
|
new Subject(1),
|
||||||
|
new Issuer('http://example.com'),
|
||||||
|
new Expiration(123 - 3600),
|
||||||
|
new NotBefore(123),
|
||||||
|
new IssuedAt(123),
|
||||||
|
new JwtId('foo'),
|
||||||
|
];
|
||||||
|
$payload = new Payload($claims, $this->validator, true);
|
||||||
|
$token = new Token('foo.bar.baz');
|
||||||
|
|
||||||
|
$this->jwt->shouldReceive('decode')->once()->with('foo.bar.baz')->andReturn($payload->toArray());
|
||||||
|
$this->jwt->shouldReceive('encode')->with($payload->toArray())->andReturn('baz.bar.foo');
|
||||||
|
|
||||||
|
$this->factory->shouldReceive('setRefreshFlow')->andReturn($this->factory);
|
||||||
|
$this->factory->shouldReceive('make')->andReturn($payload);
|
||||||
|
|
||||||
|
$this->blacklist->shouldReceive('has')->with($payload)->andReturn(false);
|
||||||
|
$this->blacklist->shouldReceive('add')->once()->with($payload);
|
||||||
|
|
||||||
|
$token = $this->manager->refresh($token);
|
||||||
|
|
||||||
|
$this->assertInstanceOf('Tymon\JWTAuth\Token', $token);
|
||||||
|
$this->assertEquals('baz.bar.foo', $token);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function it_should_invalidate_a_token()
|
||||||
|
{
|
||||||
|
$claims = [
|
||||||
|
new Subject(1),
|
||||||
|
new Issuer('http://example.com'),
|
||||||
|
new Expiration(123 + 3600),
|
||||||
|
new NotBefore(123),
|
||||||
|
new IssuedAt(123),
|
||||||
|
new JwtId('foo'),
|
||||||
|
];
|
||||||
|
$payload = new Payload($claims, $this->validator);
|
||||||
|
$token = new Token('foo.bar.baz');
|
||||||
|
|
||||||
|
$this->jwt->shouldReceive('decode')->once()->with('foo.bar.baz')->andReturn($payload->toArray());
|
||||||
|
$this->factory->shouldReceive('setRefreshFlow->make')->with($payload->toArray())->andReturn($payload);
|
||||||
|
$this->blacklist->shouldReceive('has')->with($payload)->andReturn(false);
|
||||||
|
|
||||||
|
$this->blacklist->shouldReceive('add')->with($payload)->andReturn(true);
|
||||||
|
|
||||||
|
$this->manager->invalidate($token);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function it_should_throw_an_exception_when_enable_blacklist_is_set_to_false()
|
||||||
|
{
|
||||||
|
$this->setExpectedException('Tymon\JWTAuth\Exceptions\JWTException');
|
||||||
|
|
||||||
|
$token = new Token('foo.bar.baz');
|
||||||
|
|
||||||
|
$this->manager->setBlacklistEnabled(false)->invalidate($token);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function it_should_get_the_payload_factory()
|
||||||
|
{
|
||||||
|
$this->assertInstanceOf('Tymon\JWTAuth\PayloadFactory', $this->manager->getPayloadFactory());
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function it_should_get_the_jwt_provider()
|
||||||
|
{
|
||||||
|
$this->assertInstanceOf('Tymon\JWTAuth\Providers\JWT\JWTInterface', $this->manager->getJWTProvider());
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function it_should_get_the_blacklist()
|
||||||
|
{
|
||||||
|
$this->assertInstanceOf('Tymon\JWTAuth\Blacklist', $this->manager->getBlacklist());
|
||||||
|
}
|
||||||
|
}
|
108
tests/Middleware/GetUserFromTokenTest.php
Normal file
108
tests/Middleware/GetUserFromTokenTest.php
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of jwt-auth.
|
||||||
|
*
|
||||||
|
* (c) Sean Tymon <tymon148@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 Tymon\JWTAuth\Middleware\GetUserFromToken;
|
||||||
|
use Tymon\JWTAuth\Exceptions\TokenExpiredException;
|
||||||
|
use Tymon\JWTAuth\Exceptions\TokenInvalidException;
|
||||||
|
|
||||||
|
class GetUserFromTokenTest extends \PHPUnit_Framework_TestCase
|
||||||
|
{
|
||||||
|
public function setUp()
|
||||||
|
{
|
||||||
|
$this->events = Mockery::mock('Illuminate\Contracts\Events\Dispatcher');
|
||||||
|
$this->auth = Mockery::mock('Tymon\JWTAuth\JWTAuth');
|
||||||
|
|
||||||
|
$this->request = Mockery::mock('Illuminate\Http\Request');
|
||||||
|
$this->response = Mockery::mock('Illuminate\Contracts\Routing\ResponseFactory');
|
||||||
|
|
||||||
|
$this->middleware = new GetUserFromToken($this->response, $this->events, $this->auth);
|
||||||
|
|
||||||
|
$this->auth->shouldReceive('setRequest')->once()->with($this->request)->andReturn($this->auth);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function tearDown()
|
||||||
|
{
|
||||||
|
Mockery::close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function it_should_fire_an_event_when_no_token_is_available()
|
||||||
|
{
|
||||||
|
$this->auth->shouldReceive('getToken')->once()->andReturn(false);
|
||||||
|
|
||||||
|
$this->events->shouldReceive('fire')->once()->with('tymon.jwt.absent', [], true);
|
||||||
|
$this->response->shouldReceive('json')->with(['error' => 'token_not_provided'], 400);
|
||||||
|
|
||||||
|
$this->middleware->handle($this->request, function () {
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function it_should_fire_an_event_when_the_token_has_expired()
|
||||||
|
{
|
||||||
|
$exception = new TokenExpiredException;
|
||||||
|
|
||||||
|
$this->auth->shouldReceive('getToken')->once()->andReturn('foo');
|
||||||
|
$this->auth->shouldReceive('authenticate')->once()->with('foo')->andThrow($exception);
|
||||||
|
|
||||||
|
$this->events->shouldReceive('fire')->once()->with('tymon.jwt.expired', [$exception], true);
|
||||||
|
$this->response->shouldReceive('json')->with(['error' => 'token_expired'], 401);
|
||||||
|
|
||||||
|
$this->middleware->handle($this->request, function () {
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function it_should_fire_an_event_when_the_token_is_invalid()
|
||||||
|
{
|
||||||
|
$exception = new TokenInvalidException;
|
||||||
|
|
||||||
|
$this->auth->shouldReceive('getToken')->once()->andReturn('foo');
|
||||||
|
$this->auth->shouldReceive('authenticate')->once()->with('foo')->andThrow($exception);
|
||||||
|
|
||||||
|
$this->events->shouldReceive('fire')->once()->with('tymon.jwt.invalid', [$exception], true);
|
||||||
|
$this->response->shouldReceive('json')->with(['error' => 'token_invalid'], 400);
|
||||||
|
|
||||||
|
$this->middleware->handle($this->request, function () {
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function it_should_fire_an_event_when_no_user_is_found()
|
||||||
|
{
|
||||||
|
$this->auth->shouldReceive('getToken')->once()->andReturn('foo');
|
||||||
|
$this->auth->shouldReceive('authenticate')->once()->with('foo')->andReturn(false);
|
||||||
|
|
||||||
|
$this->events->shouldReceive('fire')->once()->with('tymon.jwt.user_not_found', [], true);
|
||||||
|
$this->response->shouldReceive('json')->with(['error' => 'user_not_found'], 404);
|
||||||
|
|
||||||
|
$this->middleware->handle($this->request, function () {
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function it_should_fire_an_event_when_the_token_has_been_decoded_and_user_is_found()
|
||||||
|
{
|
||||||
|
$user = (object) ['id' => 1];
|
||||||
|
|
||||||
|
$this->auth->shouldReceive('getToken')->once()->andReturn('foo');
|
||||||
|
$this->auth->shouldReceive('authenticate')->once()->with('foo')->andReturn($user);
|
||||||
|
|
||||||
|
$this->events->shouldReceive('fire')->once()->with('tymon.jwt.valid', $user);
|
||||||
|
$this->response->shouldReceive('json')->never();
|
||||||
|
|
||||||
|
$this->middleware->handle($this->request, function () {
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
136
tests/PayloadFactoryTest.php
Normal file
136
tests/PayloadFactoryTest.php
Normal file
|
@ -0,0 +1,136 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of jwt-auth.
|
||||||
|
*
|
||||||
|
* (c) Sean Tymon <tymon148@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 Carbon\Carbon;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Tymon\JWTAuth\Claims\JwtId;
|
||||||
|
use Tymon\JWTAuth\Claims\Custom;
|
||||||
|
use Tymon\JWTAuth\Claims\Issuer;
|
||||||
|
use Tymon\JWTAuth\Claims\Subject;
|
||||||
|
use Tymon\JWTAuth\PayloadFactory;
|
||||||
|
use Tymon\JWTAuth\Claims\IssuedAt;
|
||||||
|
use Tymon\JWTAuth\Claims\NotBefore;
|
||||||
|
use Tymon\JWTAuth\Claims\Expiration;
|
||||||
|
|
||||||
|
class PayloadFactoryTest extends \PHPUnit_Framework_TestCase
|
||||||
|
{
|
||||||
|
public function setUp()
|
||||||
|
{
|
||||||
|
Carbon::setTestNow(Carbon::createFromTimeStampUTC(123));
|
||||||
|
|
||||||
|
$this->claimFactory = Mockery::mock('Tymon\JWTAuth\Claims\Factory');
|
||||||
|
$this->validator = Mockery::mock('Tymon\JWTAuth\Validators\PayloadValidator');
|
||||||
|
$this->factory = new PayloadFactory($this->claimFactory, Request::create('/foo', 'GET'), $this->validator);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function tearDown()
|
||||||
|
{
|
||||||
|
Mockery::close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function it_should_return_a_payload_when_passing_an_array_of_claims_to_make_method()
|
||||||
|
{
|
||||||
|
$this->validator->shouldReceive('setRefreshFlow->check');
|
||||||
|
|
||||||
|
$expTime = 123 + 3600;
|
||||||
|
|
||||||
|
$this->claimFactory->shouldReceive('get')->once()->with('sub', 1)->andReturn(new Subject(1));
|
||||||
|
$this->claimFactory->shouldReceive('get')->once()->with('iss', Mockery::any())->andReturn(new Issuer('/foo'));
|
||||||
|
$this->claimFactory->shouldReceive('get')->once()->with('iat', 123)->andReturn(new IssuedAt(123));
|
||||||
|
$this->claimFactory->shouldReceive('get')->once()->with('jti', 'foo')->andReturn(new JwtId('foo'));
|
||||||
|
$this->claimFactory->shouldReceive('get')->once()->with('nbf', 123)->andReturn(new NotBefore(123));
|
||||||
|
$this->claimFactory->shouldReceive('get')->once()->with('exp', $expTime)->andReturn(new Expiration($expTime));
|
||||||
|
|
||||||
|
$payload = $this->factory->make(['sub' => 1, 'jti' => 'foo', 'iat' => 123]);
|
||||||
|
|
||||||
|
$this->assertEquals($payload->get('sub'), 1);
|
||||||
|
$this->assertEquals($payload->get('iat'), 123);
|
||||||
|
$this->assertEquals($payload['exp'], $expTime);
|
||||||
|
|
||||||
|
$this->assertInstanceOf('Tymon\JWTAuth\Payload', $payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test **/
|
||||||
|
public function it_should_check_custom_claim_keys_accurately_and_accept_numeric_claims()
|
||||||
|
{
|
||||||
|
$this->validator->shouldReceive('setRefreshFlow->check');
|
||||||
|
|
||||||
|
$this->claimFactory->shouldReceive('get')->once()->with('iss', Mockery::any())->andReturn(new Issuer('/foo'));
|
||||||
|
$this->claimFactory->shouldReceive('get')->once()->with('exp', 123 + 3600)->andReturn(new Expiration(123 + 3600));
|
||||||
|
$this->claimFactory->shouldReceive('get')->once()->with('iat', 123)->andReturn(new IssuedAt(123));
|
||||||
|
$this->claimFactory->shouldReceive('get')->once()->with('jti', Mockery::any())->andReturn(new JwtId('foo'));
|
||||||
|
$this->claimFactory->shouldReceive('get')->once()->with('nbf', 123)->andReturn(new NotBefore(123));
|
||||||
|
$this->claimFactory->shouldReceive('get')->once()->with(1, 'claim one')->andReturn(new Custom(1, 'claim one'));
|
||||||
|
|
||||||
|
$payload = $this->factory->make([1 => 'claim one']);
|
||||||
|
|
||||||
|
// if the checker doesn't compare defaults properly, numeric-keyed claims might be ignored
|
||||||
|
$this->assertEquals('claim one', $payload->get(1));
|
||||||
|
// iat is $defaultClaims[1], so verify it wasn't skipped due to a bad k-v comparison
|
||||||
|
$this->assertEquals(123, $payload->get('iat'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function it_should_return_a_payload_when_chaining_claim_methods()
|
||||||
|
{
|
||||||
|
$this->validator->shouldReceive('setRefreshFlow->check');
|
||||||
|
|
||||||
|
$this->claimFactory->shouldReceive('get')->once()->with('sub', 1)->andReturn(new Subject(1));
|
||||||
|
$this->claimFactory->shouldReceive('get')->once()->with('iss', Mockery::any())->andReturn(new Issuer('/foo'));
|
||||||
|
$this->claimFactory->shouldReceive('get')->once()->with('exp', 123 + 3600)->andReturn(new Expiration(123 + 3600));
|
||||||
|
$this->claimFactory->shouldReceive('get')->once()->with('iat', 123)->andReturn(new IssuedAt(123));
|
||||||
|
$this->claimFactory->shouldReceive('get')->once()->with('jti', Mockery::any())->andReturn(new JwtId('foo'));
|
||||||
|
$this->claimFactory->shouldReceive('get')->once()->with('nbf', 123)->andReturn(new NotBefore(123));
|
||||||
|
$this->claimFactory->shouldReceive('get')->once()->with('foo', 'baz')->andReturn(new Custom('foo', 'baz'));
|
||||||
|
|
||||||
|
$payload = $this->factory->sub(1)->foo('baz')->make();
|
||||||
|
|
||||||
|
$this->assertEquals($payload['sub'], 1);
|
||||||
|
$this->assertEquals($payload->get('jti'), 'foo');
|
||||||
|
$this->assertEquals($payload->get('foo'), 'baz');
|
||||||
|
|
||||||
|
$this->assertInstanceOf('Tymon\JWTAuth\Payload', $payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function it_should_return_a_payload_when_passing_miltidimensional_claims()
|
||||||
|
{
|
||||||
|
$this->validator->shouldReceive('setRefreshFlow->check');
|
||||||
|
$userObject = ['name' => 'example'];
|
||||||
|
|
||||||
|
$this->claimFactory->shouldReceive('get')->once()->with('sub', $userObject)->andReturn(new Subject($userObject));
|
||||||
|
$this->claimFactory->shouldReceive('get')->once()->with('iss', Mockery::any())->andReturn(new Issuer('/foo'));
|
||||||
|
$this->claimFactory->shouldReceive('get')->once()->with('exp', Mockery::any())->andReturn(new Expiration(123 + 3600));
|
||||||
|
$this->claimFactory->shouldReceive('get')->once()->with('iat', Mockery::any())->andReturn(new IssuedAt(123));
|
||||||
|
$this->claimFactory->shouldReceive('get')->once()->with('jti', Mockery::any())->andReturn(new JwtId('foo'));
|
||||||
|
$this->claimFactory->shouldReceive('get')->once()->with('nbf', Mockery::any())->andReturn(new NotBefore(123));
|
||||||
|
$this->claimFactory->shouldReceive('get')->once()->with('foo', ['bar' => [0, 0, 0]])->andReturn(new Custom('foo', ['bar' => [0, 0, 0]]));
|
||||||
|
|
||||||
|
$payload = $this->factory->sub($userObject)->foo(['bar' => [0, 0, 0]])->make();
|
||||||
|
|
||||||
|
$this->assertEquals($payload->get('sub'), $userObject);
|
||||||
|
$this->assertEquals($payload->get('foo'), ['bar' => [0, 0, 0]]);
|
||||||
|
|
||||||
|
$this->assertInstanceOf('Tymon\JWTAuth\Payload', $payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function it_should_set_the_ttl()
|
||||||
|
{
|
||||||
|
$this->factory->setTTL(12345);
|
||||||
|
|
||||||
|
$this->assertEquals($this->factory->getTTL(), 12345);
|
||||||
|
}
|
||||||
|
}
|
136
tests/PayloadTest.php
Normal file
136
tests/PayloadTest.php
Normal file
|
@ -0,0 +1,136 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of jwt-auth.
|
||||||
|
*
|
||||||
|
* (c) Sean Tymon <tymon148@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 Carbon\Carbon;
|
||||||
|
use Tymon\JWTAuth\Payload;
|
||||||
|
use Tymon\JWTAuth\Claims\JwtId;
|
||||||
|
use Tymon\JWTAuth\Claims\Issuer;
|
||||||
|
use Tymon\JWTAuth\Claims\Subject;
|
||||||
|
use Tymon\JWTAuth\Claims\Audience;
|
||||||
|
use Tymon\JWTAuth\Claims\IssuedAt;
|
||||||
|
use Tymon\JWTAuth\Claims\NotBefore;
|
||||||
|
use Tymon\JWTAuth\Claims\Expiration;
|
||||||
|
|
||||||
|
class PayloadTest extends \PHPUnit_Framework_TestCase
|
||||||
|
{
|
||||||
|
public function setUp()
|
||||||
|
{
|
||||||
|
Carbon::setTestNow(Carbon::createFromTimeStampUTC(123));
|
||||||
|
|
||||||
|
$claims = [
|
||||||
|
new Subject(1),
|
||||||
|
new Issuer('http://example.com'),
|
||||||
|
new Expiration(123 + 3600),
|
||||||
|
new NotBefore(123),
|
||||||
|
new IssuedAt(123),
|
||||||
|
new JwtId('foo'),
|
||||||
|
];
|
||||||
|
|
||||||
|
$this->validator = Mockery::mock('Tymon\JWTAuth\Validators\PayloadValidator');
|
||||||
|
$this->validator->shouldReceive('setRefreshFlow->check');
|
||||||
|
|
||||||
|
$this->payload = new Payload($claims, $this->validator);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function tearDown()
|
||||||
|
{
|
||||||
|
Mockery::close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function it_throws_an_exception_when_trying_to_add_to_the_payload()
|
||||||
|
{
|
||||||
|
$this->setExpectedException('Tymon\JWTAuth\Exceptions\PayloadException');
|
||||||
|
|
||||||
|
$this->payload['foo'] = 'bar';
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function it_throws_an_exception_when_trying_to_remove_a_key_from_the_payload()
|
||||||
|
{
|
||||||
|
$this->setExpectedException('Tymon\JWTAuth\Exceptions\PayloadException');
|
||||||
|
|
||||||
|
unset($this->payload['foo']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function it_should_cast_the_payload_to_a_string_as_json()
|
||||||
|
{
|
||||||
|
$this->assertEquals((string) $this->payload, json_encode($this->payload->get()));
|
||||||
|
$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->assertEquals($this->payload['sub'], 1);
|
||||||
|
$this->assertArrayHasKey('exp', $this->payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function it_should_get_properties_of_payload_via_get_method()
|
||||||
|
{
|
||||||
|
$this->assertInternalType('array', $this->payload->get());
|
||||||
|
$this->assertEquals($this->payload->get('sub'), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function it_should_get_multiple_properties_when_passing_an_array_to_the_get_method()
|
||||||
|
{
|
||||||
|
$values = $this->payload->get(['sub', 'jti']);
|
||||||
|
|
||||||
|
list($sub, $jti) = $values;
|
||||||
|
|
||||||
|
$this->assertInternalType('array', $values);
|
||||||
|
$this->assertEquals($sub, 1);
|
||||||
|
$this->assertEquals($jti, 'foo');
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function it_should_determine_whether_the_payload_has_a_claim()
|
||||||
|
{
|
||||||
|
$this->assertTrue($this->payload->has(new Subject(1)));
|
||||||
|
$this->assertFalse($this->payload->has(new Audience(1)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function it_should_magically_get_a_property()
|
||||||
|
{
|
||||||
|
$sub = $this->payload->getSubject();
|
||||||
|
$jti = $this->payload->getJwtId();
|
||||||
|
$iss = $this->payload->getIssuer();
|
||||||
|
|
||||||
|
$this->assertEquals($sub, 1);
|
||||||
|
$this->assertEquals($jti, 'foo');
|
||||||
|
$this->assertEquals($iss, 'http://example.com');
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function it_should_throw_an_exception_when_magically_getting_a_property_that_does_not_exist()
|
||||||
|
{
|
||||||
|
$this->setExpectedException('\BadMethodCallException');
|
||||||
|
|
||||||
|
$this->payload->getFoo();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function it_should_get_the_claims()
|
||||||
|
{
|
||||||
|
$claims = $this->payload->getClaims();
|
||||||
|
|
||||||
|
$this->assertInstanceOf('Tymon\JWTAuth\Claims\Expiration', $claims[2]);
|
||||||
|
$this->assertInstanceOf('Tymon\JWTAuth\Claims\JwtId', $claims[5]);
|
||||||
|
}
|
||||||
|
}
|
65
tests/Providers/Auth/IlluminateAuthAdapterTest.php
Normal file
65
tests/Providers/Auth/IlluminateAuthAdapterTest.php
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of jwt-auth.
|
||||||
|
*
|
||||||
|
* (c) Sean Tymon <tymon148@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 Tymon\JWTAuth\Providers\Auth\IlluminateAuthAdapter;
|
||||||
|
|
||||||
|
class IlluminateAuthAdapterTest extends \PHPUnit_Framework_TestCase
|
||||||
|
{
|
||||||
|
public function setUp()
|
||||||
|
{
|
||||||
|
$this->authManager = Mockery::mock('Illuminate\Auth\AuthManager');
|
||||||
|
$this->auth = new IlluminateAuthAdapter($this->authManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function tearDown()
|
||||||
|
{
|
||||||
|
Mockery::close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @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_bubble_exceptions_from_auth()
|
||||||
|
{
|
||||||
|
$this->authManager->shouldReceive('onceUsingId')->once()->with(123)->andThrow(new \Exception('Some auth failure'));
|
||||||
|
$this->setExpectedException('Exception', 'Some auth failure');
|
||||||
|
$this->auth->byId(123);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function it_should_return_the_currently_authenticated_user()
|
||||||
|
{
|
||||||
|
$this->authManager->shouldReceive('user')->once()->andReturn((object) ['id' => 1]);
|
||||||
|
$this->assertEquals($this->auth->user()->id, 1);
|
||||||
|
}
|
||||||
|
}
|
36
tests/Providers/JWT/JWTProviderTest.php
Normal file
36
tests/Providers/JWT/JWTProviderTest.php
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of jwt-auth.
|
||||||
|
*
|
||||||
|
* (c) Sean Tymon <tymon148@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 Tymon\JWTAuth\Test\Stubs\JWTProviderStub;
|
||||||
|
|
||||||
|
class JWTProviderTest extends \PHPUnit_Framework_TestCase
|
||||||
|
{
|
||||||
|
public function setUp()
|
||||||
|
{
|
||||||
|
$this->provider = new JWTProviderStub('secret', 'HS256');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function tearDown()
|
||||||
|
{
|
||||||
|
Mockery::close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function it_should_set_the_algo()
|
||||||
|
{
|
||||||
|
$this->provider->setAlgo('HS512');
|
||||||
|
|
||||||
|
$this->assertEquals('HS512', $this->provider->getAlgo());
|
||||||
|
}
|
||||||
|
}
|
77
tests/Providers/JWT/NamshiAdapterTest.php
Normal file
77
tests/Providers/JWT/NamshiAdapterTest.php
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of jwt-auth.
|
||||||
|
*
|
||||||
|
* (c) Sean Tymon <tymon148@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 Carbon\Carbon;
|
||||||
|
use Tymon\JWTAuth\Providers\JWT\NamshiAdapter;
|
||||||
|
|
||||||
|
class NamshiAdapterTest extends \PHPUnit_Framework_TestCase
|
||||||
|
{
|
||||||
|
public function setUp()
|
||||||
|
{
|
||||||
|
Carbon::setTestNow(Carbon::createFromTimeStampUTC(123));
|
||||||
|
|
||||||
|
$this->jws = Mockery::mock('Namshi\JOSE\JWS');
|
||||||
|
$this->provider = new NamshiAdapter('secret', 'HS256', $this->jws);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function tearDown()
|
||||||
|
{
|
||||||
|
Mockery::close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function it_should_return_the_token_when_passing_a_valid_subject_to_encode()
|
||||||
|
{
|
||||||
|
$payload = ['sub' => 1, 'exp' => 123, 'iat' => 123, 'iss' => '/foo'];
|
||||||
|
|
||||||
|
$this->jws->shouldReceive('setPayload')->once()->with($payload)->andReturn(Mockery::self());
|
||||||
|
$this->jws->shouldReceive('sign')->once()->with('secret')->andReturn(Mockery::self());
|
||||||
|
$this->jws->shouldReceive('getTokenString')->once()->andReturn('foo.bar.baz');
|
||||||
|
|
||||||
|
$token = $this->provider->encode($payload);
|
||||||
|
|
||||||
|
$this->assertEquals('foo.bar.baz', $token);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function it_should_throw_an_invalid_exception_when_the_payload_could_not_be_encoded()
|
||||||
|
{
|
||||||
|
$this->setExpectedException('Tymon\JWTAuth\Exceptions\JWTException');
|
||||||
|
|
||||||
|
$this->jws->shouldReceive('sign')->andThrow(new \Exception);
|
||||||
|
|
||||||
|
$payload = ['sub' => 1, 'exp' => 123, 'iat' => 123, 'iss' => '/foo'];
|
||||||
|
$this->provider->encode($payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
// public function it_should_return_the_payload_when_passing_a_valid_token_to_decode()
|
||||||
|
// {
|
||||||
|
// $this->jws->shouldReceive('load')->once()->with('foo.bar.baz')->andReturn(true);
|
||||||
|
// $this->jws->shouldReceive('verify')->andReturn(true);
|
||||||
|
|
||||||
|
// $payload = $this->provider->decode('foo.bar.baz');
|
||||||
|
|
||||||
|
// }
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function it_should_throw_a_token_invalid_exception_when_the_token_could_not_be_decoded()
|
||||||
|
{
|
||||||
|
$this->setExpectedException('Tymon\JWTAuth\Exceptions\TokenInvalidException');
|
||||||
|
|
||||||
|
$this->jws->shouldReceive('verify')->andReturn(false);
|
||||||
|
|
||||||
|
$token = $this->provider->decode('foo');
|
||||||
|
}
|
||||||
|
}
|
63
tests/Providers/Storage/IlluminateCacheAdapterTest.php
Normal file
63
tests/Providers/Storage/IlluminateCacheAdapterTest.php
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of jwt-auth.
|
||||||
|
*
|
||||||
|
* (c) Sean Tymon <tymon148@gmail.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Tymon\JWTAuth\Test\Providers\Storage;
|
||||||
|
|
||||||
|
use Mockery;
|
||||||
|
use Tymon\JWTAuth\Providers\Storage\IlluminateCacheAdapter;
|
||||||
|
|
||||||
|
class IlluminateCacheAdapterTest extends \PHPUnit_Framework_TestCase
|
||||||
|
{
|
||||||
|
public function setUp()
|
||||||
|
{
|
||||||
|
$this->cache = Mockery::mock('Illuminate\Cache\CacheManager');
|
||||||
|
$this->storage = new IlluminateCacheAdapter($this->cache);
|
||||||
|
|
||||||
|
$this->cache->shouldReceive('tags')->andReturn(Mockery::self());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function tearDown()
|
||||||
|
{
|
||||||
|
Mockery::close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function it_should_add_the_item_to_storage()
|
||||||
|
{
|
||||||
|
$this->cache->shouldReceive('tags->put')->with('foo', 'bar', 10);
|
||||||
|
|
||||||
|
$this->storage->add('foo', 'bar', 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function it_should_check_if_the_item_exists_in_storage()
|
||||||
|
{
|
||||||
|
$this->cache->shouldReceive('tags->has')->with('foo')->andReturn(true);
|
||||||
|
|
||||||
|
$this->assertTrue($this->storage->has('foo'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function it_should_remove_the_item_from_storage()
|
||||||
|
{
|
||||||
|
$this->cache->shouldReceive('tags->forget')->with('foo')->andReturn(true);
|
||||||
|
|
||||||
|
$this->assertTrue($this->storage->destroy('foo'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function it_should_remove_all_items_from_storage()
|
||||||
|
{
|
||||||
|
$this->cache->shouldReceive('tags->flush')->withNoArgs();
|
||||||
|
|
||||||
|
$this->storage->flush();
|
||||||
|
}
|
||||||
|
}
|
41
tests/Providers/User/EloquentUserAdapterTest.php
Normal file
41
tests/Providers/User/EloquentUserAdapterTest.php
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of jwt-auth.
|
||||||
|
*
|
||||||
|
* (c) Sean Tymon <tymon148@gmail.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Tymon\JWTAuth\Test\Providers\User;
|
||||||
|
|
||||||
|
use Mockery;
|
||||||
|
use Tymon\JWTAuth\Providers\User\EloquentUserAdapter;
|
||||||
|
|
||||||
|
class EloquentUserAdapterTest extends \PHPUnit_Framework_TestCase
|
||||||
|
{
|
||||||
|
public function setUp()
|
||||||
|
{
|
||||||
|
$this->builder = Mockery::mock('Illuminate\Database\Query\Builder');
|
||||||
|
$this->model = Mockery::mock('Illuminate\Database\Eloquent\Model');
|
||||||
|
$this->user = new EloquentUserAdapter($this->model);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function tearDown()
|
||||||
|
{
|
||||||
|
Mockery::close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function it_should_return_the_user_if_found()
|
||||||
|
{
|
||||||
|
$this->builder->shouldReceive('first')->once()->withNoArgs()->andReturn((object) ['id' => 1]);
|
||||||
|
$this->model->shouldReceive('where')->once()->with('foo', 'bar')->andReturn($this->builder);
|
||||||
|
|
||||||
|
$user = $this->user->getBy('foo', 'bar');
|
||||||
|
|
||||||
|
$this->assertEquals(1, $user->id);
|
||||||
|
}
|
||||||
|
}
|
18
tests/Stubs/JWTProviderStub.php
Normal file
18
tests/Stubs/JWTProviderStub.php
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of jwt-auth.
|
||||||
|
*
|
||||||
|
* (c) Sean Tymon <tymon148@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 Tymon\JWTAuth\Providers\JWT\JWTProvider;
|
||||||
|
|
||||||
|
class JWTProviderStub extends JWTProvider
|
||||||
|
{
|
||||||
|
}
|
34
tests/TokenTest.php
Normal file
34
tests/TokenTest.php
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of jwt-auth.
|
||||||
|
*
|
||||||
|
* (c) Sean Tymon <tymon148@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 Tymon\JWTAuth\Token;
|
||||||
|
|
||||||
|
class TokenTest extends \PHPUnit_Framework_TestCase
|
||||||
|
{
|
||||||
|
public function setUp()
|
||||||
|
{
|
||||||
|
$this->token = new Token('foo.bar.baz');
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function it_should_return_the_token_when_casting_to_a_string()
|
||||||
|
{
|
||||||
|
$this->assertEquals((string) $this->token, $this->token);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function it_should_return_the_token_when_calling_get_method()
|
||||||
|
{
|
||||||
|
$this->assertInternalType('string', $this->token->get());
|
||||||
|
}
|
||||||
|
}
|
142
tests/Validators/PayloadValidatorTest.php
Normal file
142
tests/Validators/PayloadValidatorTest.php
Normal file
|
@ -0,0 +1,142 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of jwt-auth.
|
||||||
|
*
|
||||||
|
* (c) Sean Tymon <tymon148@gmail.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Tymon\JWTAuth\Test;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
|
use Tymon\JWTAuth\Validators\PayloadValidator;
|
||||||
|
|
||||||
|
class PayloadValidatorTest extends \PHPUnit_Framework_TestCase
|
||||||
|
{
|
||||||
|
public function setUp()
|
||||||
|
{
|
||||||
|
Carbon::setTestNow(Carbon::createFromTimeStampUTC(123));
|
||||||
|
$this->validator = new PayloadValidator();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function it_should_return_true_when_providing_a_valid_payload()
|
||||||
|
{
|
||||||
|
$payload = [
|
||||||
|
'iss' => 'http://example.com',
|
||||||
|
'iat' => 100,
|
||||||
|
'nbf' => 100,
|
||||||
|
'exp' => 100 + 3600,
|
||||||
|
'sub' => 1,
|
||||||
|
'jti' => 'foo',
|
||||||
|
];
|
||||||
|
|
||||||
|
$this->assertTrue($this->validator->isValid($payload));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function it_should_throw_an_exception_when_providing_an_expired_payload()
|
||||||
|
{
|
||||||
|
$this->setExpectedException('Tymon\JWTAuth\Exceptions\TokenExpiredException');
|
||||||
|
|
||||||
|
$payload = [
|
||||||
|
'iss' => 'http://example.com',
|
||||||
|
'iat' => 20,
|
||||||
|
'nbf' => 20,
|
||||||
|
'exp' => 120,
|
||||||
|
'sub' => 1,
|
||||||
|
'jti' => 'foo',
|
||||||
|
];
|
||||||
|
|
||||||
|
$this->validator->check($payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function it_should_throw_an_exception_when_providing_an_invalid_nbf_claim()
|
||||||
|
{
|
||||||
|
$this->setExpectedException('Tymon\JWTAuth\Exceptions\TokenInvalidException');
|
||||||
|
|
||||||
|
$payload = [
|
||||||
|
'iss' => 'http://example.com',
|
||||||
|
'iat' => 100,
|
||||||
|
'nbf' => 150,
|
||||||
|
'exp' => 150 + 3600,
|
||||||
|
'sub' => 1,
|
||||||
|
'jti' => 'foo',
|
||||||
|
];
|
||||||
|
|
||||||
|
$this->validator->check($payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function it_should_throw_an_exception_when_providing_an_invalid_iat_claim()
|
||||||
|
{
|
||||||
|
$this->setExpectedException('Tymon\JWTAuth\Exceptions\TokenInvalidException');
|
||||||
|
|
||||||
|
$payload = [
|
||||||
|
'iss' => 'http://example.com',
|
||||||
|
'iat' => 150,
|
||||||
|
'nbf' => 100,
|
||||||
|
'exp' => 150 + 3600,
|
||||||
|
'sub' => 1,
|
||||||
|
'jti' => 'foo',
|
||||||
|
];
|
||||||
|
|
||||||
|
$this->validator->check($payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function it_should_throw_an_exception_when_providing_an_invalid_payload()
|
||||||
|
{
|
||||||
|
$this->setExpectedException('Tymon\JWTAuth\Exceptions\TokenInvalidException');
|
||||||
|
|
||||||
|
$payload = [
|
||||||
|
'iss' => 'http://example.com',
|
||||||
|
'sub' => 1,
|
||||||
|
];
|
||||||
|
|
||||||
|
$this->validator->check($payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function it_should_throw_an_exception_when_providing_an_invalid_expiry()
|
||||||
|
{
|
||||||
|
$this->setExpectedException('Tymon\JWTAuth\Exceptions\TokenInvalidException');
|
||||||
|
|
||||||
|
$payload = [
|
||||||
|
'iss' => 'http://example.com',
|
||||||
|
'iat' => 100,
|
||||||
|
'exp' => 'foo',
|
||||||
|
'sub' => 1,
|
||||||
|
'jti' => 'foo',
|
||||||
|
];
|
||||||
|
|
||||||
|
$this->validator->check($payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test **/
|
||||||
|
public function it_should_throw_an_exception_when_required_claims_are_missing()
|
||||||
|
{
|
||||||
|
$this->setExpectedException('Tymon\JWTAuth\Exceptions\TokenInvalidException');
|
||||||
|
|
||||||
|
$payload = [
|
||||||
|
'iss' => 'http://example.com',
|
||||||
|
'foo' => 'bar',
|
||||||
|
// these are inserted to check for regression to a previous bug
|
||||||
|
// where the check would only compare keys of autoindexed name arrays
|
||||||
|
// (There are enough to account for all of the required claims' indices)
|
||||||
|
'autoindexed',
|
||||||
|
'autoindexed',
|
||||||
|
'autoindexed',
|
||||||
|
'autoindexed',
|
||||||
|
'autoindexed',
|
||||||
|
'autoindexed',
|
||||||
|
'autoindexed',
|
||||||
|
];
|
||||||
|
|
||||||
|
$this->validator->check($payload);
|
||||||
|
}
|
||||||
|
}
|
42
tests/Validators/TokenValidatorTest.php
Normal file
42
tests/Validators/TokenValidatorTest.php
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of jwt-auth.
|
||||||
|
*
|
||||||
|
* (c) Sean Tymon <tymon148@gmail.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Tymon\JWTAuth\Test;
|
||||||
|
|
||||||
|
use Tymon\JWTAuth\Validators\TokenValidator;
|
||||||
|
|
||||||
|
class TokenValidatorTest extends \PHPUnit_Framework_TestCase
|
||||||
|
{
|
||||||
|
public function 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'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function it_should_return_false_when_providing_a_malformed_token()
|
||||||
|
{
|
||||||
|
$this->assertFalse($this->validator->isValid('one.two.three.four.five'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function it_should_throw_an_axception_when_providing_a_malformed_token()
|
||||||
|
{
|
||||||
|
$this->setExpectedException('Tymon\JWTAuth\Exceptions\TokenInvalidException');
|
||||||
|
|
||||||
|
$this->validator->check('one.two.three.four.five');
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user