Skip to content

Commit 7c0f784

Browse files
authored
Merge pull request #186 from BertVM52/unit_tests
Add unit tests
2 parents 83465d3 + e7302de commit 7c0f784

File tree

7 files changed

+764
-0
lines changed

7 files changed

+764
-0
lines changed

composer.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,5 +32,13 @@
3232
"psr-4": {
3333
"TheNetworg\\OAuth2\\Client\\": "src/"
3434
}
35+
},
36+
"autoload-dev": {
37+
"psr-4": {
38+
"TheNetworg\\OAuth2\\Client\\Tests\\": "tests/"
39+
}
40+
},
41+
"require-dev": {
42+
"phpunit/phpunit": "^9.6"
3543
}
3644
}

tests/Fakers/B2cTokenFaker.php

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
<?php
2+
3+
namespace TheNetworg\OAuth2\Client\Tests\Fakers;
4+
5+
use Firebase\JWT\JWT;
6+
7+
class B2cTokenFaker
8+
{
9+
10+
/** @var string */
11+
private $publicKey;
12+
/** @var string */
13+
private $modulus;
14+
/** @var string */
15+
private $exponent;
16+
17+
18+
/** @var array<string, string> */
19+
private $fakeData;
20+
21+
public function __construct()
22+
{
23+
$this->publicKey = 'pubkey';
24+
$this->modulus = 'n';
25+
$this->exponent = 'e';
26+
}
27+
28+
/**
29+
* @return array<string, mixed>
30+
*/
31+
public function getB2cTokenResponse(): array
32+
{
33+
return $this->createResponse();
34+
}
35+
36+
/**
37+
* @param string $b2cId
38+
* @param bool $isSuccessful
39+
*/
40+
public function setFakeData(
41+
string $b2cId,
42+
bool $isSuccessful,
43+
string $clientId,
44+
string $issuer,
45+
?int $expires = null,
46+
?int $notBefore = null
47+
): void
48+
{
49+
$this->fakeData = [
50+
'sub' => $b2cId,
51+
'is_b2c_successful' => $isSuccessful,
52+
'aud' => $clientId,
53+
'iss' => $issuer,
54+
'exp' => $expires ?? time() + 3600, // expires in one hour
55+
'nbf' => $notBefore ?? time(),
56+
];
57+
}
58+
59+
/**
60+
* @param array<string, string> $fakeData
61+
* @return array<string, mixed>
62+
*/
63+
private function createResponse(): array
64+
{
65+
66+
if ($this->fakeData['is_b2c_successful']) {
67+
68+
$accessToken = array(
69+
'iss' => 'iss',
70+
'exp' => $this->fakeData['exp'],
71+
'nbf' => $this->fakeData['nbf'],
72+
'aud' => $this->fakeData['aud'],
73+
'idp_access_token' => '123',
74+
'idp' => 'idp',
75+
'sub' => $this->fakeData['sub'],
76+
'tfp' => 'tfp',
77+
'ver' => '1.0',
78+
'iat' => time()
79+
);
80+
81+
$idToken = array(
82+
"exp" => $this->fakeData['exp'],
83+
"nbf" => $this->fakeData['nbf'],
84+
"ver" => "1.0",
85+
"iss" => $this->fakeData['iss'],
86+
"sub" => $this->fakeData['sub'],
87+
"aud" => $this->fakeData['aud'],
88+
"iat" => time(),
89+
"auth_time" => time(),
90+
"idp_access_token" => '123',
91+
"idp" => "idp",
92+
"tfp" => "tfp",
93+
"at_hash" => "rfz4eAdZL7I_G8tQBvHI5Q"
94+
);
95+
96+
$encryptedAccessToken = $this->createJWT($accessToken);
97+
$encryptedIdToken = $this->createJWT($idToken);
98+
99+
100+
return array(
101+
'access_token' => $encryptedAccessToken,
102+
'id_token' => $encryptedIdToken,
103+
'token_type' => 'Bearer',
104+
'not_before' => time(),
105+
'expires_in' => 3600,
106+
'expires_on' => time() + 3600,
107+
'resource' => 'resource',
108+
'refresh_token' => $encryptedIdToken,
109+
'refresh_token_expires_in' => time() + 1209600
110+
);
111+
}
112+
113+
return [];
114+
}
115+
116+
117+
/**
118+
* @param array<string, string> $payload
119+
* @return string
120+
*/
121+
private function createJWT(array $payload): string
122+
{
123+
124+
$private_key = openssl_pkey_new();
125+
$details = openssl_pkey_get_details($private_key);
126+
127+
$publicKeyPEM = $details['key'];
128+
129+
$modulus = $details['rsa']['n'];
130+
$exponent = $details['rsa']['e'];
131+
132+
openssl_pkey_export($private_key, $privateKeyPEM);
133+
134+
$this->publicKey = $publicKeyPEM;
135+
$this->modulus = $this->urlsafeBase64($modulus);
136+
$this->exponent = $this->urlsafeBase64($exponent);
137+
138+
139+
return JWT::encode($payload, $privateKeyPEM, 'RS256', $publicKeyPEM);
140+
}
141+
142+
/**
143+
* @param string $string
144+
* @return string
145+
*/
146+
private function urlsafeBase64(string $string): string
147+
{
148+
return str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($string));
149+
}
150+
151+
/**
152+
* @return string
153+
*/
154+
public function getPublicKey(): string
155+
{
156+
return $this->publicKey;
157+
}
158+
159+
/**
160+
* @return string
161+
*/
162+
public function getModulus(): string
163+
{
164+
return $this->modulus;
165+
}
166+
167+
/**
168+
* @return string
169+
*/
170+
public function getExponent(): string
171+
{
172+
return $this->exponent;
173+
}
174+
175+
}

