Skip to content

Commit 56d2f13

Browse files
committed
WIP federation client
1 parent f4026d8 commit 56d2f13

17 files changed

+760
-48
lines changed

build/.phpunit.cache/test-results

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{"version":2,"defects":{"Cicnavi\\Tests\\Oidc\\DataStore\\PhpSessionDataStoreTest::testPut":5,"Cicnavi\\Tests\\Oidc\\DataStore\\PhpSessionDataStoreTest::testDelete":5,"Cicnavi\\Tests\\Oidc\\DataStore\\PhpSessionDataStoreTest::testExists":5,"Cicnavi\\Tests\\Oidc\\DataStore\\PhpSessionDataStoreTest::testConstruct":5,"Cicnavi\\Tests\\Oidc\\DataStore\\PhpSessionDataStoreTest::testGet":5,"Cicnavi\\Tests\\Oidc\\Helpers\\HttpHelperTest::testIsRequestHttpSecure":5,"Cicnavi\\Tests\\Oidc\\Helpers\\HttpHelperTest::testNormalizeSessionCookieParams":5,"Cicnavi\\Tests\\Oidc\\Helpers\\StringHelperTest::testRandom":5,"Cicnavi\\Tests\\Oidc\\Helpers\\StringHelperTest::testRandomThrows":5,"Cicnavi\\Tests\\Oidc\\Helpers\\StringHelperTest::testRandomThrowsForInvalidCallback":5,"Cicnavi\\Tests\\Oidc\\Http\\RequestFactoryTest::testCreateRequest":5,"Cicnavi\\Tests\\Oidc\\DataStore\\DataHandlers\\PkceTest::testRemoveCodeVerifierParameter":5,"Cicnavi\\Tests\\Oidc\\DataStore\\DataHandlers\\StateNonceTest::testVerifyInvalidValueThrows":5,"Cicnavi\\Tests\\Oidc\\DataStore\\DataHandlers\\StateNonceTest::testVerify":5,"Cicnavi\\Tests\\Oidc\\DataStore\\DataHandlers\\PkceTest::testNewCodeVerifier":5},"times":{"Cicnavi\\Tests\\Oidc\\Cache\\FileCacheTest::testConstruct":0.006,"Cicnavi\\Tests\\Oidc\\Cache\\FileCacheTest::testConstructWithCustomCacheNameAndPath":0.001,"Cicnavi\\Tests\\Oidc\\Cache\\FileCacheTest::testSetHasGet":0.004,"Cicnavi\\Tests\\Oidc\\DataStore\\DataHandlers\\PkceTest::testValidatePkceCodeChallengeMethod":0.001,"Cicnavi\\Tests\\Oidc\\DataStore\\DataHandlers\\PkceTest::testGenerateCodeChallengeFromCodeVerifierValidation":0.001,"Cicnavi\\Tests\\Oidc\\DataStore\\DataHandlers\\PkceTest::testGenerateCodeChallengeFromCodeVerifier":0.002,"Cicnavi\\Tests\\Oidc\\DataStore\\DataHandlers\\PkceTest::testRemoveCodeVerifierParameter":0.003,"Cicnavi\\Tests\\Oidc\\DataStore\\DataHandlers\\PkceTest::testGetExistingCodeVerifier":0.001,"Cicnavi\\Tests\\Oidc\\DataStore\\DataHandlers\\PkceTest::testNewCodeVerifier":0.006,"Cicnavi\\Tests\\Oidc\\DataStore\\DataHandlers\\PkceTest::testConstructWithoutArguments":0,"Cicnavi\\Tests\\Oidc\\DataStore\\DataHandlers\\PkceTest::testConstructWithArguments":0.001,"Cicnavi\\Tests\\Oidc\\DataStore\\DataHandlers\\PkceTest::testSetStore":0,"Cicnavi\\Tests\\Oidc\\DataStore\\DataHandlers\\StateNonceTest::testVerifyInvalidKeyThrows":0.001,"Cicnavi\\Tests\\Oidc\\DataStore\\DataHandlers\\StateNonceTest::testVerifyInvalidValueThrows":0.001,"Cicnavi\\Tests\\Oidc\\DataStore\\DataHandlers\\StateNonceTest::testVerifyNonExistantKeyThrows":0.001,"Cicnavi\\Tests\\Oidc\\DataStore\\DataHandlers\\StateNonceTest::testVerify":0,"Cicnavi\\Tests\\Oidc\\DataStore\\DataHandlers\\StateNonceTest::testGetInvalidKeyThrows":0.001,"Cicnavi\\Tests\\Oidc\\DataStore\\DataHandlers\\StateNonceTest::testGetExistingValue":0.001,"Cicnavi\\Tests\\Oidc\\DataStore\\DataHandlers\\StateNonceTest::testGetNewValue":0,"Cicnavi\\Tests\\Oidc\\DataStore\\PhpSessionDataStoreTest::testPut":0.001,"Cicnavi\\Tests\\Oidc\\DataStore\\PhpSessionDataStoreTest::testDelete":0.001,"Cicnavi\\Tests\\Oidc\\DataStore\\PhpSessionDataStoreTest::testExists":0.001,"Cicnavi\\Tests\\Oidc\\DataStore\\PhpSessionDataStoreTest::testConstruct":0,"Cicnavi\\Tests\\Oidc\\DataStore\\PhpSessionDataStoreTest::testGet":0.001,"Cicnavi\\Tests\\Oidc\\Helpers\\HttpHelperTest::testIsRequestHttpSecure":0.001,"Cicnavi\\Tests\\Oidc\\Helpers\\HttpHelperTest::testNormalizeSessionCookieParams":0.001,"Cicnavi\\Tests\\Oidc\\Helpers\\StringHelperTest::testRandom":0,"Cicnavi\\Tests\\Oidc\\Helpers\\StringHelperTest::testRandomThrows":0.001,"Cicnavi\\Tests\\Oidc\\Helpers\\StringHelperTest::testRandomThrowsForInvalidCallback":0,"Cicnavi\\Tests\\Oidc\\Http\\RequestFactoryTest::testCreateRequest":0.004,"Cicnavi\\Tests\\Oidc\\OpMetadataTest::testConstruct":0.002,"Cicnavi\\Tests\\Oidc\\OpMetadataTest::testConstructThrowsOnRequestException":0.002,"Cicnavi\\Tests\\Oidc\\OpMetadataTest::testConstructThrowsOnWrongResposeCode":0.009,"Cicnavi\\Tests\\Oidc\\OpMetadataTest::testConstructThrowsOnCacheSetException":0.004,"Cicnavi\\Tests\\Oidc\\OpMetadataTest::testMetadataValidationNotArray":0.001,"Cicnavi\\Tests\\Oidc\\OpMetadataTest::testMetadataValidationMissingProperties":0.003,"Cicnavi\\Tests\\Oidc\\OpMetadataTest::testGetUsingInvalidKeyThrows":0.001,"Cicnavi\\Tests\\Oidc\\OpMetadataTest::testAllGetMethods":0}}
1+
{"version":2,"defects":{"Cicnavi\\Tests\\Oidc\\DataStore\\PhpSessionDataStoreTest::testPut":5,"Cicnavi\\Tests\\Oidc\\DataStore\\PhpSessionDataStoreTest::testDelete":5,"Cicnavi\\Tests\\Oidc\\DataStore\\PhpSessionDataStoreTest::testExists":5,"Cicnavi\\Tests\\Oidc\\DataStore\\PhpSessionDataStoreTest::testConstruct":5,"Cicnavi\\Tests\\Oidc\\DataStore\\PhpSessionDataStoreTest::testGet":5,"Cicnavi\\Tests\\Oidc\\Helpers\\HttpHelperTest::testIsRequestHttpSecure":5,"Cicnavi\\Tests\\Oidc\\Helpers\\HttpHelperTest::testNormalizeSessionCookieParams":5,"Cicnavi\\Tests\\Oidc\\Helpers\\StringHelperTest::testRandom":5,"Cicnavi\\Tests\\Oidc\\Helpers\\StringHelperTest::testRandomThrows":5,"Cicnavi\\Tests\\Oidc\\Helpers\\StringHelperTest::testRandomThrowsForInvalidCallback":5,"Cicnavi\\Tests\\Oidc\\Http\\RequestFactoryTest::testCreateRequest":5,"Cicnavi\\Tests\\Oidc\\DataStore\\DataHandlers\\PkceTest::testRemoveCodeVerifierParameter":5,"Cicnavi\\Tests\\Oidc\\DataStore\\DataHandlers\\StateNonceTest::testVerifyInvalidValueThrows":5,"Cicnavi\\Tests\\Oidc\\DataStore\\DataHandlers\\StateNonceTest::testVerify":5,"Cicnavi\\Tests\\Oidc\\DataStore\\DataHandlers\\PkceTest::testNewCodeVerifier":5},"times":{"Cicnavi\\Tests\\Oidc\\Cache\\FileCacheTest::testConstruct":0.003,"Cicnavi\\Tests\\Oidc\\Cache\\FileCacheTest::testConstructWithCustomCacheNameAndPath":0.003,"Cicnavi\\Tests\\Oidc\\Cache\\FileCacheTest::testSetHasGet":0.004,"Cicnavi\\Tests\\Oidc\\DataStore\\DataHandlers\\PkceTest::testValidatePkceCodeChallengeMethod":0.002,"Cicnavi\\Tests\\Oidc\\DataStore\\DataHandlers\\PkceTest::testGenerateCodeChallengeFromCodeVerifierValidation":0.001,"Cicnavi\\Tests\\Oidc\\DataStore\\DataHandlers\\PkceTest::testGenerateCodeChallengeFromCodeVerifier":0,"Cicnavi\\Tests\\Oidc\\DataStore\\DataHandlers\\PkceTest::testRemoveCodeVerifierParameter":0.004,"Cicnavi\\Tests\\Oidc\\DataStore\\DataHandlers\\PkceTest::testGetExistingCodeVerifier":0.001,"Cicnavi\\Tests\\Oidc\\DataStore\\DataHandlers\\PkceTest::testNewCodeVerifier":0.01,"Cicnavi\\Tests\\Oidc\\DataStore\\DataHandlers\\PkceTest::testConstructWithoutArguments":0.002,"Cicnavi\\Tests\\Oidc\\DataStore\\DataHandlers\\PkceTest::testConstructWithArguments":0,"Cicnavi\\Tests\\Oidc\\DataStore\\DataHandlers\\PkceTest::testSetStore":0.001,"Cicnavi\\Tests\\Oidc\\DataStore\\DataHandlers\\StateNonceTest::testVerifyInvalidKeyThrows":0.001,"Cicnavi\\Tests\\Oidc\\DataStore\\DataHandlers\\StateNonceTest::testVerifyInvalidValueThrows":0.002,"Cicnavi\\Tests\\Oidc\\DataStore\\DataHandlers\\StateNonceTest::testVerifyNonExistantKeyThrows":0.001,"Cicnavi\\Tests\\Oidc\\DataStore\\DataHandlers\\StateNonceTest::testVerify":0.002,"Cicnavi\\Tests\\Oidc\\DataStore\\DataHandlers\\StateNonceTest::testGetInvalidKeyThrows":0,"Cicnavi\\Tests\\Oidc\\DataStore\\DataHandlers\\StateNonceTest::testGetExistingValue":0.001,"Cicnavi\\Tests\\Oidc\\DataStore\\DataHandlers\\StateNonceTest::testGetNewValue":0.002,"Cicnavi\\Tests\\Oidc\\DataStore\\PhpSessionDataStoreTest::testPut":0.001,"Cicnavi\\Tests\\Oidc\\DataStore\\PhpSessionDataStoreTest::testDelete":0.001,"Cicnavi\\Tests\\Oidc\\DataStore\\PhpSessionDataStoreTest::testExists":0.002,"Cicnavi\\Tests\\Oidc\\DataStore\\PhpSessionDataStoreTest::testConstruct":0,"Cicnavi\\Tests\\Oidc\\DataStore\\PhpSessionDataStoreTest::testGet":0,"Cicnavi\\Tests\\Oidc\\Helpers\\HttpHelperTest::testIsRequestHttpSecure":0.001,"Cicnavi\\Tests\\Oidc\\Helpers\\HttpHelperTest::testNormalizeSessionCookieParams":0.001,"Cicnavi\\Tests\\Oidc\\Helpers\\StringHelperTest::testRandom":0.001,"Cicnavi\\Tests\\Oidc\\Helpers\\StringHelperTest::testRandomThrows":0.002,"Cicnavi\\Tests\\Oidc\\Helpers\\StringHelperTest::testRandomThrowsForInvalidCallback":0.001,"Cicnavi\\Tests\\Oidc\\Http\\RequestFactoryTest::testCreateRequest":0.004,"Cicnavi\\Tests\\Oidc\\OpMetadataTest::testConstruct":0.004,"Cicnavi\\Tests\\Oidc\\OpMetadataTest::testConstructThrowsOnRequestException":0.003,"Cicnavi\\Tests\\Oidc\\OpMetadataTest::testConstructThrowsOnWrongResposeCode":0.003,"Cicnavi\\Tests\\Oidc\\OpMetadataTest::testConstructThrowsOnCacheSetException":0.003,"Cicnavi\\Tests\\Oidc\\OpMetadataTest::testMetadataValidationNotArray":0.012,"Cicnavi\\Tests\\Oidc\\OpMetadataTest::testMetadataValidationMissingProperties":0.002,"Cicnavi\\Tests\\Oidc\\OpMetadataTest::testGetUsingInvalidKeyThrows":0.001,"Cicnavi\\Tests\\Oidc\\OpMetadataTest::testAllGetMethods":0}}

