Skip to content

Commit

Permalink
Add JWKs into ProviderMetadata
Browse files Browse the repository at this point in the history
  • Loading branch information
spajxo committed Nov 4, 2021
1 parent 689b786 commit ec4bb9d
Show file tree
Hide file tree
Showing 12 changed files with 149 additions and 25 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,11 @@ All notable changes will be documented in this file.

Updates should follow the [Keep a CHANGELOG](http://keepachangelog.com/) principles.

## [0.2.0] 2021-11-04
### Added
- Add JWKs into ProviderMetadata
### Changed
- Renamed and moved MetadataTrait to ParametersTrait

## [0.1.0] 2021-11-01
First release 🚀
7 changes: 4 additions & 3 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,19 +33,20 @@
},
"require-dev": {
"dealerdirect/phpcodesniffer-composer-installer": "^0.7.0",
"nyholm/nsa":"^1.3.0",
"nyholm/nsa": "^1.3.0",
"nyholm/psr7": "^1.4.1",
"php-http/curl-client": "^2.2.0",
"php-http/discovery": "^1.14.1",
"php-http/mock-client": "^1.5.0",
"phpstan/extension-installer": "^1.1.0",
"phpstan/phpstan": "^0.12.84",
"phpstan/phpstan-phpunit": "^0.12.18",
"phpstan/phpstan-strict-rules": "^0.12.9",
"phpunit/phpunit": "^9.5.10",
"php-http/discovery": "^1.14.1",
"slevomat/coding-standard": "^7.0.16",
"squizlabs/php_codesniffer": "^3.5.8",
"symfony/cache": "^5.3.8"
"symfony/cache": "^5.3.8",
"symfony/var-dumper": "^5.3"
},
"autoload": {
"psr-4": {
Expand Down
19 changes: 14 additions & 5 deletions src/HttpDiscoverer.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,17 @@ public function __construct(private ClientInterface $client, private RequestFact
* @throws DiscoveryException
*/
public function discover(string $uri): ProviderMetadata
{
$configuration = $this->sendRequest($uri);
$jwks = $this->sendRequest($configuration['jwks_uri']);

return new ProviderMetadata($configuration, JWKs::fromArray($jwks));
}

/**
* @return array<string, mixed>
*/
private function sendRequest(string $uri): array
{
$request = $this->requestFactory->createRequest('GET', $uri);

Expand All @@ -32,15 +43,13 @@ public function discover(string $uri): ProviderMetadata
$statusCode = $response->getStatusCode();

if ($statusCode !== 200) {
throw new DiscoveryException('Bad response status code ' . $response->getStatusCode());
throw new DiscoveryException('Bad response status code ' . $response->getStatusCode() . ' on ' . $uri);
}

try {
$result = json_decode((string)$response->getBody(), true, 512, JSON_THROW_ON_ERROR);
return json_decode((string)$response->getBody(), true, 512, JSON_THROW_ON_ERROR);
} catch (JsonException $e) {
throw new DiscoveryException('Unable to parse response', $e->getCode(), $e);
throw new DiscoveryException('Unable to parse response from ' . $uri, $e->getCode(), $e);
}

return new ProviderMetadata($result);
}
}
20 changes: 20 additions & 0 deletions src/JWK.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

declare(strict_types=1);

namespace DigitalCz\OpenIDConnect\Discovery;

use DigitalCz\OpenIDConnect\Discovery\Traits\ParametersTrait;
use InvalidArgumentException;

final class JWK
{
use ParametersTrait;

/** @param array<string, mixed> $values */
public function __construct(array $values)
{
$values['kty'] ?? throw new InvalidArgumentException('Key "kty" is mandatory');
$this->parameters = $values;
}
}
46 changes: 46 additions & 0 deletions src/JWKs.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<?php

declare(strict_types=1);

namespace DigitalCz\OpenIDConnect\Discovery;

use InvalidArgumentException;
use JsonSerializable;

class JWKs implements JsonSerializable
{
/** @var JWK[] */
private array $keys;

/** @param JWK[] $keys */
public function __construct(array $keys)
{
$this->keys = $keys;
}

/** @param array<string, mixed> $values */
public static function fromArray(array $values): self
{
$keys = $values['keys'] ?? throw new InvalidArgumentException('Key "keys" is missing');

return new self(array_map(static fn (array $keyData) => new JWK($keyData), $keys));
}

/** @return JWK[] */
public function keys(): array
{
return $this->keys;
}

/** @return array{keys: array<int, array<string, string>>} */
public function toArray(): array
{
return ['keys' => array_map(static fn (JWK $key) => $key->all(), $this->keys())];
}

/** @return array{keys: array<int, array<string, string>>} */
public function jsonSerialize(): array
{
return $this->toArray();
}
}
15 changes: 12 additions & 3 deletions src/ProviderMetadata.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,20 @@

namespace DigitalCz\OpenIDConnect\Discovery;

use DigitalCz\OpenIDConnect\Discovery\Traits\ParametersTrait;
use JsonSerializable;

final class ProviderMetadata implements JsonSerializable
{
use MetadataTrait;
use ParametersTrait;

private JWKs $JWKs;

/** @param array<string, mixed> $metadata */
public function __construct(array $metadata)
public function __construct(array $metadata, JWKs $JWKs)
{
$this->metadata = $metadata;
$this->parameters = $metadata;
$this->JWKs = $JWKs;
}

public function issuer(): string
Expand Down Expand Up @@ -212,4 +216,9 @@ public function opTosUri(): string
{
return $this->get('op_tos_uri');
}

public function getJWKs(): JWKs
{
return $this->JWKs;
}
}
12 changes: 6 additions & 6 deletions src/MetadataTrait.php → src/Traits/ParametersTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,23 @@

declare(strict_types=1);

namespace DigitalCz\OpenIDConnect\Discovery;
namespace DigitalCz\OpenIDConnect\Discovery\Traits;

use DigitalCz\OpenIDConnect\Discovery\Exception\MetadataException;

trait MetadataTrait
trait ParametersTrait
{
/** @var array<string, mixed> */
private array $metadata = [];
private array $parameters = [];

public function has(string $key): bool
{
return isset($this->metadata[$key]);
return isset($this->parameters[$key]);
}

public function get(string $key, mixed $default = null): mixed
{
return $this->metadata[$key] ?? $default;
return $this->parameters[$key] ?? $default;
}

public function ensure(string $key): mixed
Expand All @@ -29,7 +29,7 @@ public function ensure(string $key): mixed
/** @return array<string, mixed> */
public function all(): array
{
return $this->metadata;
return $this->parameters;
}

/** @return array<string, mixed> */
Expand Down
2 changes: 1 addition & 1 deletion tests/CachedDiscovererTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public function testDiscover(): void
$innerDiscoverer->expects(self::once())
->method('discover')
->with(self::equalTo('https://example.com/.well-known/openid-configuration'))
->willReturn(new ProviderMetadata(['issuer' => 'https://example.com']));
->willReturn(new ProviderMetadata(['issuer' => 'https://example.com'], new JWKs([])));

$cache = new Psr16Cache(new ArrayAdapter());
$cachedDiscoverer = new CachedDiscoverer($innerDiscoverer, $cache);
Expand Down
File renamed without changes.
20 changes: 20 additions & 0 deletions tests/Dummy/jwks.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"keys": [
{
"kty": "RSA",
"e": "AQAB",
"use": "sig",
"n": "y930dtGTeMG52IPsKmMuEpPHLaxuYQlduZd6BqFVjc2-UFZR8fNqtnYzAjbXWJD_Tqxgdlj_MW4vogvX4sHwVpZONvdyeGoIyDQtis6iuGQhQamV85F_JbrEUnEw3QCO87Liz5UXG6BK2HRyPhDfMex1_tO0ROmySLFdCTS17D0wah71Ibpi0gI8LUi6kzVRjYDIC1oE-iK3Y9s88Bi4ZGYJxXAbnNwbwVkGOKCXja9k0jjBGRxZD-4KDuf493lFOOEGSLDA2Qp9rDqrURP12XYgvf_zJx_kSDipnr0gL6Vz2n3H4-XN4tA45zuzRkHoE7-XexPq-tv7kQ8pSjY2uQ",
"kid": "bbd2ac7c4c5eb8adc8eeffbc8f5a2dd6cf7545e4",
"alg": "RS256"
},
{
"kty": "RSA",
"e": "AQAB",
"use": "sig",
"alg": "RS256",
"kid": "85828c59284a69b54b27483e487c3bd46cd2a2b3",
"n": "zMHxWuxztMKXdBhv3rImlUvW_yp6nO03cVXPyA0Vyq0-M7LfOJJIF-OdNoRGdsFPHVKCFoo6qGhR8rBCmMxA4fM-Ubk5qKuUqCN9eP3yZJq8Cw9tUrt_qh7uW-qfMr0upcyeSHhC_zW1lTGs5sowDorKN_jQ1Sfh9hfBxfc8T7dQAAgEqqMcE3u-2J701jyhJz0pvurCfziiB3buY6SGREhBQwNwpnQjt_lE2U4km8FS0woPzt0ccE3zsGL2qM-LWZbOm9aXquSnqNJLt3tGVvShnev-GiJ1XfQ3EWm0f4w0TX9fTOkxstl0vo_vW_FjGQ0D1pXSjqb7n-hAdXwc9w"
}
]
}
21 changes: 17 additions & 4 deletions tests/HttpDiscovererTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,31 @@ class HttpDiscovererTest extends TestCase
{
public function testDiscover(): void
{
$configuration = file_get_contents(TESTS_DIR . '/configuration.json');
$configuration = file_get_contents(TESTS_DIR . '/Dummy/configuration.json');
$jwks = file_get_contents(TESTS_DIR . '/Dummy/jwks.json');

self::assertIsString($configuration);
self::assertIsString($jwks);

$client = new Client();
$client->addResponse(new Response(200, [], $configuration));
$client->addResponse(new Response(200, [], $jwks));
$discoverer = new HttpDiscoverer($client, new Psr17Factory());
$metadata = $discoverer->discover('https://example.com/.well-known/openid-configuration');

$lastRequest = $client->getLastRequest();
self::assertSame('GET', $lastRequest->getMethod());
self::assertSame('https://example.com/.well-known/openid-configuration', (string)$lastRequest->getUri());
self::assertCount(2, $client->getRequests());

$configurationRequest = $client->getRequests()[0];
self::assertSame('GET', $configurationRequest->getMethod());
self::assertSame(
'https://example.com/.well-known/openid-configuration',
(string)$configurationRequest->getUri()
);

$jwksRequest = $client->getRequests()[1];
self::assertSame('GET', $jwksRequest->getMethod());
self::assertSame('https://www.googleapis.com/oauth2/v3/certs', (string)$jwksRequest->getUri());

self::assertSame('https://accounts.google.com', $metadata->issuer());
self::assertSame('https://accounts.google.com/o/oauth2/v2/auth', $metadata->authorizationEndpoint());
}
Expand Down
6 changes: 3 additions & 3 deletions tests/ProviderMetadataTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@

/**
* @covers \DigitalCz\OpenIDConnect\Discovery\ProviderMetadata
* @covers \DigitalCz\OpenIDConnect\Discovery\MetadataTrait
* @covers \DigitalCz\OpenIDConnect\Discovery\Traits\ParametersTrait
*/
class ProviderMetadataTest extends TestCase
{
public function testMethods(): void
{
$config = (string)file_get_contents(TESTS_DIR . '/configuration.json');
$config = (string)file_get_contents(TESTS_DIR . '/Dummy/configuration.json');
$values = json_decode($config, true, 512, JSON_THROW_ON_ERROR);
$metadata = new ProviderMetadata($values);
$metadata = new ProviderMetadata($values, new JWKs([]));

self::assertSame('https://accounts.google.com', $metadata->issuer());
self::assertSame('https://accounts.google.com/o/oauth2/v2/auth', $metadata->authorizationEndpoint());
Expand Down

0 comments on commit ec4bb9d

Please sign in to comment.