This repository has been archived by the owner on Aug 22, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Authentication.php
204 lines (173 loc) · 6.91 KB
/
Authentication.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
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
<?php
namespace AD_B2C_Auth;
use Lcobucci\JWT\ValidationData;
use Lcobucci\JWT\Parser;
use Jose\Component\Core\JWK;
use Jose\Component\Signature\JWSVerifier;
use Jose\Component\Signature\Algorithm;
use Jose\Component\Signature\Serializer\CompactSerializer;
use Jose\Component\Core\AlgorithmManagerFactory;
use Jose\Component\Core\Converter\StandardConverter;
use Jose\Component\Signature\Algorithm\RS256;
use Exception;
class Authentication {
private const TOKEN_POST_NAME = 'id_token';
private const TOKEN_COOKIE_NAME = 'auth_id_token';
public static function getSignInSignUpInstance() {
static $inst = null;
if ($inst === null) {
$settings = Settings::getInstance();
$inst = new self($settings->getTenantName(), $settings->getSignInSignUpPolicyName(), $settings->getClientId());
}
return $inst;
}
public static function getPasswordResetInstance() {
static $inst = null;
if ($inst === null) {
$settings = Settings::getInstance();
$inst = new self($settings->getTenantName(), $settings->getPasswordResetPolicyName(), $settings->getClientId());
}
return $inst;
}
private $tenant;
private $policy_name;
private $client_id;
private $token;
/**
* Initializes the object
*
* @param string $tenant
* @param string $policy_name
*/
public function __construct(string $tenant, string $policy_name, string $client_id) {
$this->tenant = $tenant;
$this->policy_name = $policy_name;
$this->client_id = $client_id;
}
public function getProviderMetadata() {
$metadata_url = 'https://login.microsoftonline.com/' .
$this->tenant .
'/v2.0/.well-known/openid-configuration?p=' .
$this->policy_name;
$response = wp_remote_get($metadata_url);
$decoded_response = json_decode( $response['body'], true );
if (count($decoded_response) == 0 ) {
throw new Exception('Unable to retrieve metadata from ' . $metadata_url);
}
return $decoded_response;
}
public function getAuthorizationUrl( $redirect_uri, $state ) {
// Get the OpenID Provider Metadata document
$metadata = $this->getProviderMetadata();
$settings = Settings::getInstance();
// Build up the URL for login
return $metadata['authorization_endpoint'] .
'&client_id=' . $settings->getClientId() .
'&response_type=' . $settings->getResponseType() .
'&redirect_uri=' . $redirect_uri .
'&scope=' . $settings->getScope() .
'&nonce=' . NonceUtil::generate( $settings->getNonceSecret() ) .
'&response_mode=' . $settings->getResponseMode() .
'&state=' . $state;
}
public function getToken() {
if ( !isset($this->token) ) {
$token_str = $this->getTokenString();
if ( isset($token_str) ) {
// Parse the token string into an object
$this->token = (new Parser())->parse($token_str);
}
}
return $this->token;
}
private function getTokenString() {
if ( $this->isTokenPosted() ) {
// Parse the token string into an object
return $_POST[self::TOKEN_POST_NAME];
}
if ( $this->isTokenSaved() ) {
return $_COOKIE[self::TOKEN_COOKIE_NAME];
}
return null;
}
public function isTokenPosted() {
return isset($_POST[self::TOKEN_POST_NAME]);
}
public function isTokenSaved() {
return isset($_COOKIE[self::TOKEN_COOKIE_NAME]);
}
public function isTokenValid( ) {
// Parse the token string into an object
$token = $this->getToken();
if ( !isset($token) ) {
return false;
}
// Get the OpenID Provider Metadata document
$metadata = $this->getProviderMetadata();
// Get the JWKSet
$jwks = $this->getJWKSet( $metadata );
$kid = $token->getHeader('kid');
foreach ($jwks['keys'] as $key) {
if ($key['kid'] == $kid) {
$jwk = $key;
break;
}
}
if ( !isset($jwk) ) {
throw new Exception("Could not find the correct key.");
}
$jwk = JWK::create($jwk);
if (!$jwk->has('kty') || $jwk->get('kty') !== 'RSA') {
throw new Exception("Could not find a key with the correct encryption type.");
}
// Convert the token string to an object
$serializer = new CompactSerializer(new StandardConverter());
$jws = $serializer->unserialize($this->getTokenString());
// Get the signature
$algorithm_key = 'RS256';
// Get the signature verifier class
$algorithm_manager_factory = new AlgorithmManagerFactory();
$algorithm_manager_factory->add($algorithm_key, new Algorithm\RS256());
$algorithm_manager = $algorithm_manager_factory->create( [$algorithm_key] );
$verifier = new JWSVerifier( $algorithm_manager );
// Verify the token's signature
try {
$is_verified = $verifier->verifyWithKey($jws, $jwk, 0);
} catch (\Throwable $e) {
throw new Exception("B2C Error: An error occurred while verifying the key. Ensure the GMP extension for PHP is active.");
}
if (!$is_verified) {
throw new Exception("Invalid response from the authentication server.");
}
// Set up the validation data to check the token's claims against
$validation_data = new ValidationData();
$validation_data->setIssuer( $metadata['issuer'] );
$validation_data->setAudience( $this->client_id );
$validation_data->setId( Settings::getInstance()->getNonceSecret() );
// Validate the token
return $token->validate($validation_data);
}
private function getJWKSet( $metadata ) {
$jwks_uri = $metadata['jwks_uri'];
$response = wp_remote_get($jwks_uri);
$decoded_response = json_decode( $response['body'], true );
if (count($decoded_response) == 0 ) {
throw new Exception('Unable to retrieve JWKS.');
}
return $decoded_response;
}
public function saveToken() {
if (!$this->isTokenPosted()) {
throw new Exception('Unable to get token string.');
}
// TODO: THIS NEEDS TO BE MORE SECURE!!!
setcookie('auth_id_token', $_POST[self::TOKEN_POST_NAME], 0, COOKIEPATH, COOKIE_DOMAIN, false, true);
}
public function unsaveToken() {
if (!$this->isTokenSaved()) {
throw new Exception('Unable to get token string.');
}
// TODO: THIS NEEDS TO BE MORE SECURE!!!
setcookie('auth_id_token', '', time() - DAY_IN_SECONDS, COOKIEPATH, COOKIE_DOMAIN, false, true);
}
}