src/Entities/ClaimBag.php

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Cicnavi\Oidc\Entities;
6+
7+
class ClaimBag
8+
{
9+
/**
10+
* @param mixed[] $claims
11+
*/
12+
public function __construct(
13+
protected array $claims = [],
14+
) {
15+
}
16+
17+
/**
18+
* @return mixed[]
19+
*/
20+
public function getAll(): array
21+
{
22+
return $this->claims;
23+
}
24+
25+
public function get(string $name): mixed
26+
{
27+
return $this->claims[$name] ?? null;
28+
}
29+
30+
public function has(string $name): bool
31+
{
32+
return isset($this->claims[$name]);
33+
}
34+
35+
public function set(string $name, mixed $value): void
36+
{
37+
$this->claims[$name] = $value;
38+
}
39+
40+
/**
41+
* @param mixed[] $claims
42+
*/
43+
public function merge(array $claims): void
44+
{
45+
$this->claims = array_merge($this->claims, $claims);
46+
}
47+
48+
public function remove(string $name): void
49+
{
50+
unset($this->claims[$name]);
51+
}
52+
53+
public function getAsString(string $name): ?string
54+
{
55+
$ret = $this->get($name);
56+
57+
if (is_string($ret)) {
58+
return $ret;
59+
}
60+
61+
return null;
62+
}
63+
64+
/**
65+
* @return mixed[]|null
66+
*/
67+
public function getAsArray(string $name): ?array
68+
{
69+
$ret = $this->get($name);
70+
71+
if (is_array($ret)) {
72+
return $ret;
73+
}
74+
75+
return null;
76+
}
77+
78+
public function getAsInt(string $name): ?int
79+
{
80+
$ret = $this->get($name);
81+
82+
if (is_int($ret)) {
83+
return $ret;
84+
}
85+
86+
return null;
87+
}
88+
}

