Skip to content

Commit

Permalink
Merge pull request #9 from cgauge/web-token-library
Browse files Browse the repository at this point in the history
Upgrade Web token library
  • Loading branch information
abdala committed Jun 12, 2024
2 parents 3089447 + 40f70fc commit b34b00d
Show file tree
Hide file tree
Showing 12 changed files with 138 additions and 88 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@
composer.phar
composer.lock
/vendor/
.phpunit.result.cache
.phpunit.result.cache
.phpunit.cache/
19 changes: 10 additions & 9 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,23 +13,19 @@
"minimum-stability": "dev",
"prefer-stable": true,
"require": {
"php": ">=7.3",
"php": ">=8.1",
"ext-gmp": "*",
"ext-json": "*",
"web-token/jwt-checker": "^2.0",
"web-token/jwt-core": "^2.0",
"web-token/jwt-signature": "^2.0",
"web-token/jwt-signature-algorithm-rsa": "^2.0",
"web-token/jwt-library": "^3.0",
"illuminate/contracts": ">=7.1"
},
"require-dev": {
"illuminate/cache": ">=7.1",
"illuminate/config": ">=7.1",
"illuminate/container": ">=7.1",
"phpunit/phpunit": "^9.0",
"doctrine/coding-standard": "^6.0 || ^8.0",
"phpstan/phpstan": "^1.0",
"web-token/jwt-easy": "~2.0"
"phpunit/phpunit": "^10.0",
"doctrine/coding-standard": "^12.0",
"phpstan/phpstan": "^1.0"
},
"autoload": {
"psr-4": {
Expand All @@ -47,5 +43,10 @@
"CustomerGauge\\Cognito\\LaravelCognitoServiceProvider"
]
}
},
"config": {
"allow-plugins": {
"dealerdirect/phpcodesniffer-composer-installer": true
}
}
}
12 changes: 6 additions & 6 deletions phpunit.xml.dist
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" backupGlobals="false" backupStaticAttributes="false" bootstrap="vendor/autoload.php" colors="true" convertErrorsToExceptions="true" convertNoticesToExceptions="true" convertWarningsToExceptions="true" processIsolation="false" stopOnFailure="false" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd">
<coverage processUncoveredFiles="true">
<include>
<directory suffix=".php">./src</directory>
</include>
</coverage>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" backupGlobals="false" bootstrap="vendor/autoload.php" colors="true" processIsolation="false" stopOnFailure="false" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/11.2/phpunit.xsd" cacheDirectory=".phpunit.cache" backupStaticProperties="false">
<testsuites>
<testsuite name="Unit Tests">
<directory suffix="Test.php">./tests</directory>
</testsuite>
</testsuites>
<source>
<include>
<directory suffix=".php">./src</directory>
</include>
</source>
</phpunit>
52 changes: 37 additions & 15 deletions src/CognitoUserProvider.php
Original file line number Diff line number Diff line change
@@ -1,32 +1,29 @@
<?php declare(strict_types=1);
<?php

declare(strict_types=1);

namespace CustomerGauge\Cognito;

use BadMethodCallException;
use CustomerGauge\Cognito\Contracts\UserFactory;
use Exception;
use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Contracts\Auth\UserProvider;
use Throwable;

final class CognitoUserProvider implements UserProvider
{
private $parser;

private $factory;

public function __construct(TokenParser $parser, UserFactory $factory)
public function __construct(private TokenParser $parser, private UserFactory $factory)
{
$this->parser = $parser;
$this->factory = $factory;
}

/** @inheritdoc */
public function retrieveByCredentials(array $credentials)
{
$token = $credentials['cognito_token'];

try {
$payload = $this->parser->parse($token);

} catch (Exception $e) {
} catch (Throwable) {
// If we cannot parse the token, that probably means it's an invalid Token. Since
// the Authenticate Middleware implements a Chain Of Responsibility Pattern,
// we have to return null so that other Guards can try to authenticate.
Expand All @@ -36,23 +33,48 @@ public function retrieveByCredentials(array $credentials)
return $this->factory->make($payload);
}

/** @phpstan ignore */
/**
* @inheritdoc
* @phpstan ignore
*/
public function validateCredentials(Authenticatable $user, array $credentials)
{
throw new BadMethodCallException('Not implemented');
}

/** @phpstan ignore */
/**
* @inheritdoc
* @phpstan ignore
*/
public function retrieveById($identifier)
{
throw new BadMethodCallException('Not implemented');
}

/** @phpstan ignore */
/**
* @inheritdoc
* @phpstan ignore
*/
public function retrieveByToken($identifier, $token)
{
throw new BadMethodCallException('Not implemented');
}

/** @phpstan ignore */
/**
* @inheritdoc
* @phpstan ignore
*/
public function updateRememberToken(Authenticatable $user, $token)
{
throw new BadMethodCallException('Not implemented');
}

/**
* @inheritdoc
* @phpstan ignore
*/
public function rehashPasswordIfRequired(Authenticatable $user, array $credentials, bool $force = false)
{
throw new BadMethodCallException('Not implemented');
}
}
9 changes: 6 additions & 3 deletions src/Contracts/UserFactory.php
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
<?php declare(strict_types=1);
<?php

declare(strict_types=1);

namespace CustomerGauge\Cognito\Contracts;

use Illuminate\Contracts\Auth\Authenticatable;

interface UserFactory
{
public function make(array $payload): ?Authenticatable;
}
/** @param mixed[] $payload */
public function make(array $payload): Authenticatable|null;
}
14 changes: 6 additions & 8 deletions src/Issuer.php
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
<?php declare(strict_types=1);
<?php

