Passed
Push — master ( 2f64c5...6a6a0c )
by Thomas Mauro
02:50
created

ClientRegistrationService::__construct()   A

Complexity

Conditions 3
Paths 1

Size

Total Lines 6
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

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