Issues (120)

tests/RequestObject/RequestObjectFactoryTest.php (4 issues)

Labels
Severity
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Facile\OpenIDClientTest\RequestObject;
6
7
use Facile\JoseVerifier\JWK\JwksProviderInterface;
8
use function Facile\OpenIDClient\base64url_decode;
9
use function Facile\OpenIDClient\base64url_encode;
10
use Facile\OpenIDClient\Client\ClientInterface;
11
use Facile\OpenIDClient\Client\Metadata\ClientMetadataInterface;
12
use Facile\OpenIDClient\Issuer\IssuerInterface;
13
use Facile\OpenIDClient\Issuer\Metadata\IssuerMetadataInterface;
14
use Facile\OpenIDClient\RequestObject\RequestObjectFactory;
15
use Jose\Component\Core\AlgorithmManager;
16
use Jose\Component\Core\JWK;
17
use Jose\Component\Encryption\Algorithm\KeyEncryption\RSAOAEP;
18
use Jose\Component\Encryption\JWE;
19
use Jose\Component\Encryption\JWEBuilder;
20
use Jose\Component\Encryption\Serializer\JWESerializer;
21
use Jose\Component\Signature\Algorithm\RS256;
22
use Jose\Component\Signature\JWS;
23
use Jose\Component\Signature\JWSBuilder;
24
use Jose\Component\Signature\Serializer\JWSSerializer;
25
use Facile\OpenIDClientTest\TestCase;
26
use Prophecy\Argument;
27
use Prophecy\Prophecy\ObjectProphecy;
28
29
class RequestObjectFactoryTest extends TestCase
30
{
31
    /** @var ObjectProphecy|AlgorithmManager */
32
    private $algorithmManager;
33
34
    /** @var ObjectProphecy|JWSBuilder */
35
    private $jwsBuilder;
36
37
    /** @var ObjectProphecy|JWEBuilder */
38
    private $jweBuilder;
39
40
    /** @var ObjectProphecy|JWSSerializer */
41
    private $jwsSerializer;
42
43
    /** @var ObjectProphecy|JWESerializer */
44
    private $jweSerializer;
45
46
    /** @var ObjectProphecy|ClientInterface */
47
    private $client;
48
49
    /** @var ObjectProphecy|ClientMetadataInterface */
50
    private $clientMetadata;
51
52
    /** @var ObjectProphecy|IssuerInterface */
53
    private $issuer;
54
55
    /** @var ObjectProphecy|IssuerMetadataInterface */
56
    private $issuerMetadata;
57
58
    /** @var RequestObjectFactory */
59
    private $factory;
60
61
    protected function setUp(): void
62
    {
63
        parent::setUp();
64
65
        $this->algorithmManager = $this->prophesize(AlgorithmManager::class);
66
        $this->jwsBuilder = $this->prophesize(JWSBuilder::class);
67
        $this->jweBuilder = $this->prophesize(JWEBuilder::class);
68
        $this->jwsSerializer = $this->prophesize(JWSSerializer::class);
69
        $this->jweSerializer = $this->prophesize(JWESerializer::class);
70
71
        $client = $this->prophesize(ClientInterface::class);
72
        $issuer = $this->prophesize(IssuerInterface::class);
73
        $clientMetadata = $this->prophesize(ClientMetadataInterface::class);
74
        $issuerMetadata = $this->prophesize(IssuerMetadataInterface::class);
75
76
        $client->getIssuer()->willReturn($issuer->reveal());
77
        $client->getMetadata()->willReturn($clientMetadata->reveal());
78
        $clientMetadata->getClientId()->willReturn('client-id');
79
        $clientMetadata->getClientSecret()->willReturn('client-secret');
80
        $issuer->getMetadata()->willReturn($issuerMetadata->reveal());
81
        $issuerMetadata->getIssuer()->willReturn('http://issuer.com');
82
83
        $this->client = $client;
84
        $this->clientMetadata = $clientMetadata;
85
        $this->issuer = $issuer;
86
        $this->issuerMetadata = $issuerMetadata;
87
88
        $this->factory = new RequestObjectFactory(
89
            $this->algorithmManager->reveal(),
90
            $this->jwsBuilder->reveal(),
91
            $this->jweBuilder->reveal(),
92
            $this->jwsSerializer->reveal(),
93
            $this->jweSerializer->reveal()
94
        );
95
    }
96
97
    public function testMinimalConstructor(): void
98
    {
99
        $factory = new RequestObjectFactory();
100
        self::assertInstanceOf(RequestObjectFactory::class, $factory);
101
    }
102
103
    public function testCreateWithNoSignAndNoEnc(): void
104
    {
105
        $this->clientMetadata->get('request_object_signing_alg')->willReturn('none');
106
        $this->clientMetadata->get('request_object_encryption_alg')->willReturn(null);
107
        $this->clientMetadata->get('request_object_encryption_enc')->willReturn(null);
108
109
        $token = $this->factory->create($this->client->reveal(), ['foo' => 'bar']);
110
111
        [$header, $payload] = \explode('.', $token);
112
        $header = \json_decode(base64url_decode($header), true);
113
        $payload = \json_decode(base64url_decode($payload), true);
114
115
        self::assertSame('none', $header['alg'] ?? null);
116
        self::assertSame('client-id', $payload['iss'] ?? null);
117
        self::assertSame('client-id', $payload['client_id'] ?? null);
118
        self::assertSame('http://issuer.com', $payload['aud'] ?? null);
119
        self::assertSame('bar', $payload['foo'] ?? null);
120
        self::assertIsString($payload['jti'] ?? null);
121
        self::assertIsInt($payload['iat'] ?? null);
122
        self::assertIsInt($payload['exp'] ?? null);
123
    }
124
125
    public function testCreateWithSymSignAndNoEnc(): void
126
    {
127
        $this->clientMetadata->get('request_object_signing_alg')->willReturn('HS256');
128
        $this->clientMetadata->get('request_object_encryption_alg')->willReturn(null);
129
        $this->clientMetadata->get('request_object_encryption_enc')->willReturn(null);
130
131
        $jws = $this->prophesize(JWS::class);
132
133
        $this->jwsBuilder->create()->willReturn($this->jwsBuilder->reveal());
134
        $this->jwsBuilder->withPayload(Argument::type('string'))->willReturn($this->jwsBuilder->reveal());
135
        $this->jwsBuilder->addSignature(Argument::allOf(
0 ignored issues
show
Prophecy\Argument::allOf...on(...) { /* ... */ })) of type Prophecy\Argument\Token\LogicalAndToken is incompatible with the type Jose\Component\Core\JWK expected by parameter $signatureKey of Jose\Component\Signature...Builder::addSignature(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

135
        $this->jwsBuilder->addSignature(/** @scrutinizer ignore-type */ Argument::allOf(
Loading history...
136
            Argument::type(JWK::class),
137
            Argument::that(function (JWK $jwk) {
138
                return $jwk->get('k') === base64url_encode('client-secret');
139
            }),
140
            Argument::that(function (JWK $jwk) {
141
                return $jwk->get('kty') === 'oct';
142
            })
143
        ), [
144
            'alg' => 'HS256',
145
            'typ' => 'JWT',
146
        ])
147
            ->willReturn($this->jwsBuilder->reveal());
148
        $this->jwsBuilder->build()->willReturn($jws->reveal());
149
150
        $this->jwsSerializer->serialize($jws->reveal(), 0)
151
            ->willReturn('token');
152
153
        $token = $this->factory->create($this->client->reveal(), ['foo' => 'bar']);
154
155
        self::assertSame('token', $token);
156
    }
157
158
    public function testCreateWithAsymSignAndNoEnc(): void
159
    {
160
        $this->clientMetadata->get('request_object_signing_alg')->willReturn('RS256');
161
        $this->clientMetadata->get('request_object_encryption_alg')->willReturn(null);
162
        $this->clientMetadata->get('request_object_encryption_enc')->willReturn(null);
163
164
        $alg = new RS256();
165
        $this->algorithmManager->get('RS256')->willReturn($alg);
166
167
        $jwksProvider = $this->prophesize(JwksProviderInterface::class);
168
        $jwksProvider->getJwks()->willReturn([
169
            'keys' => [
170
                ['kty' => 'RSA', 'kid' => 'some-key-id', 'alg' => 'RS256', 'use' => 'sig'],
171
            ],
172
        ]);
173
174
        $this->client->getJwksProvider()->willReturn($jwksProvider->reveal());
175
176
        $jws = $this->prophesize(JWS::class);
177
178
        $this->jwsBuilder->create()->willReturn($this->jwsBuilder->reveal());
179
        $this->jwsBuilder->withPayload(Argument::type('string'))->willReturn($this->jwsBuilder->reveal());
180
        $this->jwsBuilder->addSignature(Argument::that(function (JWK $key) {
0 ignored issues
show
Prophecy\Argument::that(...ion(...) { /* ... */ }) of type Prophecy\Argument\Token\CallbackToken is incompatible with the type Jose\Component\Core\JWK expected by parameter $signatureKey of Jose\Component\Signature...Builder::addSignature(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

180
        $this->jwsBuilder->addSignature(/** @scrutinizer ignore-type */ Argument::that(function (JWK $key) {
Loading history...
181
            return 'some-key-id' === $key->get('kid');
182
        }), [
183
            'alg' => 'RS256',
184
            'typ' => 'JWT',
185
            'kid' => 'some-key-id',
186
        ])
187
            ->willReturn($this->jwsBuilder->reveal());
188
        $this->jwsBuilder->build()->willReturn($jws->reveal());
189
190
        $this->jwsSerializer->serialize($jws->reveal(), 0)
191
            ->willReturn('token');
192
193
        $token = $this->factory->create($this->client->reveal(), ['foo' => 'bar']);
194
195
        self::assertSame('token', $token);
196
    }
197
198
    public function testCreateWithNoSignAndSymEnc(): void
199
    {
200
        $this->clientMetadata->get('request_object_signing_alg')->willReturn('none');
201
        $this->clientMetadata->get('request_object_encryption_alg')->willReturn('ASY1');
202
        $this->clientMetadata->get('request_object_encryption_enc')->willReturn('ASY2');
203
204
        $jwe = $this->prophesize(JWE::class);
205
206
        $this->jweBuilder->create()->willReturn($this->jweBuilder->reveal());
207
        $this->jweBuilder->withPayload(Argument::type('string'))->willReturn($this->jweBuilder->reveal());
208
        $this->jweBuilder->withSharedProtectedHeader([
209
            'alg' => 'ASY1',
210
            'enc' => 'ASY2',
211
            'cty' => 'JWT',
212
        ])->willReturn($this->jweBuilder->reveal());
213
        $this->jweBuilder->addRecipient(Argument::allOf(
0 ignored issues
show
Prophecy\Argument::allOf...on(...) { /* ... */ })) of type Prophecy\Argument\Token\LogicalAndToken is incompatible with the type Jose\Component\Core\JWK expected by parameter $recipientKey of Jose\Component\Encryptio...Builder::addRecipient(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

213
        $this->jweBuilder->addRecipient(/** @scrutinizer ignore-type */ Argument::allOf(
Loading history...
214
            Argument::type(JWK::class),
215
            Argument::that(function (JWK $jwk) {
216
                return $jwk->get('k') === base64url_encode('client-secret');
217
            }),
218
            Argument::that(function (JWK $jwk) {
219
                return $jwk->get('kty') === 'oct';
220
            })
221
        ))
222
            ->willReturn($this->jweBuilder->reveal());
223
        $this->jweBuilder->build()->willReturn($jwe->reveal());
224
225
        $this->jweSerializer->serialize($jwe->reveal(), 0)
226
            ->willReturn('token');
227
228
        $token = $this->factory->create($this->client->reveal(), ['foo' => 'bar']);
229
230
        self::assertSame('token', $token);
231
    }
232
233
    public function testCreateWithNoSignAndAsymEnc(): void
234
    {
235
        $this->clientMetadata->get('request_object_signing_alg')->willReturn('none');
236
        $this->clientMetadata->get('request_object_encryption_alg')->willReturn('RSA-OAEP');
237
        $this->clientMetadata->get('request_object_encryption_enc')->willReturn('ASY2');
238
239
        $alg = new RSAOAEP();
240
        $this->algorithmManager->get('RSA-OAEP')->willReturn($alg);
241
242
        $jwksProvider = $this->prophesize(JwksProviderInterface::class);
243
        $jwksProvider->getJwks()->willReturn([
244
            'keys' => [
245
                ['kty' => 'RSA', 'kid' => 'some-key-id', 'alg' => 'RSA-OAEP', 'use' => 'enc'],
246
            ],
247
        ]);
248
        $this->issuer->getJwksProvider()->willReturn($jwksProvider->reveal());
249
250
        $jwe = $this->prophesize(JWE::class);
251
252
        $this->jweBuilder->create()->willReturn($this->jweBuilder->reveal());
253
        $this->jweBuilder->withPayload(Argument::type('string'))->willReturn($this->jweBuilder->reveal());
254
        $this->jweBuilder->withSharedProtectedHeader([
255
            'alg' => 'RSA-OAEP',
256
            'enc' => 'ASY2',
257
            'cty' => 'JWT',
258
            'kid' => 'some-key-id',
259
        ])->willReturn($this->jweBuilder->reveal());
260
        $this->jweBuilder->addRecipient(Argument::that(function (JWK $key) {
0 ignored issues
show
Prophecy\Argument::that(...ion(...) { /* ... */ }) of type Prophecy\Argument\Token\CallbackToken is incompatible with the type Jose\Component\Core\JWK expected by parameter $recipientKey of Jose\Component\Encryptio...Builder::addRecipient(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

260
        $this->jweBuilder->addRecipient(/** @scrutinizer ignore-type */ Argument::that(function (JWK $key) {
Loading history...
261
            return 'some-key-id' === $key->get('kid');
262
        }))
263
            ->willReturn($this->jweBuilder->reveal());
264
        $this->jweBuilder->build()->willReturn($jwe->reveal());
265
266
        $this->jweSerializer->serialize($jwe->reveal(), 0)
267
            ->willReturn('token');
268
269
        $token = $this->factory->create($this->client->reveal(), ['foo' => 'bar']);
270
271
        self::assertSame('token', $token);
272
    }
273
}
274