Skip to content

Commit

Permalink
Merge pull request tymondesigns#14 from eschricker/feature/php8-lcobu…
Browse files Browse the repository at this point in the history
…cci4-support

Migrated pull request tymondesigns#2117
  • Loading branch information
Messhias authored Sep 21, 2021
2 parents 9dc6001 + 2aad5e8 commit 7c588f9
Show file tree
Hide file tree
Showing 4 changed files with 215 additions and 77 deletions.
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
"illuminate/database": "^8.61",
"illuminate/http": "^5.2|^6|^7|^8",
"illuminate/support": "^5.2|^6|^7|^8",
"lcobucci/jwt": "<3.4",
"lcobucci/jwt": "^4.0",
"namshi/jose": "^7.0",
"nesbot/carbon": "^1.0|^2.0"
},
Expand Down
4 changes: 0 additions & 4 deletions src/Providers/AbstractServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@
namespace PHPOpenSourceSaver\JWTAuth\Providers;

use Illuminate\Support\ServiceProvider;
use Lcobucci\JWT\Builder as JWTBuilder;
use Lcobucci\JWT\Parser as JWTParser;
use Namshi\JOSE\JWS;
use PHPOpenSourceSaver\JWTAuth\Blacklist;
use PHPOpenSourceSaver\JWTAuth\Claims\Factory as ClaimFactory;
Expand Down Expand Up @@ -167,8 +165,6 @@ protected function registerLcobucciProvider()
{
$this->app->singleton('tymon.jwt.provider.jwt.lcobucci', function ($app) {
return new Lcobucci(
new JWTBuilder(),
new JWTParser(),
$this->config('secret'),
$this->config('algo'),
$this->config('keys')
Expand Down
145 changes: 112 additions & 33 deletions src/Providers/JWT/Lcobucci.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,23 @@
use Exception;
use Illuminate\Support\Collection;
use Lcobucci\JWT\Builder;
use Lcobucci\JWT\Parser;
use Lcobucci\JWT\Configuration;
use Lcobucci\JWT\Signer\Ecdsa;
use Lcobucci\JWT\Signer\Ecdsa\Sha256 as ES256;
use Lcobucci\JWT\Signer\Ecdsa\Sha384 as ES384;
use Lcobucci\JWT\Signer\Ecdsa\Sha512 as ES512;
use Lcobucci\JWT\Signer\Hmac\Sha256 as HS256;
use Lcobucci\JWT\Signer\Hmac\Sha384 as HS384;
use Lcobucci\JWT\Signer\Hmac\Sha512 as HS512;
use Lcobucci\JWT\Signer\Keychain;
use Lcobucci\JWT\Signer\Key\InMemory;
use Lcobucci\JWT\Signer;
use Lcobucci\JWT\Signer\Key;
use Lcobucci\JWT\Signer\Rsa;
use Lcobucci\JWT\Signer\Rsa\Sha256 as RS256;
use Lcobucci\JWT\Signer\Rsa\Sha384 as RS384;
use Lcobucci\JWT\Signer\Rsa\Sha512 as RS512;
use Lcobucci\JWT\Token\RegisteredClaims;
use Lcobucci\JWT\Validation\Constraint\SignedWith;
use ReflectionClass;
use PHPOpenSourceSaver\JWTAuth\Contracts\Providers\JWT;
use PHPOpenSourceSaver\JWTAuth\Exceptions\JWTException;
Expand All @@ -35,42 +39,68 @@
class Lcobucci extends Provider implements JWT
{
/**
* The Builder instance.
* The builder instance.
*
* @var \Lcobucci\JWT\Builder
* @var Builder
*/
protected $builder;

/**
* The Parser instance.
* The configuration instance.
*
* @var \Lcobucci\JWT\Parser
* @var Configuration
*/
protected $parser;
protected $config;

/**
* The Signer instance.
*
* @var Signer
*/
protected $signer;

/**
* Create the Lcobucci provider.
*
* @param \Lcobucci\JWT\Builder $builder
* @param \Lcobucci\JWT\Parser $parser
* @param string $secret
* @param string $algo
* @param array $keys
* @param Configuration $config Optional, to pass an existing configuration to be used.
*
* @return void
*/
public function __construct(
Builder $builder,
Parser $parser,
$secret,
$algo,
array $keys
array $keys,
$config = null
) {
parent::__construct($secret, $algo, $keys);

$this->builder = $builder;
$this->parser = $parser;
$this->signer = $this->getSigner();

if (!is_null($config)) {
$this->config = $config;
} elseif ($this->isAsymmetric()) {
$this->config = Configuration::forAsymmetricSigner($this->signer, $this->getSigningKey(), $this->getVerificationKey());
} else {
$this->config = Configuration::forSymmetricSigner($this->signer, InMemory::plainText($this->getSecret()));
}
if (!count($this->config->validationConstraints())) {
$this->config->setValidationConstraints(
new SignedWith($this->signer, $this->getVerificationKey()),
);
}
}

/**
* Gets the {@see $config} attribute.
*
* @return Configuration
*/
public function getConfig()
{
return $this->config;
}

/**
Expand Down Expand Up @@ -101,19 +131,18 @@ public function __construct(
*/
public function encode(array $payload)
{
// Remove the signature on the builder instance first.
$this->builder->unsign();
$this->builder = null;
$this->builder = $this->config->builder();

try {
foreach ($payload as $key => $value) {
$this->builder->set($key, $value);
$this->addClaim($key, $value);
}
$this->builder->sign($this->signer, $this->getSigningKey());

return $this->builder->getToken($this->config->signer(), $this->config->signingKey())->toString();
} catch (Exception $e) {
throw new JWTException('Could not create token: '.$e->getMessage(), $e->getCode(), $e);
throw new JWTException('Could not create token: ' . $e->getMessage(), $e->getCode(), $e);
}

return (string) $this->builder->getToken();
}

/**
Expand All @@ -128,20 +157,66 @@ public function encode(array $payload)
public function decode($token)
{
try {
$jwt = $this->parser->parse($token);
$jwt = $this->config->parser()->parse($token);
} catch (Exception $e) {
throw new TokenInvalidException('Could not decode token: '.$e->getMessage(), $e->getCode(), $e);
throw new TokenInvalidException('Could not decode token: ' . $e->getMessage(), $e->getCode(), $e);
}

if (! $jwt->verify($this->signer, $this->getVerificationKey())) {
if (!$this->config->validator()->validate($jwt, ...$this->config->validationConstraints())) {
throw new TokenInvalidException('Token Signature could not be verified.');
}

return (new Collection($jwt->getClaims()))->map(function ($claim) {
return is_object($claim) ? $claim->getValue() : $claim;
return (new Collection($jwt->claims()->all()))->map(function ($claim) {
if (is_a($claim, \DateTimeImmutable::class)) {
return $claim->getTimestamp();
}
if (is_object($claim) && method_exists($claim, 'getValue')) {
return $claim->getValue();
}

return $claim;
})->toArray();
}

/**
* Adds a claim to the {@see $config}.
*
* @param string $key
* @param mixed $value
*/
protected function addClaim($key, $value)
{
if (!isset($this->builder)) {
$this->builder = $this->config->builder();
}

switch ($key) {
case RegisteredClaims::ID:
$this->builder->identifiedBy($value);
break;
case RegisteredClaims::EXPIRATION_TIME:
$this->builder->expiresAt(\DateTimeImmutable::createFromFormat('U', $value));
break;
case RegisteredClaims::NOT_BEFORE:
$this->builder->canOnlyBeUsedAfter(\DateTimeImmutable::createFromFormat('U', $value));
break;
case RegisteredClaims::ISSUED_AT:
$this->builder->issuedAt(\DateTimeImmutable::createFromFormat('U', $value));
break;
case RegisteredClaims::ISSUER:
$this->builder->issuedBy($value);
break;
case RegisteredClaims::AUDIENCE:
$this->builder->permittedFor($value);
break;
case RegisteredClaims::SUBJECT:
$this->builder->relatedTo($value);
break;
default:
$this->builder->withClaim($key, $value);
}
}

/**
* Get the signer instance.
*
Expand All @@ -151,7 +226,7 @@ public function decode($token)
*/
protected function getSigner()
{
if (! array_key_exists($this->algo, $this->signers)) {
if (!array_key_exists($this->algo, $this->signers)) {
throw new JWTException('The given algorithm could not be found');
}

Expand All @@ -169,22 +244,26 @@ protected function isAsymmetric()
}

/**
* {@inheritdoc}
* Get the key used to sign the tokens.
*
* @return Key|string
*/
protected function getSigningKey()
{
return $this->isAsymmetric() ?
(new Keychain())->getPrivateKey($this->getPrivateKey(), $this->getPassphrase()) :
$this->getSecret();
InMemory::plainText($this->getPrivateKey(), $this->getPassphrase() ?? '') :
InMemory::plainText($this->getSecret());
}

/**
* {@inheritdoc}
* Get the key used to verify the tokens.
*
* @return Key|string
*/
protected function getVerificationKey()
{
return $this->isAsymmetric() ?
(new Keychain())->getPublicKey($this->getPublicKey()) :
$this->getSecret();
InMemory::plainText($this->getPublicKey()) :
InMemory::plainText($this->getSecret());
}
}
Loading

0 comments on commit 7c588f9

Please sign in to comment.