src/Entities/KeyPair.php

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Cicnavi\Oidc\Entities;
6+
7+
use SimpleSAML\OpenID\Algorithms\SignatureAlgorithmEnum;
8+
use SimpleSAML\OpenID\Jwk\JwkDecorator;
9+
10+
class KeyPair
11+
{
12+
public function __construct(
13+
protected JwkDecorator $privateKey,
14+
protected JwkDecorator $publicKey,
15+
protected readonly string $keyId,
16+
protected readonly ?string $privateKeyPassword = null,
17+
) {
18+
}
19+
20+
public function getPrivateKey(): JwkDecorator
21+
{
22+
return $this->privateKey;
23+
}
24+
25+
public function getPublicKey(): JwkDecorator
26+
{
27+
return $this->publicKey;
28+
}
29+
30+
public function getKeyId(): string
31+
{
32+
return $this->keyId;
33+
}
34+
35+
public function getPrivateKeyPassword(): ?string
36+
{
37+
return $this->privateKeyPassword;
38+
}
39+
}

src/Entities/KeyPairConfig.php

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Cicnavi\Oidc\Entities;
6+
7+
use SimpleSAML\OpenID\Algorithms\SignatureAlgorithmEnum;
8+
9+
class KeyPairConfig
10+
{
11+
/**
12+
* @param non-empty-string $privateKeyFilename Path to the PEM file containing the
13+
* private key used to sign the JWTs.
14+
* @param non-empty-string $publicKeyFilename Path to the PEM file containing the
15+
* public key used to verify the JWTs.
16+
* @param non-empty-string|null $privateKeyPassword Password for the private key, if
17+
* any.
18+
* @param non-empty-string|null $keyId Key ID to use for the key pair. If not
19+
* provided, a public key thumbprint will be used.
20+
*/
21+
public function __construct(
22+
protected readonly string $privateKeyFilename,
23+
protected readonly string $publicKeyFilename,
24+
protected readonly ?string $privateKeyPassword = null,
25+
protected readonly ?string $keyId = null,
26+
) {
27+
}
28+
29+
/**
30+
* @return non-empty-string
31+
*/
32+
public function getPrivateKeyFilename(): string
33+
{
34+
return $this->privateKeyFilename;
35+
}
36+
37+
/**
38+
* @return non-empty-string
39+
*/
40+
public function getPublicKeyFilename(): string
41+
{
42+
return $this->publicKeyFilename;
43+
}
44+
45+
/**
46+
* @return non-empty-string|null
47+
*/
48+
public function getPrivateKeyPassword(): ?string
49+
{
50+
return $this->privateKeyPassword;
51+
}
52+
53+
/**
54+
* @return non-empty-string|null
55+
*/
56+
public function getKeyId(): ?string
57+
{
58+
return $this->keyId;
59+
}
60+
}

src/Entities/KeyedString.php

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Cicnavi\Oidc\Entities;
6+
7+
class KeyedString implements \JsonSerializable
8+
{
9+
public function __construct(
10+
protected string $key,
11+
protected string $value,
12+
) {
13+
}
14+
15+
public function getKey(): string
16+
{
17+
return $this->key;
18+
}
19+
20+
public function getValue(): string
21+
{
22+
return $this->value;
23+
}
24+
25+
/**
26+
* @return array<string,string>
27+
*/
28+
public function jsonSerialize(): array
29+
{
30+
return [$this->key => $this->value];
31+
}
32+
}

src/Entities/KeyedStringBag.php

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Cicnavi\Oidc\Entities;
6+
7+
class KeyedStringBag implements \JsonSerializable
8+
{
9+
/**
10+
* @var array<string,KeyedString>
11+
*/
12+
protected array $keyedStrings;
13+
14+
public function __construct(
15+
KeyedString ...$keyedStrings
16+
) {
17+
$this->add(...$keyedStrings);
18+
}
19+
20+
public function add(KeyedString ...$keyedStrings): void
21+
{
22+
foreach ($keyedStrings as $keyedString) {
23+
$this->keyedStrings[$keyedString->getKey()] = $keyedString;
24+
}
25+
}
26+
27+
public function has(string $key): bool
28+
{
29+
return isset($this->keyedStrings[$key]);
30+
}
31+
32+
public function get(string $key): ?KeyedString
33+
{
34+
return $this->keyedStrings[$key] ?? null;
35+
}
36+
37+
/**
38+
* @return array<string,KeyedString>
39+
*/
40+
public function getAll(): array
41+
{
42+
return $this->keyedStrings;
43+
}
44+
45+
/**
46+
* @return array<string,string>
47+
*/
48+
public function jsonSerialize(): array
49+
{
50+
$arr = [];
51+
foreach ($this->keyedStrings as $keyedString) {
52+
$arr[$keyedString->getKey()] = $keyedString->getValue();
53+
}
54+
55+
return $arr;
56+
}
57+
}

