Skip to content

Commit 8708bdc

Browse files
committed
Introduce Jar and RequestObject facades
1 parent 7fa7128 commit 8708bdc

18 files changed

Lines changed: 1619 additions & 0 deletions

src/Codebooks/ClaimsEnum.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,8 @@ enum ClaimsEnum: string
326326

327327
case ProofTypesSupported = 'proof_types_supported';
328328

329+
case PushedAuthorizationRequestEndpoint = 'pushed_authorization_request_endpoint';
330+
329331
case RedirectUris = 'redirect_uris';
330332

331333
// Reference
@@ -359,8 +361,12 @@ enum ClaimsEnum: string
359361

360362
case RequireAuthTime = 'require_auth_time';
361363

364+
case RequirePushedAuthorizationRequests = 'require_pushed_authorization_requests';
365+
362366
case RequireRequestUriRegistration = 'require_request_uri_registration';
363367

368+
case RequireSignedRequestObject = 'require_signed_request_object';
369+
364370
case ResponseModesSupported = 'response_modes_supported';
365371

366372
case ResponseTypes = 'response_types';

src/Codebooks/ParamsEnum.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,8 @@ enum ParamsEnum: string
7171

7272
case Request = 'request';
7373

74+
case RequestUri = 'request_uri';
75+
7476
case ResponseMode = 'response_mode';
7577

7678
case ResponseType = 'response_type';

