Passed
Push — master ( 106006...c1ddb5 )
by Thomas Mauro
06:38 queued 11s
created

RegistrationService::update()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 39
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

Changes 0
Metric Value
cc 4
eloc 19
nc 4
nop 3
dl 0
loc 39
ccs 0
cts 27
cp 0
crap 20
rs 9.6333
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace TMV\OpenIdClient\Service;
6
7
use function array_diff_key;
8
use function array_flip;
9
use function array_intersect_key;
10
use function array_key_exists;
11
use function array_merge;
12
use Http\Discovery\Psr17FactoryDiscovery;
13
use Http\Discovery\Psr18ClientDiscovery;
14
use function json_encode;
15
use Psr\Http\Client\ClientExceptionInterface;
16
use Psr\Http\Client\ClientInterface;
17
use Psr\Http\Message\RequestFactoryInterface;
18
use function TMV\OpenIdClient\check_server_response;
19
use TMV\OpenIdClient\Exception\InvalidArgumentException;
20
use TMV\OpenIdClient\Exception\RuntimeException;
21
use TMV\OpenIdClient\Issuer\IssuerInterface;
22
use function TMV\OpenIdClient\parse_metadata_response;
23
24
/**
25
 * Dynamic Client Registration Protocol
26
 *
27
 * @link https://tools.ietf.org/html/rfc7591
28
 * @link https://openid.net/specs/openid-connect-registration-1_0.html
29
 */
30
class RegistrationService
31
{
32
    /** @var ClientInterface */
33
    private $client;
34
35
    /** @var RequestFactoryInterface */
36
    private $requestFactory;
37
38
    /** @var string[] */
39
    private static $registrationClaims = [
40
        'registration_access_token',
41
        'registration_client_uri',
42
        'client_secret_expires_at',
43
        'client_id_issued_at',
44
    ];
45
46
    public function __construct(
47
        ?ClientInterface $client = null,
48
        ?RequestFactoryInterface $requestFactory = null
49
    ) {
50
        $this->client = $client ?: Psr18ClientDiscovery::find();
51
        $this->requestFactory = $requestFactory ?: Psr17FactoryDiscovery::findRequestFactory();
52
    }
53
54
    public function register(
55
        IssuerInterface $issuer,
56
        array $metadata,
57
        ?string $initialToken = null
58
    ): array {
59
        $registrationEndpoint = $issuer->getMetadata()->getRegistrationEndpoint();
60
61
        if (null === $registrationEndpoint) {
62
            throw new InvalidArgumentException('Issuer does not support dynamic client registration');
63
        }
64
65
        $encodedMetadata = json_encode($metadata);
66
67
        if (false === $encodedMetadata) {
68
            throw new RuntimeException('Unable to encode client metadata');
69
        }
70
71
        $request = $this->requestFactory->createRequest('POST', $registrationEndpoint)
72
            ->withHeader('content-type', 'application/json')
73
            ->withHeader('accept', 'application/json');
74
75
        $request->getBody()->write($encodedMetadata);
76
77
        if (null !== $initialToken) {
78
            $request = $request->withHeader('authorization', 'Bearer ' . $initialToken);
79
        }
80
81
        try {
82
            $response = $this->client->sendRequest($request);
83
        } catch (ClientExceptionInterface $e) {
84
            throw new RuntimeException('Unable to register OpenID client', 0, $e);
85
        }
86
87
        $data = parse_metadata_response($response, 201);
88
89
        if (! array_key_exists('client_id', $data)) {
90
            throw new RuntimeException('Registration response did not return a client_id field');
91
        }
92
93
        return $data;
94
    }
95
96
    public function read(string $clientUri, string $accessToken): array
97
    {
98
        $request = $this->requestFactory->createRequest('GET', $clientUri)
99
            ->withHeader('accept', 'application/json')
100
            ->withHeader('authorization', 'Bearer ' . $accessToken);
101
102
        try {
103
            $response = $this->client->sendRequest($request);
104
        } catch (ClientExceptionInterface $e) {
105
            throw new RuntimeException('Unable to read OpenID client', 0, $e);
106
        }
107
108
        $claims = parse_metadata_response($response, 200);
109
110
        if (! array_key_exists('client_id', $claims)) {
111
            throw new RuntimeException('Registration response did not return a client_id field');
112
        }
113
114
        return $claims;
115
    }
116
117
    public function update(
118
        string $clientUri,
119
        string $accessToken,
120
        array $metadata
121
    ): array {
122
        /** @var array<string, mixed> $clientRegistrationMetadata */
123
        $clientRegistrationMetadata = array_intersect_key($metadata, array_flip(static::$registrationClaims));
0 ignored issues
show
Bug introduced by
Since $registrationClaims is declared private, accessing it with static will lead to errors in possible sub-classes; you can either use self, or increase the visibility of $registrationClaims to at least protected.
Loading history...
124
        /** @var array<string, mixed> $metadata */
125
        $metadata = array_diff_key($metadata, $clientRegistrationMetadata);
126
127
        $encodedMetadata = json_encode($metadata);
128
129
        if (false === $encodedMetadata) {
130
            throw new RuntimeException('Unable to encode client metadata');
131
        }
132
133
        $request = $this->requestFactory->createRequest('PUT', $clientUri)
134
            ->withHeader('accept', 'application/json')
135
            ->withHeader('content-type', 'application/json')
136
            ->withHeader('authorization', 'Bearer ' . $accessToken);
137
138
        $request->getBody()->write($encodedMetadata);
139
140
        try {
141
            $response = $this->client->sendRequest($request);
142
        } catch (ClientExceptionInterface $e) {
143
            throw new RuntimeException('Unable to update OpenID client', 0, $e);
144
        }
145
146
        $data = parse_metadata_response($response, 200);
147
148
        if (! array_key_exists('client_id', $data)) {
149
            throw new RuntimeException('Registration response did not return a client_id field');
150
        }
151
152
        /** @var array<string, mixed> $merged */
153
        $merged = array_merge($clientRegistrationMetadata, $data);
154
155
        return $merged;
156
    }
157
158
    public function delete(string $clientUri, string $accessToken): void
159
    {
160
        $request = $this->requestFactory->createRequest('DELETE', $clientUri)
161
            ->withHeader('authorization', 'Bearer ' . $accessToken);
162
163
        try {
164
            $response = $this->client->sendRequest($request);
165
        } catch (ClientExceptionInterface $e) {
166
            throw new RuntimeException('Unable to delete OpenID client', 0, $e);
167
        }
168
169
        check_server_response($response, 204);
170
    }
171
}
172