src/Entities/RedirectUriBag.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Cicnavi\Oidc\Entities;
6+
7+
class RedirectUriBag extends UniqueStringBag
8+
{
9+
public function __construct(
10+
protected readonly string $defaultRedirectUri,
11+
string ...$additionalRedirectUris,
12+
) {
13+
parent::__construct($this->defaultRedirectUri, ...$additionalRedirectUris);
14+
}
15+
16+
public function getDefaultRedirectUri(): string
17+
{
18+
return $this->defaultRedirectUri;
19+
}
20+
}

src/Entities/SignatureKeyPair.php

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Cicnavi\Oidc\Entities;
6+
7+
use SimpleSAML\OpenID\Algorithms\SignatureAlgorithmEnum;
8+
use SimpleSAML\OpenID\Jwk\JwkDecorator;
9+
10+
class SignatureKeyPair
11+
{
12+
public function __construct(
13+
protected readonly SignatureAlgorithmEnum $signatureAlgorithm,
14+
protected readonly KeyPair $keyPair,
15+
) {
16+
}
17+
18+
public function getSignatureAlgorithm(): SignatureAlgorithmEnum
19+
{
20+
return $this->signatureAlgorithm;
21+
}
22+
23+
public function getKeyPair(): KeyPair
24+
{
25+
return $this->keyPair;
26+
}
27+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Cicnavi\Oidc\Entities;
6+
7+
class SignatureKeyPairBag
8+
{
9+
/**
10+
* @var array<string,SignatureKeyPair>
11+
*/
12+
protected array $signatureKeyPairs;
13+
14+
public function __construct(
15+
SignatureKeyPair ...$signatureKeyPairs
16+
) {
17+
$this->add(...$signatureKeyPairs);
18+
}
19+
20+
public function add(SignatureKeyPair ...$signatureKeyPairs): void
21+
{
22+
foreach ($signatureKeyPairs as $signatureKeyPair) {
23+
$this->signatureKeyPairs[$signatureKeyPair->getKeyPair()->getKeyId()] = $signatureKeyPair;
24+
}
25+
}
26+
27+
public function getByKeyId(string $keyId): ?SignatureKeyPair
28+
{
29+
return $this->signatureKeyPairs[$keyId] ?? null;
30+
}
31+
32+
/**
33+
* @return array<string,SignatureKeyPair>
34+
*/
35+
public function getAll(): array
36+
{
37+
return $this->signatureKeyPairs;
38+
}
39+
40+
public function has(string $keyId): bool
41+
{
42+
return isset($this->signatureKeyPairs[$keyId]);
43+
}
44+
}

0 commit comments

Comments
 (0)