-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathBaseCertificateContext.php
161 lines (150 loc) · 5.36 KB
/
BaseCertificateContext.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
<?php
/**
* Copyright (c) Microsoft Corporation. All Rights Reserved.
* Licensed under the MIT License. See License in the project root
* for license information.
*/
namespace Microsoft\Kiota\Authentication\Oauth;
use Firebase\JWT\JWT;
use Ramsey\Uuid\Uuid;
use InvalidArgumentException;
/**
* Class BaseCertificateContext
* @package Microsoft\Kiota\Authentication\Oauth
* @copyright 2023 Microsoft Corporation
* @license https://opensource.org/licenses/MIT MIT License
* @link https://learn.microsoft.com/en-us/openapi/kiota/
*/
class BaseCertificateContext
{
/**
* @var string Tenant Id
*/
private string $tenantId;
/**
* @var string Client Id
*/
private string $clientId;
/**
* @var string
*/
private string $certificateFingerprint;
/**
* @var string JWT token signed with the private key
*/
private string $clientAssertion;
/**
* @param string $tenantId
* @param string $clientId
* @param string $certificatePath PEM file containing the certificate
* @param string $privateKeyPath PEM file containing the certificate's private key
* @param string $privateKeyPassphrase password protecting the private key
*/
public function __construct(string $tenantId,
string $clientId,
string $certificatePath,
string $privateKeyPath,
string $privateKeyPassphrase = '')
{
if (!$tenantId || !$clientId || !$certificatePath || !$privateKeyPath) {
throw new InvalidArgumentException(
'$tenantId, $clientId, $certificatePath or $privateKeyPath cannot be empty.'
);
}
$this->tenantId = $tenantId;
$this->clientId = $clientId;
$certificateContents = file_get_contents($certificatePath);
if (!$certificateContents) {
throw new InvalidArgumentException("Unable to read certificate file content at $certificatePath.");
}
$certificate = openssl_x509_read($certificateContents);
if (!$certificate) {
throw new InvalidArgumentException("Could not read X.509 certificate at $certificatePath.");
}
$fingerPrint = openssl_x509_fingerprint($certificate);
if (!$fingerPrint) {
throw new InvalidArgumentException(
"Failed to calculate the fingerprint of the X.509 certificate at $certificatePath."
);
}
$this->certificateFingerprint = $fingerPrint;
$privateKeyContents = file_get_contents($privateKeyPath);
if (!$privateKeyContents) {
throw new InvalidArgumentException("Unable to read private key file contents at $privateKeyPath.");
}
$privateKey = openssl_pkey_get_private($privateKeyContents, $privateKeyPassphrase);
if (!$privateKey) {
throw new InvalidArgumentException(
"Failed to read the private key at $privateKeyPath using passphrase $privateKeyPassphrase."
);
}
if (!openssl_x509_check_private_key($certificate, $privateKey)) {
throw new InvalidArgumentException(
"Private Key at $privateKeyPath does not correspond to the certificate at $certificatePath."
);
}
$this->clientAssertion = $this->getClientAssertion($privateKey);
}
/**
* @return array<string, string>
*/
public function getParams(): array
{
return [
'client_id' => $this->clientId,
'client_assertion' => $this->clientAssertion,
'client_assertion_type' => 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer'
];
}
/**
* @param string $refreshToken
* @return array<string, string>
*/
public function getRefreshTokenParams(string $refreshToken): array
{
return [
'client_id' => $this->clientId,
'client_assertion' => $this->clientAssertion,
'client_assertion_type' => 'urn:ietf:params:Oauth:client-assertion-type:jwt-bearer',
'refresh_token' => $refreshToken,
'grant_type' => 'refresh_token'
];
}
/**
* @return string
*/
public function getTenantId(): string
{
return $this->tenantId;
}
/**
* @return string
*/
public function getClientId(): string
{
return $this->clientId;
}
/**
* Generates JSON Web Token ref (https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-certificate-credentials)
* @param $privateKey
* @return string
* @phpstan-ignore-next-line
*/
private function getClientAssertion($privateKey): string
{
$currentTimeSecs = time();
$claims = [
'aud' => "https://login.microsoftonline.com/{$this->tenantId}/v2.0",
'iss' => $this->clientId,
'jti' => Uuid::uuid4(), // random UUID based on RFC 4122
'sub' => $this->clientId,
'iat' => $currentTimeSecs,
'nbf' => $currentTimeSecs,
'exp' => $currentTimeSecs + (5 * 60), // add 5 minutes to iat
];
$hexBinInput = hex2bin($this->certificateFingerprint);
return JWT::encode($claims, $privateKey, 'RS256', null, [
'x5t' => JWT::urlsafeB64Encode($hexBinInput ?: '')
]);
}
}