declare(strict_types=1);

namespace CustomerGauge\Cognito;

use function sprintf;

final class Issuer
{
private $userPoolId;

private $region;

public function __construct(string $userPoolId, string $region)
public function __construct(private string $userPoolId, private string $region)
{
$this->userPoolId = $userPoolId;
$this->region = $region;
}

public function toString(): string
Expand Down
25 changes: 15 additions & 10 deletions src/KeyResolver.php
Original file line number Diff line number Diff line change
@@ -1,27 +1,32 @@
<?php declare(strict_types=1);
<?php

declare(strict_types=1);

namespace CustomerGauge\Cognito;

use Illuminate\Contracts\Cache\Repository;
use InvalidArgumentException;

use function file_get_contents;

final class KeyResolver
{
private $cache;

private $issuer;

public function __construct(Issuer $issuer, Repository $cache)
public function __construct(private Issuer $issuer, private Repository $cache)
{
$this->issuer = $issuer;
$this->cache = $cache;
}

public function jwkset(): string
{
$url = $this->issuer->toString() . '/.well-known/jwks.json';

return $this->cache->remember('jwks', 7200, function() use ($url) {
return file_get_contents($url);
return $this->cache->remember('jwks', 7200, static function () use ($url) {
$content = file_get_contents($url);

if ($content === false) {
throw new InvalidArgumentException('Invalid JWKS file');
}

return $content;
});
}

Expand Down
8 changes: 5 additions & 3 deletions src/LaravelCognitoServiceProvider.php
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
<?php declare(strict_types=1);
<?php

declare(strict_types=1);

namespace CustomerGauge\Cognito;

Expand All @@ -8,7 +10,7 @@

final class LaravelCognitoServiceProvider extends ServiceProvider
{
public function register()
public function register(): void
{
$this->registerIssuer();

Expand All @@ -30,7 +32,7 @@ private function registerIssuer(): void

private function registerCognitoUserProvider(): void
{
Auth::provider(CognitoUserProvider::class, function (Container $app) {
Auth::provider(CognitoUserProvider::class, static function (Container $app) {
return $app->make(CognitoUserProvider::class);
});
}
Expand Down
63 changes: 39 additions & 24 deletions src/Testing/TokenGenerator.php
Original file line number Diff line number Diff line change
@@ -1,51 +1,66 @@
<?php declare(strict_types=1);
<?php

declare(strict_types=1);

namespace CustomerGauge\Cognito\Testing;

use InvalidArgumentException;
use Jose\Component\Core\AlgorithmManager;
use Jose\Component\Core\JWKSet;
use Jose\Easy\Build;
use Jose\Component\Core\Util\JsonConverter;
use Jose\Component\Signature\Algorithm\RS256;
use Jose\Component\Signature\JWSBuilder;
use Jose\Component\Signature\Serializer\CompactSerializer;

use function file_get_contents;
use function time;

final class TokenGenerator
{
private $jwk;
public string $jti = 'token-id';

public $jti = 'token-id';
public string $algorithm = 'RS256';

public $algorithm = 'RS256';
public string $issuer = 'https://cognito-idp.local.amazonaws.com/phpunit-pool-id';

public $issuer = 'https://cognito-idp.local.amazonaws.com/phpunit-pool-id';
public string $subject = 'testing';

public $subject = 'testing';

public function __construct(JWKSet $jwk)
public function __construct(private JWKSet $jwk)
{
$this->jwk = $jwk;
}

public static function fromFile(string $path): self
{
$key = file_get_contents($path);

if ($key === false) {
throw new InvalidArgumentException('Invalid file');
}

return new self(JWKSet::createFromJson($key));
}

/** @param mixed[] $attributes */
public function sign(array $attributes): string
{
$time = time();

$builder = Build::jws()
->exp($time + 3600)
->iat($time)
->nbf($time)
->jti($this->jti, true)
->alg($this->algorithm)
->iss($this->issuer)
->sub($this->subject);

foreach ($attributes as $key => $value) {
$builder->claim($key, $value, true);
}
$algorithmManager = new AlgorithmManager([new RS256()]);
$jwsBuilder = new JWSBuilder($algorithmManager);
$payload = JsonConverter::encode([
'iat' => $time,
'nbf' => $time,
'exp' => $time + 3600,
'iss' => $this->issuer,
'jti' => $this->jti,
'sub' => $this->subject,
] + $attributes);

$jws = $jwsBuilder->create()
->withPayload($payload)
->addSignature($this->jwk->get(0), ['alg' => $this->algorithm])
->build();

return $builder->sign($this->jwk->get(0));
return (new CompactSerializer())->serialize($jws);
}
}
}
Loading

0 comments on commit b34b00d

Please sign in to comment.