src/Jar.php

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace SimpleSAML\OpenID;
6+
7+
use DateInterval;
8+
use SimpleSAML\OpenID\Algorithms\AlgorithmManagerDecorator;
9+
use SimpleSAML\OpenID\Decorators\DateIntervalDecorator;
10+
use SimpleSAML\OpenID\Factories\AlgorithmManagerDecoratorFactory;
11+
use SimpleSAML\OpenID\Factories\ClaimFactory;
12+
use SimpleSAML\OpenID\Factories\DateIntervalDecoratorFactory;
13+
use SimpleSAML\OpenID\Factories\JwsSerializerManagerDecoratorFactory;
14+
use SimpleSAML\OpenID\Jar\Factories\RequestObjectFactory;
15+
use SimpleSAML\OpenID\Jwks\Factories\JwksDecoratorFactory;
16+
use SimpleSAML\OpenID\Jws\Factories\JwsDecoratorBuilderFactory;
17+
use SimpleSAML\OpenID\Jws\Factories\JwsVerifierDecoratorFactory;
18+
use SimpleSAML\OpenID\Jws\JwsDecoratorBuilder;
19+
use SimpleSAML\OpenID\Jws\JwsVerifierDecorator;
20+
use SimpleSAML\OpenID\Serializers\JwsSerializerManagerDecorator;
21+
22+
class Jar
23+
{
24+
protected ?DateIntervalDecoratorFactory $dateIntervalDecoratorFactory = null;
25+
26+
protected ?RequestObjectFactory $requestObjectFactory = null;
27+
28+
protected ?JwsSerializerManagerDecorator $jwsSerializerManagerDecorator = null;
29+
30+
protected ?JwsSerializerManagerDecoratorFactory $jwsSerializerManagerDecoratorFactory = null;
31+
32+
protected ?JwsDecoratorBuilderFactory $jwsDecoratorBuilderFactory = null;
33+
34+
protected ?AlgorithmManagerDecoratorFactory $algorithmManagerDecoratorFactory = null;
35+
36+
protected ?AlgorithmManagerDecorator $algorithmManagerDecorator = null;
37+
38+
protected ?Helpers $helpers = null;
39+
40+
protected ?JwsVerifierDecoratorFactory $jwsVerifierDecoratorFactory = null;
41+
42+
protected ?JwsVerifierDecorator $jwsVerifierDecorator = null;
43+
44+
protected ?JwksDecoratorFactory $jwksDecoratorFactory = null;
45+
46+
protected DateIntervalDecorator $timestampValidationLeewayDecorator;
47+
48+
protected ?ClaimFactory $claimFactory = null;
49+
50+
protected ?JwsDecoratorBuilder $jwsDecoratorBuilder = null;
51+
52+
53+
public function __construct(
54+
protected readonly SupportedSerializers $supportedSerializers = new SupportedSerializers(),
55+
protected readonly SupportedAlgorithms $supportedAlgorithms = new SupportedAlgorithms(),
56+
DateInterval $timestampValidationLeeway = new DateInterval('PT1M'),
57+
) {
58+
$this->timestampValidationLeewayDecorator = $this->dateIntervalDecoratorFactory()
59+
->build($timestampValidationLeeway);
60+
}
61+
62+
63+
public function dateIntervalDecoratorFactory(): DateIntervalDecoratorFactory
64+
{
65+
return $this->dateIntervalDecoratorFactory ??= new DateIntervalDecoratorFactory();
66+
}
67+
68+
69+
public function jwsSerializerManagerDecoratorFactory(): JwsSerializerManagerDecoratorFactory
70+
{
71+
return $this->jwsSerializerManagerDecoratorFactory ??= new JwsSerializerManagerDecoratorFactory();
72+
}
73+
74+
75+
public function supportedSerializers(): SupportedSerializers
76+
{
77+
return $this->supportedSerializers;
78+
}
79+
80+
81+
public function jwsDecoratorBuilderFactory(): JwsDecoratorBuilderFactory
82+
{
83+
return $this->jwsDecoratorBuilderFactory ??= new JwsDecoratorBuilderFactory();
84+
}
85+
86+
87+
public function jwsSerializerManagerDecorator(): JwsSerializerManagerDecorator
88+
{
89+
return $this->jwsSerializerManagerDecorator ??= $this->jwsSerializerManagerDecoratorFactory()
90+
->build($this->supportedSerializers);
91+
}
92+
93+
94+
public function algorithmManagerDecoratorFactory(): AlgorithmManagerDecoratorFactory
95+
{
96+
return $this->algorithmManagerDecoratorFactory ??= new AlgorithmManagerDecoratorFactory();
97+
}
98+
99+
100+
public function algorithmManagerDecorator(): AlgorithmManagerDecorator
101+
{
102+
return $this->algorithmManagerDecorator ??= $this->algorithmManagerDecoratorFactory()
103+
->build($this->supportedAlgorithms);
104+
}
105+
106+
107+
public function helpers(): Helpers
108+
{
109+
return $this->helpers ??= new Helpers();
110+
}
111+
112+
113+
public function jwsDecoratorBuilder(): JwsDecoratorBuilder
114+
{
115+
return $this->jwsDecoratorBuilder ??= $this->jwsDecoratorBuilderFactory()->build(
116+
$this->jwsSerializerManagerDecorator(),
117+
$this->algorithmManagerDecorator(),
118+
$this->helpers(),
119+
);
120+
}
121+
122+
123+
public function jwsVerifierDecoratorFactory(): JwsVerifierDecoratorFactory
124+
{
125+
return $this->jwsVerifierDecoratorFactory ??= new JwsVerifierDecoratorFactory();
126+
}
127+
128+
129+
public function jwsVerifierDecorator(): JwsVerifierDecorator
130+
{
131+
return $this->jwsVerifierDecorator ??= $this->jwsVerifierDecoratorFactory()->build(
132+
$this->algorithmManagerDecorator(),
133+
);
134+
}
135+
136+
137+
public function jwksDecoratorFactory(): JwksDecoratorFactory
138+
{
139+
return $this->jwksDecoratorFactory ??= new JwksDecoratorFactory();
140+
}
141+
142+
143+
public function timestampValidationLeewayDecorator(): DateIntervalDecorator
144+
{
145+
return $this->timestampValidationLeewayDecorator;
146+
}
147+
148+
149+
public function claimFactory(): ClaimFactory
150+
{
151+
return $this->claimFactory ??= new ClaimFactory(
152+
$this->helpers(),
153+
);
154+
}
155+
156+
157+
public function requestObjectFactory(): RequestObjectFactory
158+
{
159+
return $this->requestObjectFactory ??= new RequestObjectFactory(
160+
$this->jwsDecoratorBuilder(),
161+
$this->jwsVerifierDecorator(),
162+
$this->jwksDecoratorFactory(),
163+
$this->jwsSerializerManagerDecorator(),
164+
$this->timestampValidationLeewayDecorator(),
165+
$this->helpers(),
166+
$this->claimFactory(),
167+
);
168+
}
169+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace SimpleSAML\OpenID\Jar\Factories;
6+
7+
use SimpleSAML\OpenID\Algorithms\SignatureAlgorithmEnum;
8+
use SimpleSAML\OpenID\Jar\RequestObject;
9+
use SimpleSAML\OpenID\Jwk\JwkDecorator;
10+
use SimpleSAML\OpenID\Jws\Factories\ParsedJwsFactory;
11+
12+
class RequestObjectFactory extends ParsedJwsFactory
13+
{
14+
public function fromToken(string $token): RequestObject
15+
{
16+
return new RequestObject(
17+
$this->jwsDecoratorBuilder->fromToken($token),
18+
$this->jwsVerifierDecorator,
19+
$this->jwksDecoratorFactory,
20+
$this->jwsSerializerManagerDecorator,
21+
$this->timestampValidationLeeway,
22+
$this->helpers,
23+
$this->claimFactory,
24+
);
25+
}
26+
27+
28+
/**
29+
* @param array<non-empty-string,mixed> $payload
30+
* @param array<non-empty-string,mixed> $header
31+
* @throws \SimpleSAML\OpenID\Exceptions\JwsException
32+
*/
33+
public function fromData(
34+
JwkDecorator $signingKey,
35+
SignatureAlgorithmEnum $signatureAlgorithm,
36+
array $payload,
37+
array $header,
38+
): RequestObject {
39+
return new RequestObject(
40+
$this->jwsDecoratorBuilder->fromData(
41+
$signingKey,
42+
$signatureAlgorithm,
43+
$payload,
44+
$header,
45+
),
46+
$this->jwsVerifierDecorator,
47+
$this->jwksDecoratorFactory,
48+
$this->jwsSerializerManagerDecorator,
49+
$this->timestampValidationLeeway,
50+
$this->helpers,
51+
$this->claimFactory,
52+
);
53+
}
54+
}

src/Jar/RequestObject.php

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace SimpleSAML\OpenID\Jar;
6+
7+
use SimpleSAML\OpenID\Algorithms\SignatureAlgorithmEnum;
8+
use SimpleSAML\OpenID\Codebooks\ClaimsEnum;
9+
use SimpleSAML\OpenID\Codebooks\ParamsEnum;
10+
use SimpleSAML\OpenID\Exceptions\RequestObjectException;
11+
use SimpleSAML\OpenID\Jws\ParsedJws;
12+
13+
class RequestObject extends ParsedJws
14+
{
15+
/**
16+
* @throws \SimpleSAML\OpenID\Exceptions\JwsException
17+
* @throws \SimpleSAML\OpenID\Exceptions\RequestObjectException
18+
* @return non-empty-string
19+
*/
20+
public function getClientId(): string
21+
{
22+
$claimKey = ClaimsEnum::ClientId->value;
23+
$clientId = $this->getPayloadClaim($claimKey);
24+
25+
if (is_null($clientId)) {
26+
throw new RequestObjectException('No Client ID claim found.');
27+
}
28+
29+
return $this->helpers->type()->ensureNonEmptyString($clientId, $claimKey);
30+
}
31+
32+
33+
/**
34+
* @throws \SimpleSAML\OpenID\Exceptions\JwsException
35+
* @throws \SimpleSAML\OpenID\Exceptions\RequestObjectException
36+
*/
37+
protected function validateAlgorithm(): void
38+
{
39+
if (is_null($alg = $this->getAlgorithm())) {
40+
throw new RequestObjectException('Missing Algorithm header claim.');
41+
}
42+
43+
$signatureAlgorithm = SignatureAlgorithmEnum::tryFrom($alg) ?? throw new RequestObjectException(
44+
'Invalid Algorithm header claim.',
45+
);
46+
47+
if ($signatureAlgorithm === SignatureAlgorithmEnum::none) {
48+
throw new RequestObjectException('Algorithm header claim must not be "none".');
49+
}
50+
}
51+
52+
53+
/**
54+
* @throws \SimpleSAML\OpenID\Exceptions\JwsException
55+
* @throws \SimpleSAML\OpenID\Exceptions\RequestObjectException
56+
*/
57+
protected function validateForbiddenRequestObjectParams(): void
58+
{
59+
foreach ([ParamsEnum::Request, ParamsEnum::RequestUri] as $param) {
60+
if (array_key_exists($param->value, $this->getPayload())) {
61+
throw new RequestObjectException(
62+
sprintf('Request Object must not contain %s.', $param->value),
63+
);
64+
}
65+
}
66+
}
67+
68+
69+
/**
70+
* @throws \SimpleSAML\OpenID\Exceptions\JwsException
71+
*/
72+
protected function validate(): void
73+
{
74+
$this->validateByCallbacks(
75+
$this->getClientId(...),
76+
$this->validateAlgorithm(...),
77+
$this->validateForbiddenRequestObjectParams(...),
78+
);
79+
}
80+
}

0 commit comments

Comments
 (0)