tests/Fakers/KeysFaker.php

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?php
2+
3+
namespace TheNetworg\OAuth2\Client\Tests\Fakers;
4+
5+
6+
class KeysFaker
7+
{
8+
/**
9+
* @param $publicKey
10+
* @param $modulus
11+
* @param $exponent
12+
* @return array<string, mixed>
13+
*/
14+
public function getKeysResponse($publicKey, $modulus, $exponent): array
15+
{
16+
17+
return array(
18+
'keys' => [
19+
array(
20+
'kid' => $publicKey,
21+
'nbf' => time(),
22+
'use' => 'sig',
23+
'kty' => 'RSA',
24+
'e' => $exponent,
25+
'n' => $modulus
26+
)
27+
]
28+
);
29+
}
30+
31+
32+
}

tests/Helper/AzureHelper.php

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
<?php
2+
3+
namespace TheNetworg\OAuth2\Client\Tests\Helper;
4+
5+
use GuzzleHttp\Client;
6+
use GuzzleHttp\Handler\MockHandler;
7+
use GuzzleHttp\Psr7\Response;
8+
use League\OAuth2\Client\Provider\Exception\IdentityProviderException;
9+
use League\OAuth2\Client\Token\AccessTokenInterface;
10+
use TheNetworg\OAuth2\Client\Provider\Azure;
11+
use TheNetworg\OAuth2\Client\Tests\Fakers\KeysFaker;
12+
use TheNetworg\OAuth2\Client\Tests\Fakers\B2cTokenFaker;
13+
14+
class AzureHelper
15+
{
16+
17+
18+
/** @var B2cTokenFaker */
19+
private $tokenFaker;
20+
21+
/** @var KeysFaker */
22+
private $keysFaker;
23+
24+
25+
/** @var string */
26+
private $defaultClientId;
27+
28+
29+
/** @var string */
30+
private $defaultIss;
31+
32+
/** @var string */
33+
private $defaultAuthEndpoint;
34+
35+
/** @var string */
36+
private $defaultLogoutUrl;
37+
38+
public function __construct(B2cTokenFaker $tokenFaker, KeysFaker $keysFaker)
39+
{
40+
$this->tokenFaker = $tokenFaker;
41+
$this->keysFaker = $keysFaker;
42+
43+
$this->defaultClientId = 'client_id';
44+
$this->defaultIss = 'iss';
45+
$this->defaultAuthEndpoint = 'auth_endpoint';
46+
$this->defaultLogoutUrl = 'logout_url';
47+
}
48+
49+
/**
50+
* @return void
51+
*/
52+
private function setDefaultFakeData(): void
53+
{
54+
$this->tokenFaker->setFakeData('b2cId', true, $this->defaultClientId, $this->defaultIss);
55+
}
56+
57+
/**
58+
* @return string[]
59+
*/
60+
public function getConfig(): array
61+
{
62+
return array(
63+
'issuer' => $this->defaultIss,
64+
'authorization_endpoint' => $this->defaultAuthEndpoint,
65+
'end_session_endpoint' => $this->defaultLogoutUrl,
66+
'token_endpoint' => '',
67+
'jwks_uri' => ''
68+
);
69+
}
70+
71+
/**
72+
* @param bool $defaultFakeData
73+
* @param bool $valid_token
74+
* @param bool $valid_key
75+
* @return MockHandler
76+
*/
77+
private function getHandler(bool $defaultFakeData, bool $valid_token, bool $valid_key): MockHandler
78+
{
79+
if ($defaultFakeData) {
80+
$this->setDefaultFakeData();
81+
}
82+
$config = $this->getConfig();
83+
$tokenResponse = $valid_token ? $this->tokenFaker->getB2cTokenResponse() : [''];
84+
$keyResponse = $valid_key ? $this->keysFaker->getKeysResponse($this->tokenFaker->getPublicKey(), $this->tokenFaker->getModulus(), $this->tokenFaker->getExponent()) : ['keys' => [['']]];
85+
86+
return new MockHandler([
87+
new Response(200, ['content-type' => 'application/json'], json_encode($config)),
88+
new Response(200, ['content-type' => 'application/json'], json_encode($tokenResponse)),
89+
new Response(200, ['content-type' => 'application/json'], json_encode($keyResponse)),
90+
new Response(200, ['content-type' => 'application/json'], json_encode($config)),
91+
new Response(200, ['content-type' => 'application/json'], json_encode($keyResponse)),
92+
]);
93+
}
94+
95+
96+
/**
97+
* @param bool $defaultFakeData
98+
* @param bool $valid_token
99+
* @param bool $valid_key
100+
* @return Client
101+
*/
102+
public function getMockHttpClient(bool $defaultFakeData = true, bool $valid_token = true, bool $valid_key = true): Client
103+
{
104+
return new Client(['handler' => $this->getHandler($defaultFakeData, $valid_token, $valid_key)]);
105+
}
106+
107+
/**
108+
* @param Azure $azure
109+
* @return AccessTokenInterface
110+
* @throws IdentityProviderException
111+
*/
112+
public function getAccessToken(Azure $azure): AccessTokenInterface
113+
{
114+
return $azure->getAccessToken('authorization_code', [
115+
'scope' => $azure->scope,
116+
'code' => 'authorization_code',
117+
]);
118+
}
119+
120+
121+
/**
122+
* @return string
123+
*/
124+
public function getDefaultClientId(): string
125+
{
126+
return $this->defaultClientId;
127+
}
128+
129+
/**
130+
* @return string
131+
*/
132+
public function getDefaultIss(): string
133+
{
134+
return $this->defaultIss;
135+
}
136+
137+
/**
138+
* @return string
139+
*/
140+
public function getDefaultAuthEndpoint(): string
141+
{
142+
return $this->defaultAuthEndpoint;
143+
}
144+
145+
/**
146+
* @return string
147+
*/
148+
public function getDefaultLogoutUrl(): string
149+
{
150+
return $this->defaultLogoutUrl;
151+
}
152+
153+
/**
154+
* @return B2cTokenFaker
155+
*/
156+
public function getTokenFaker(): B2cTokenFaker
157+
{
158+
return $this->tokenFaker;
159+
}
160+
}

0 commit comments

Comments
 (0)