1 | <?php |
||
28 | final class ClientAssertionJwt implements AuthenticationMethod |
||
29 | { |
||
30 | /** |
||
31 | * @var JwsCompactSerializer |
||
32 | */ |
||
33 | private $jwsSerializer; |
||
34 | |||
35 | /** |
||
36 | * @var JWSVerifier |
||
37 | */ |
||
38 | private $jwsVerifier; |
||
39 | |||
40 | /** |
||
41 | * @var JweCompactSerializer |
||
42 | */ |
||
43 | private $jweSerializer; |
||
44 | |||
45 | /** |
||
46 | * @var JWEDecrypter |
||
47 | */ |
||
48 | private $jweDecrypter; |
||
49 | |||
50 | /** |
||
51 | * @var bool |
||
52 | */ |
||
53 | private $encryptionRequired = false; |
||
54 | |||
55 | /** |
||
56 | * @var JWKSet|null |
||
57 | */ |
||
58 | private $keyEncryptionKeySet = null; |
||
59 | |||
60 | /** |
||
61 | * @var int |
||
62 | */ |
||
63 | private $secretLifetime; |
||
64 | |||
65 | /** |
||
66 | * @var ClaimCheckerManager |
||
67 | */ |
||
68 | private $claimCheckerManager; |
||
69 | |||
70 | /** |
||
71 | * ClientAssertionJwt constructor. |
||
72 | * |
||
73 | * @param JwsCompactSerializer $jwsSerializer |
||
74 | * @param JWSVerifier $jwsVerifier |
||
75 | * @param ClaimCheckerManager $claimCheckerManager |
||
76 | * @param int $secretLifetime |
||
77 | */ |
||
78 | public function __construct(JwsCompactSerializer $jwsSerializer, JWSVerifier $jwsVerifier, ClaimCheckerManager $claimCheckerManager, int $secretLifetime = 0) |
||
88 | |||
89 | /** |
||
90 | * @param JweCompactSerializer $jweSerializer |
||
91 | * @param JWEDecrypter $jweDecrypter |
||
92 | * @param JWKSet $keyEncryptionKeySet |
||
93 | * @param bool $encryptionRequired |
||
94 | */ |
||
95 | public function enableEncryptedAssertions(JweCompactSerializer $jweSerializer, JWEDecrypter $jweDecrypter, JWKSet $keyEncryptionKeySet, bool $encryptionRequired) |
||
102 | |||
103 | /** |
||
104 | * @return string[] |
||
105 | */ |
||
106 | public function getSupportedSignatureAlgorithms(): array |
||
110 | |||
111 | /** |
||
112 | * @return string[] |
||
113 | */ |
||
114 | public function getSupportedContentEncryptionAlgorithms(): array |
||
118 | |||
119 | /** |
||
120 | * @return string[] |
||
121 | */ |
||
122 | public function getSupportedKeyEncryptionAlgorithms(): array |
||
126 | |||
127 | /** |
||
128 | * {@inheritdoc} |
||
129 | */ |
||
130 | public function getSchemesParameters(): array |
||
134 | |||
135 | /** |
||
136 | * {@inheritdoc} |
||
137 | */ |
||
138 | public function findClientIdAndCredentials(ServerRequestInterface $request, &$clientCredentials = null): ? ClientId |
||
139 | { |
||
140 | $parameters = $request->getParsedBody() ?? []; |
||
141 | if (!array_key_exists('client_assertion_type', $parameters)) { |
||
142 | return null; |
||
143 | } |
||
144 | $clientAssertionType = $parameters['client_assertion_type']; |
||
145 | |||
146 | //We verify the client assertion type in the request |
||
147 | if ('urn:ietf:params:oauth:client-assertion-type:jwt-bearer' !== $clientAssertionType) { |
||
148 | return null; |
||
149 | } |
||
150 | |||
151 | try { |
||
152 | if (!array_key_exists('client_assertion', $parameters)) { |
||
153 | throw new \InvalidArgumentException('Parameter "client_assertion" is missing.'); |
||
154 | } |
||
155 | $client_assertion = $parameters['client_assertion']; |
||
156 | $client_assertion = $this->tryToDecryptClientAssertion($client_assertion); |
||
157 | $jws = $this->jwsSerializer->unserialize($client_assertion); |
||
158 | if (1 !== $jws->countSignatures()) { |
||
159 | throw new \InvalidArgumentException('The assertion must have only one signature.'); |
||
160 | } |
||
161 | $claims = json_decode($jws->getPayload(), true); |
||
162 | $claims = $this->claimCheckerManager->check($claims); |
||
163 | |||
164 | $diff = array_diff(['iss', 'sub', 'aud', 'jti', 'exp'], array_keys($claims)); |
||
165 | if (!empty($diff)) { |
||
166 | throw new \InvalidArgumentException(sprintf('The following claim(s) is/are mandatory: "%s".', implode(', ', array_values($diff)))); |
||
167 | } |
||
168 | if ($claims['sub'] !== $claims['iss']) { |
||
169 | throw new \InvalidArgumentException('The claims "sub" and "iss" must contain the client public ID.'); |
||
170 | } |
||
171 | } catch (\Exception $e) { |
||
172 | throw new OAuth2Exception(400, OAuth2Exception::ERROR_INVALID_REQUEST, $e->getMessage()); |
||
173 | } |
||
174 | |||
175 | $clientCredentials = $jws; |
||
176 | |||
177 | return ClientId::create($claims['sub']); |
||
178 | } |
||
179 | |||
180 | /** |
||
181 | * @param string $assertion |
||
182 | * |
||
183 | * @return string |
||
184 | * |
||
185 | * @throws OAuth2Exception |
||
186 | */ |
||
187 | private function tryToDecryptClientAssertion(string $assertion): string |
||
211 | |||
212 | /** |
||
213 | * {@inheritdoc} |
||
214 | */ |
||
215 | public function isClientAuthenticated(Client $client, $clientCredentials, ServerRequestInterface $request): bool |
||
228 | |||
229 | /** |
||
230 | * {@inheritdoc} |
||
231 | */ |
||
232 | public function getSupportedMethods(): array |
||
233 | { |
||
234 | return ['client_secret_jwt', 'private_key_jwt']; |
||
235 | } |
||
236 | |||
237 | /** |
||
238 | * {@inheritdoc} |
||
239 | */ |
||
240 | public function checkClientConfiguration(DataBag $commandParameters, DataBag $validatedParameters): DataBag |
||
266 | |||
267 | /** |
||
268 | * @return string |
||
269 | */ |
||
270 | private function createClientSecret(): string |
||
274 | } |
||
275 |
Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code: