mirror of
https://github.com/mailcow/mailcow-dockerized.git
synced 2026-10-29 12:43:25 +00:00
[Web] Update composer libraries
This commit is contained in:
1297 files changed
+71585
-24460
No files matched your search
@@ -7,8 +7,6 @@ We accept contributions via Pull Requests on [Github](https://github.com/stevenm
|
||||
|
||||
## Pull Requests
|
||||
|
||||
- **[PSR-2 Coding Standard](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md)** - The easiest way to apply the conventions is to install [PHP Code Sniffer](http://pear.php.net/package/PHP_CodeSniffer).
|
||||
|
||||
- **Add tests!** - Your patch won't be accepted if it doesn't have tests.
|
||||
|
||||
- **Document any change in behaviour** - Make sure the README and any other relevant documentation are kept up-to-date.
|
||||
@@ -29,14 +27,7 @@ We accept contributions via Pull Requests on [Github](https://github.com/stevenm
|
||||
## Running Tests
|
||||
|
||||
``` bash
|
||||
$ ./vendor/bin/phpunit
|
||||
```
|
||||
|
||||
|
||||
## Running PHP Code Sniffer
|
||||
|
||||
``` bash
|
||||
$ ./vendor/bin/phpcs src --standard=psr2 -sp
|
||||
$ composer test
|
||||
```
|
||||
|
||||
**Happy coding**!
|
||||
@@ -1,7 +1,6 @@
|
||||
# Keycloak Provider for OAuth 2.0 Client
|
||||
[](https://github.com/stevenmaguire/oauth2-keycloak/releases)
|
||||
[](LICENSE.md)
|
||||
[](https://travis-ci.org/stevenmaguire/oauth2-keycloak)
|
||||
[](LICENSE)
|
||||
[](https://scrutinizer-ci.com/g/stevenmaguire/oauth2-keycloak/code-structure)
|
||||
[](https://scrutinizer-ci.com/g/stevenmaguire/oauth2-keycloak)
|
||||
[](https://packagist.org/packages/stevenmaguire/oauth2-keycloak)
|
||||
@@ -20,9 +19,11 @@ composer require stevenmaguire/oauth2-keycloak
|
||||
|
||||
Usage is the same as The League's OAuth client, using `\Stevenmaguire\OAuth2\Client\Provider\Keycloak` as the provider.
|
||||
|
||||
Use `authServerUrl` to specify the Keycloak server URL. You can lookup the correct value from the Keycloak client installer JSON under `auth-server-url`, eg. `http://localhost:8080/auth`.
|
||||
Use `authServerUrl` to specify the Keycloak server URL. This depends on your Keycloak version:
|
||||
- legacy releases often use `http://localhost:8080/auth`
|
||||
- modern releases (Keycloak 17+) typically use `http://localhost:8080`
|
||||
|
||||
Use `realm` to specify the Keycloak realm name. You can lookup the correct value from the Keycloak client installer JSON under `resource`, eg. `master`.
|
||||
Use `realm` to specify the Keycloak realm name. In client configuration exports this is represented as `realm`, e.g. `master`.
|
||||
|
||||
### Authorization Code Flow
|
||||
|
||||
@@ -34,8 +35,8 @@ $provider = new Stevenmaguire\OAuth2\Client\Provider\Keycloak([
|
||||
'clientSecret' => '{keycloak-client-secret}',
|
||||
'redirectUri' => 'https://example.com/callback-url',
|
||||
'encryptionAlgorithm' => 'RS256', // optional
|
||||
'encryptionKeyPath' => '../key.pem' // optional
|
||||
'encryptionKey' => 'contents_of_key_or_certificate' // optional
|
||||
'encryptionKeyPath' => '../key.pem', // optional
|
||||
'encryptionKey' => 'contents_of_key_or_certificate', // optional
|
||||
'version' => '20.0.1', // optional
|
||||
]);
|
||||
|
||||
@@ -55,7 +56,7 @@ if (!isset($_GET['code'])) {
|
||||
|
||||
} else {
|
||||
|
||||
// Try to get an access token (using the authorization coe grant)
|
||||
// Try to get an access token (using the authorization code grant)
|
||||
try {
|
||||
$token = $provider->getAccessToken('authorization_code', [
|
||||
'code' => $_GET['code']
|
||||
@@ -155,8 +156,8 @@ $provider->setEncryptionKeyPath($keyPath);
|
||||
|
||||
## Testing
|
||||
|
||||
``` bash
|
||||
$ ./vendor/bin/phpunit
|
||||
```bash
|
||||
$ composer test
|
||||
```
|
||||
|
||||
## Contributing
|
||||
|
||||
@@ -18,14 +18,15 @@
|
||||
"keycloak"
|
||||
],
|
||||
"require": {
|
||||
"php": "~7.2 || ~8.0",
|
||||
"league/oauth2-client": "^2.0",
|
||||
"firebase/php-jwt": "^4.0 || ^5.0 || ^6.0"
|
||||
"php": "^8.0",
|
||||
"league/oauth2-client": "^2.8",
|
||||
"firebase/php-jwt": "^7.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "~9.6.4",
|
||||
"mockery/mockery": "~1.5.0",
|
||||
"squizlabs/php_codesniffer": "~3.7.0"
|
||||
"mockery/mockery": "^1.6",
|
||||
"squizlabs/php_codesniffer": "~3.7.0",
|
||||
"phpstan/phpstan": "^1.12"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
@@ -37,6 +38,11 @@
|
||||
"Stevenmaguire\\OAuth2\\Client\\Test\\": "test/src/"
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"platform": {
|
||||
"php": "8.0.0"
|
||||
}
|
||||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.0.x-dev"
|
||||
@@ -48,4 +54,4 @@
|
||||
"phpunit --colors=always"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
parameters:
|
||||
level: max
|
||||
paths:
|
||||
- src
|
||||
+2
-2
@@ -11,9 +11,9 @@ class EncryptionConfigurationException extends Exception
|
||||
*
|
||||
* @return \Stevenmaguire\OAuth2\Client\Provider\Exception\EncryptionConfigurationException
|
||||
*/
|
||||
public static function undeterminedEncryption()
|
||||
public static function undeterminedEncryption(): self
|
||||
{
|
||||
return new static(
|
||||
return new self(
|
||||
'The given response may be encrypted and sufficient '.
|
||||
'encryption configuration has not been provided.',
|
||||
400
|
||||
|
||||
+80
-52
@@ -4,6 +4,7 @@ namespace Stevenmaguire\OAuth2\Client\Provider;
|
||||
|
||||
use Exception;
|
||||
use Firebase\JWT\JWT;
|
||||
use Firebase\JWT\Key;
|
||||
use League\OAuth2\Client\Provider\AbstractProvider;
|
||||
use League\OAuth2\Client\Provider\Exception\IdentityProviderException;
|
||||
use League\OAuth2\Client\Token\AccessToken;
|
||||
@@ -37,43 +38,43 @@ class Keycloak extends AbstractProvider
|
||||
* https://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-40
|
||||
* for a list of spec-compliant algorithms.
|
||||
*
|
||||
* @var string
|
||||
* @var string|null
|
||||
*/
|
||||
public $encryptionAlgorithm = null;
|
||||
|
||||
/**
|
||||
* Encryption key.
|
||||
*
|
||||
* @var string
|
||||
* @var string|null
|
||||
*/
|
||||
public $encryptionKey = null;
|
||||
|
||||
/**
|
||||
* Keycloak version.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
* Keycloak version.
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
public $version = null;
|
||||
|
||||
/**
|
||||
* Constructs an OAuth 2.0 service provider.
|
||||
*
|
||||
* @param array $options An array of options to set on this provider.
|
||||
* @param array<string, mixed> $options An array of options to set on this provider.
|
||||
* Options include `clientId`, `clientSecret`, `redirectUri`, and `state`.
|
||||
* Individual providers may introduce more options, as needed.
|
||||
* @param array $collaborators An array of collaborators that may be used to
|
||||
* @param array<string, mixed> $collaborators An array of collaborators that may be used to
|
||||
* override this provider's default behavior. Collaborators include
|
||||
* `grantFactory`, `requestFactory`, `httpClient`, and `randomFactory`.
|
||||
* Individual providers may introduce more collaborators, as needed.
|
||||
*/
|
||||
public function __construct(array $options = [], array $collaborators = [])
|
||||
{
|
||||
if (isset($options['encryptionKeyPath'])) {
|
||||
if (isset($options['encryptionKeyPath']) && is_string($options['encryptionKeyPath'])) {
|
||||
$this->setEncryptionKeyPath($options['encryptionKeyPath']);
|
||||
unset($options['encryptionKeyPath']);
|
||||
}
|
||||
|
||||
if (isset($options['version'])) {
|
||||
if (isset($options['version']) && is_string($options['version'])) {
|
||||
$this->setVersion($options['version']);
|
||||
}
|
||||
|
||||
@@ -83,27 +84,39 @@ class Keycloak extends AbstractProvider
|
||||
/**
|
||||
* Attempts to decrypt the given response.
|
||||
*
|
||||
* @param string|array|null $response
|
||||
* @param string|array<string, mixed>|null $response
|
||||
*
|
||||
* @return string|array|null
|
||||
* @return string|array<string, mixed>|null
|
||||
* @throws EncryptionConfigurationException
|
||||
*/
|
||||
public function decryptResponse($response)
|
||||
public function decryptResponse($response): array|string|null
|
||||
{
|
||||
if (!is_string($response)) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
if ($this->usesEncryption()) {
|
||||
return json_decode(
|
||||
json_encode(
|
||||
JWT::decode(
|
||||
$response,
|
||||
$this->encryptionKey,
|
||||
array($this->encryptionAlgorithm)
|
||||
)
|
||||
),
|
||||
true
|
||||
if (!is_string($this->encryptionKey) || !is_string($this->encryptionAlgorithm)) {
|
||||
throw EncryptionConfigurationException::undeterminedEncryption();
|
||||
}
|
||||
|
||||
$tokenData = JWT::decode(
|
||||
$response,
|
||||
new Key(
|
||||
$this->encryptionKey,
|
||||
$this->encryptionAlgorithm
|
||||
)
|
||||
);
|
||||
$tokenJson = json_encode($tokenData);
|
||||
if (!is_string($tokenJson)) {
|
||||
throw EncryptionConfigurationException::undeterminedEncryption();
|
||||
}
|
||||
$decodedToken = json_decode($tokenJson, true);
|
||||
if (!is_array($decodedToken)) {
|
||||
throw EncryptionConfigurationException::undeterminedEncryption();
|
||||
}
|
||||
|
||||
return $decodedToken;
|
||||
}
|
||||
|
||||
throw EncryptionConfigurationException::undeterminedEncryption();
|
||||
@@ -114,7 +127,7 @@ class Keycloak extends AbstractProvider
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getBaseAuthorizationUrl()
|
||||
public function getBaseAuthorizationUrl(): string
|
||||
{
|
||||
return $this->getBaseUrlWithRealm().'/protocol/openid-connect/auth';
|
||||
}
|
||||
@@ -122,11 +135,11 @@ class Keycloak extends AbstractProvider
|
||||
/**
|
||||
* Get access token url to retrieve token
|
||||
*
|
||||
* @param array $params
|
||||
* @param array<string, mixed> $params
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getBaseAccessTokenUrl(array $params)
|
||||
public function getBaseAccessTokenUrl(array $params): string
|
||||
{
|
||||
return $this->getBaseUrlWithRealm().'/protocol/openid-connect/token';
|
||||
}
|
||||
@@ -138,7 +151,7 @@ class Keycloak extends AbstractProvider
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getResourceOwnerDetailsUrl(AccessToken $token)
|
||||
public function getResourceOwnerDetailsUrl(AccessToken $token): string
|
||||
{
|
||||
return $this->getBaseUrlWithRealm().'/protocol/openid-connect/userinfo';
|
||||
}
|
||||
@@ -146,10 +159,10 @@ class Keycloak extends AbstractProvider
|
||||
/**
|
||||
* Builds the logout URL.
|
||||
*
|
||||
* @param array $options
|
||||
* @param array<string, mixed> $options
|
||||
* @return string Authorization URL
|
||||
*/
|
||||
public function getLogoutUrl(array $options = [])
|
||||
public function getLogoutUrl(array $options = []): string
|
||||
{
|
||||
$base = $this->getBaseLogoutUrl();
|
||||
$params = $this->getAuthorizationParameters($options);
|
||||
@@ -158,10 +171,13 @@ class Keycloak extends AbstractProvider
|
||||
// As of this version the parameter is called post_logout_redirect_uri. In addition to this
|
||||
// a parameter id_token_hint has to be provided.
|
||||
if ($this->validateGteVersion('18.0.0')) {
|
||||
if (isset($options['access_token']) === true) {
|
||||
if (isset($options['access_token']) === true && $options['access_token'] instanceof AccessToken) {
|
||||
$accessToken = $options['access_token'];
|
||||
$values = $accessToken->getValues();
|
||||
|
||||
$params['id_token_hint'] = $accessToken->getValues()['id_token'];
|
||||
if (isset($values['id_token']) && is_string($values['id_token'])) {
|
||||
$params['id_token_hint'] = $values['id_token'];
|
||||
}
|
||||
$params['post_logout_redirect_uri'] = $params['redirect_uri'];
|
||||
}
|
||||
|
||||
@@ -177,7 +193,7 @@ class Keycloak extends AbstractProvider
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function getBaseLogoutUrl()
|
||||
private function getBaseLogoutUrl(): string
|
||||
{
|
||||
return $this->getBaseUrlWithRealm() . '/protocol/openid-connect/logout';
|
||||
}
|
||||
@@ -187,7 +203,7 @@ class Keycloak extends AbstractProvider
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getBaseUrlWithRealm()
|
||||
protected function getBaseUrlWithRealm(): string
|
||||
{
|
||||
return $this->authServerUrl.'/realms/'.$this->realm;
|
||||
}
|
||||
@@ -200,7 +216,7 @@ class Keycloak extends AbstractProvider
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
protected function getDefaultScopes()
|
||||
protected function getDefaultScopes(): array
|
||||
{
|
||||
$scopes = [
|
||||
'profile',
|
||||
@@ -218,7 +234,7 @@ class Keycloak extends AbstractProvider
|
||||
*
|
||||
* @return string Scope separator, defaults to ','
|
||||
*/
|
||||
protected function getScopeSeparator()
|
||||
protected function getScopeSeparator(): string
|
||||
{
|
||||
return ' ';
|
||||
}
|
||||
@@ -229,28 +245,32 @@ class Keycloak extends AbstractProvider
|
||||
*
|
||||
* @throws IdentityProviderException
|
||||
* @param ResponseInterface $response
|
||||
* @param string $data Parsed response data
|
||||
* @param mixed $data Parsed response data
|
||||
* @return void
|
||||
*/
|
||||
protected function checkResponse(ResponseInterface $response, $data)
|
||||
protected function checkResponse(ResponseInterface $response, $data): void
|
||||
{
|
||||
if (!is_array($data)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!empty($data['error'])) {
|
||||
$error = $data['error'];
|
||||
if (isset($data['error_description'])) {
|
||||
$error.=': '.$data['error_description'];
|
||||
$error .= ': '.$data['error_description'];
|
||||
}
|
||||
throw new IdentityProviderException($error, 0, $data);
|
||||
throw new IdentityProviderException($error, $response->getStatusCode(), $data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a user object from a successful user details request.
|
||||
*
|
||||
* @param array $response
|
||||
* @param array<string, mixed> $response
|
||||
* @param AccessToken $token
|
||||
* @return KeycloakResourceOwner
|
||||
*/
|
||||
protected function createResourceOwner(array $response, AccessToken $token)
|
||||
protected function createResourceOwner(array $response, AccessToken $token): KeycloakResourceOwner
|
||||
{
|
||||
return new KeycloakResourceOwner($response);
|
||||
}
|
||||
@@ -262,9 +282,12 @@ class Keycloak extends AbstractProvider
|
||||
* @return KeycloakResourceOwner
|
||||
* @throws EncryptionConfigurationException
|
||||
*/
|
||||
public function getResourceOwner(AccessToken $token)
|
||||
public function getResourceOwner(AccessToken $token): KeycloakResourceOwner
|
||||
{
|
||||
$response = $this->fetchResourceOwnerDetails($token);
|
||||
if (!is_array($response)) {
|
||||
throw new UnexpectedValueException('Expected resource owner details to be an array.');
|
||||
}
|
||||
|
||||
// We are always getting an array. We have to check if it is
|
||||
// the array we created
|
||||
@@ -273,6 +296,9 @@ class Keycloak extends AbstractProvider
|
||||
}
|
||||
|
||||
$response = $this->decryptResponse($response);
|
||||
if (!is_array($response)) {
|
||||
throw new UnexpectedValueException('Expected decrypted resource owner details to be an array.');
|
||||
}
|
||||
|
||||
return $this->createResourceOwner($response, $token);
|
||||
}
|
||||
@@ -284,7 +310,7 @@ class Keycloak extends AbstractProvider
|
||||
*
|
||||
* @return Keycloak
|
||||
*/
|
||||
public function setEncryptionAlgorithm($encryptionAlgorithm)
|
||||
public function setEncryptionAlgorithm(string $encryptionAlgorithm): self
|
||||
{
|
||||
$this->encryptionAlgorithm = $encryptionAlgorithm;
|
||||
|
||||
@@ -298,7 +324,7 @@ class Keycloak extends AbstractProvider
|
||||
*
|
||||
* @return Keycloak
|
||||
*/
|
||||
public function setEncryptionKey($encryptionKey)
|
||||
public function setEncryptionKey(string $encryptionKey): self
|
||||
{
|
||||
$this->encryptionKey = $encryptionKey;
|
||||
|
||||
@@ -313,10 +339,13 @@ class Keycloak extends AbstractProvider
|
||||
*
|
||||
* @return Keycloak
|
||||
*/
|
||||
public function setEncryptionKeyPath($encryptionKeyPath)
|
||||
public function setEncryptionKeyPath(string $encryptionKeyPath): self
|
||||
{
|
||||
try {
|
||||
$this->encryptionKey = file_get_contents($encryptionKeyPath);
|
||||
$encryptionKey = file_get_contents($encryptionKeyPath);
|
||||
if (is_string($encryptionKey)) {
|
||||
$this->encryptionKey = $encryptionKey;
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
// Not sure how to handle this yet.
|
||||
}
|
||||
@@ -331,7 +360,7 @@ class Keycloak extends AbstractProvider
|
||||
*
|
||||
* @return Keycloak
|
||||
*/
|
||||
public function setVersion($version)
|
||||
public function setVersion(string $version): self
|
||||
{
|
||||
$this->version = $version;
|
||||
|
||||
@@ -343,7 +372,7 @@ class Keycloak extends AbstractProvider
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function usesEncryption()
|
||||
public function usesEncryption(): bool
|
||||
{
|
||||
return (bool) $this->encryptionAlgorithm && $this->encryptionKey;
|
||||
}
|
||||
@@ -353,16 +382,15 @@ class Keycloak extends AbstractProvider
|
||||
*
|
||||
* @throws UnexpectedValueException
|
||||
* @param ResponseInterface $response
|
||||
* @return array
|
||||
* @return string|array<string, mixed>
|
||||
*/
|
||||
protected function parseResponse(ResponseInterface $response)
|
||||
protected function parseResponse(ResponseInterface $response): string|array
|
||||
{
|
||||
// We have a problem with keycloak when the userinfo responses
|
||||
// with a jwt token
|
||||
// Because it just return a jwt as string with the header
|
||||
// application/jwt
|
||||
// This can't be parsed to a array
|
||||
// Dont know why this function only allow an array as return value...
|
||||
$content = (string) $response->getBody();
|
||||
$type = $this->getContentType($response);
|
||||
|
||||
@@ -380,8 +408,8 @@ class Keycloak extends AbstractProvider
|
||||
* @param string $version
|
||||
* @return bool
|
||||
*/
|
||||
private function validateGteVersion($version)
|
||||
private function validateGteVersion(string $version): bool
|
||||
{
|
||||
return (isset($this->version) && version_compare($this->version, $version, '>='));
|
||||
return is_string($this->version) && version_compare($this->version, $version, '>=');
|
||||
}
|
||||
}
|
||||
Vendored
+67
-10
@@ -9,14 +9,14 @@ class KeycloakResourceOwner implements ResourceOwnerInterface
|
||||
/**
|
||||
* Raw response
|
||||
*
|
||||
* @var array
|
||||
* @var array<string, mixed>
|
||||
*/
|
||||
protected $response;
|
||||
|
||||
/**
|
||||
* Creates new resource owner.
|
||||
*
|
||||
* @param array $response
|
||||
* @param array<string, mixed> $response
|
||||
*/
|
||||
public function __construct(array $response = array())
|
||||
{
|
||||
@@ -28,9 +28,16 @@ class KeycloakResourceOwner implements ResourceOwnerInterface
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getId()
|
||||
public function getId(): ?string
|
||||
{
|
||||
return \array_key_exists('sub', $this->response) ? $this->response['sub'] : null;
|
||||
if (!\array_key_exists('sub', $this->response)) {
|
||||
return null;
|
||||
}
|
||||
if (!is_scalar($this->response['sub'])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (string) $this->response['sub'];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -38,9 +45,13 @@ class KeycloakResourceOwner implements ResourceOwnerInterface
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getEmail()
|
||||
public function getEmail(): ?string
|
||||
{
|
||||
return \array_key_exists('email', $this->response) ? $this->response['email'] : null;
|
||||
if (!\array_key_exists('email', $this->response) || !is_string($this->response['email'])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->response['email'];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -48,17 +59,63 @@ class KeycloakResourceOwner implements ResourceOwnerInterface
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getName()
|
||||
public function getName(): ?string
|
||||
{
|
||||
return \array_key_exists('name', $this->response) ? $this->response['name'] : null;
|
||||
if (!\array_key_exists('name', $this->response) || !is_string($this->response['name'])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->response['name'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get resource owner username
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getUsername(): ?string
|
||||
{
|
||||
if (!\array_key_exists('preferred_username', $this->response) || !is_string($this->response['preferred_username'])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->response['preferred_username'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get resource owner first name
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getFirstName(): ?string
|
||||
{
|
||||
if (!\array_key_exists('given_name', $this->response) || !is_string($this->response['given_name'])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->response['given_name'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get resource owner last name
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getLastName(): ?string
|
||||
{
|
||||
if (!\array_key_exists('family_name', $this->response) || !is_string($this->response['family_name'])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->response['family_name'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all of the owner details available as an array.
|
||||
*
|
||||
* @return array
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function toArray()
|
||||
public function toArray(): array
|
||||
{
|
||||
return $this->response;
|
||||
}
|
||||
|
||||
+307
-84
@@ -7,7 +7,7 @@ namespace
|
||||
|
||||
namespace Stevenmaguire\OAuth2\Client\Provider
|
||||
{
|
||||
function file_get_contents()
|
||||
function file_get_contents(...$args): string|false
|
||||
{
|
||||
global $mockFileGetContents;
|
||||
if (isset($mockFileGetContents) && ! is_null($mockFileGetContents)) {
|
||||
@@ -16,17 +16,22 @@ namespace Stevenmaguire\OAuth2\Client\Provider
|
||||
}
|
||||
return $mockFileGetContents;
|
||||
} else {
|
||||
return call_user_func_array('\file_get_contents', func_get_args());
|
||||
return \file_get_contents(...$args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace Stevenmaguire\OAuth2\Client\Test\Provider
|
||||
{
|
||||
use DateInterval;
|
||||
use DateTimeImmutable;
|
||||
use Firebase\JWT\JWT;
|
||||
use League\OAuth2\Client\Provider\Exception\IdentityProviderException;
|
||||
use League\OAuth2\Client\Token\AccessToken;
|
||||
use League\OAuth2\Client\Tool\QueryBuilderTrait;
|
||||
use Mockery as m;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Psr\Http\Message\StreamInterface;
|
||||
use Stevenmaguire\OAuth2\Client\Provider\Exception\EncryptionConfigurationException;
|
||||
use Stevenmaguire\OAuth2\Client\Provider\Keycloak;
|
||||
|
||||
@@ -34,7 +39,59 @@ namespace Stevenmaguire\OAuth2\Client\Test\Provider
|
||||
{
|
||||
use QueryBuilderTrait;
|
||||
|
||||
protected $provider;
|
||||
public const ENCRYPTION_KEY = <<<EOD
|
||||
-----BEGIN PUBLIC KEY-----
|
||||
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC8kGa1pSjbSYZVebtTRBLxBz5H
|
||||
4i2p/llLCrEeQhta5kaQu/RnvuER4W8oDH3+3iuIYW4VQAzyqFpwuzjkDI+17t5t
|
||||
0tyazyZ8JXw+KgXTxldMPEL95+qVhgXvwtihXC1c5oGbRlEDvDF6Sa53rcFVsYJ4
|
||||
ehde/zUxo6UvS7UrBQIDAQAB
|
||||
-----END PUBLIC KEY-----
|
||||
EOD;
|
||||
|
||||
public const ENCRYPTION_ALGORITHM = 'HS256';
|
||||
|
||||
private string $jwtTemplate = <<<EOF
|
||||
{
|
||||
"exp": "%s",
|
||||
"iat": "%s",
|
||||
"jti": "e11a85c8-aa91-4f75-9088-57db4586f8b9",
|
||||
"iss": "https://example.org/auth/realms/test-realm",
|
||||
"aud": "account",
|
||||
"nbf": "%s",
|
||||
"sub": "4332085e-b944-4acc-9eb1-27d8f5405f3e",
|
||||
"typ": "Bearer",
|
||||
"azp": "test-app",
|
||||
"session_state": "c90c8e0d-aabb-4c71-b8a8-e88792cacd96",
|
||||
"acr": "1",
|
||||
"realm_access": {
|
||||
"roles": [
|
||||
"default-roles-test-realm",
|
||||
"offline_access",
|
||||
"uma_authorization"
|
||||
]
|
||||
},
|
||||
"resource_access": {
|
||||
"account": {
|
||||
"roles": [
|
||||
"manage-account",
|
||||
"manage-account-links",
|
||||
"view-profile"
|
||||
]
|
||||
}
|
||||
},
|
||||
"scope": "openid email profile",
|
||||
"sid": "c90c8e0d-aabb-4c71-b8a8-e88792cacd96",
|
||||
"address": {},
|
||||
"email_verified": true,
|
||||
"name": "Test User",
|
||||
"preferred_username": "test-user",
|
||||
"given_name": "Test",
|
||||
"family_name": "User",
|
||||
"email": "test-user@example.org"
|
||||
}
|
||||
EOF;
|
||||
|
||||
protected Keycloak $provider;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
@@ -53,7 +110,7 @@ namespace Stevenmaguire\OAuth2\Client\Test\Provider
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
public function testAuthorizationUrl()
|
||||
public function testAuthorizationUrl(): void
|
||||
{
|
||||
$url = $this->provider->getAuthorizationUrl();
|
||||
$uri = parse_url($url);
|
||||
@@ -68,7 +125,7 @@ namespace Stevenmaguire\OAuth2\Client\Test\Provider
|
||||
$this->assertNotNull($this->provider->getState());
|
||||
}
|
||||
|
||||
public function testEncryptionAlgorithm()
|
||||
public function testEncryptionAlgorithm(): void
|
||||
{
|
||||
$algorithm = uniqid();
|
||||
$provider = new Keycloak([
|
||||
@@ -83,7 +140,7 @@ namespace Stevenmaguire\OAuth2\Client\Test\Provider
|
||||
$this->assertEquals($algorithm, $provider->encryptionAlgorithm);
|
||||
}
|
||||
|
||||
public function testEncryptionKey()
|
||||
public function testEncryptionKey(): void
|
||||
{
|
||||
$key = uniqid();
|
||||
$provider = new Keycloak([
|
||||
@@ -98,7 +155,7 @@ namespace Stevenmaguire\OAuth2\Client\Test\Provider
|
||||
$this->assertEquals($key, $provider->encryptionKey);
|
||||
}
|
||||
|
||||
public function testEncryptionKeyPath()
|
||||
public function testEncryptionKeyPath(): void
|
||||
{
|
||||
global $mockFileGetContents;
|
||||
$path = uniqid();
|
||||
@@ -120,7 +177,7 @@ namespace Stevenmaguire\OAuth2\Client\Test\Provider
|
||||
$this->assertEquals($key, $provider->encryptionKey);
|
||||
}
|
||||
|
||||
public function testEncryptionKeyPathFails()
|
||||
public function testEncryptionKeyPathFails(): void
|
||||
{
|
||||
$this->markTestIncomplete('Need to assess the test to see what is required to be checked.');
|
||||
|
||||
@@ -136,7 +193,7 @@ namespace Stevenmaguire\OAuth2\Client\Test\Provider
|
||||
$provider->setEncryptionKeyPath($path);
|
||||
}
|
||||
|
||||
public function testScopes()
|
||||
public function testScopes(): void
|
||||
{
|
||||
$scopeSeparator = ' ';
|
||||
$options = ['scope' => [uniqid(), uniqid()]];
|
||||
@@ -146,7 +203,7 @@ namespace Stevenmaguire\OAuth2\Client\Test\Provider
|
||||
$this->assertStringContainsString($encodedScope, $url);
|
||||
}
|
||||
|
||||
public function testGetAuthorizationUrl()
|
||||
public function testGetAuthorizationUrl(): void
|
||||
{
|
||||
$url = $this->provider->getAuthorizationUrl();
|
||||
$uri = parse_url($url);
|
||||
@@ -154,7 +211,7 @@ namespace Stevenmaguire\OAuth2\Client\Test\Provider
|
||||
$this->assertEquals('/auth/realms/mock_realm/protocol/openid-connect/auth', $uri['path']);
|
||||
}
|
||||
|
||||
public function testGetLogoutUrl()
|
||||
public function testGetLogoutUrl(): void
|
||||
{
|
||||
$url = $this->provider->getLogoutUrl();
|
||||
$uri = parse_url($url);
|
||||
@@ -162,7 +219,26 @@ namespace Stevenmaguire\OAuth2\Client\Test\Provider
|
||||
$this->assertEquals('/auth/realms/mock_realm/protocol/openid-connect/logout', $uri['path']);
|
||||
}
|
||||
|
||||
public function testGetBaseAccessTokenUrl()
|
||||
public function testGetLogoutUrlWithIdTokenHint(): void
|
||||
{
|
||||
$this->provider->setVersion('18.0.0');
|
||||
|
||||
$options = [
|
||||
'access_token' => new AccessToken(
|
||||
[
|
||||
'id_token' => 'the_id_token',
|
||||
'access_token' => 'the_access_token',
|
||||
]
|
||||
),
|
||||
];
|
||||
$url = $this->provider->getLogoutUrl($options);
|
||||
$uri = parse_url($url);
|
||||
|
||||
$this->assertEquals('/auth/realms/mock_realm/protocol/openid-connect/logout', $uri['path']);
|
||||
$this->assertStringContainsString('id_token_hint=the_id_token', $uri['query']);
|
||||
}
|
||||
|
||||
public function testGetBaseAccessTokenUrl(): void
|
||||
{
|
||||
$params = [];
|
||||
|
||||
@@ -172,12 +248,19 @@ namespace Stevenmaguire\OAuth2\Client\Test\Provider
|
||||
$this->assertEquals('/auth/realms/mock_realm/protocol/openid-connect/token', $uri['path']);
|
||||
}
|
||||
|
||||
public function testGetAccessToken()
|
||||
public function testGetAccessToken(): void
|
||||
{
|
||||
$stream = $this->createMock(StreamInterface::class);
|
||||
$stream
|
||||
->method('__toString')
|
||||
->willReturn('{"access_token":"mock_access_token","scope":"email","token_type":"bearer"}');
|
||||
|
||||
$response = m::mock('Psr\Http\Message\ResponseInterface');
|
||||
$response->shouldReceive('getBody')
|
||||
->andReturn('{"access_token":"mock_access_token", "scope":"email", "token_type":"bearer"}');
|
||||
$response->shouldReceive('getHeader')
|
||||
$response
|
||||
->shouldReceive('getBody')
|
||||
->andReturn($stream);
|
||||
$response
|
||||
->shouldReceive('getHeader')
|
||||
->andReturn(['content-type' => 'json']);
|
||||
|
||||
$client = m::mock('GuzzleHttp\ClientInterface');
|
||||
@@ -186,7 +269,9 @@ namespace Stevenmaguire\OAuth2\Client\Test\Provider
|
||||
->andReturn($response);
|
||||
$this->provider->setHttpClient($client);
|
||||
|
||||
$token = $this->provider->getAccessToken('authorization_code', ['code' => 'mock_authorization_code']);
|
||||
$token = $this
|
||||
->provider
|
||||
->getAccessToken('authorization_code', ['code' => 'mock_authorization_code']);
|
||||
|
||||
$this->assertEquals('mock_access_token', $token->getToken());
|
||||
$this->assertNull($token->getExpires());
|
||||
@@ -194,34 +279,66 @@ namespace Stevenmaguire\OAuth2\Client\Test\Provider
|
||||
$this->assertNull($token->getResourceOwnerId());
|
||||
}
|
||||
|
||||
public function testUserData()
|
||||
public function testUserData(): void
|
||||
{
|
||||
$userId = rand(1000, 9999);
|
||||
$name = uniqid();
|
||||
$nickname = uniqid();
|
||||
$email = uniqid();
|
||||
$username = uniqid();
|
||||
$firstName = uniqid();
|
||||
$lastName = uniqid();
|
||||
|
||||
$getAccessTokenResponseStream = $this->createMock(StreamInterface::class);
|
||||
$getAccessTokenResponseStream
|
||||
->method('__toString')
|
||||
->willReturn(
|
||||
'{"access_token":"mock_access_token","expires":"3600","refresh_token":"mock_refresh_token","otherKey":[1234]}'
|
||||
);
|
||||
|
||||
$postResponse = m::mock('Psr\Http\Message\ResponseInterface');
|
||||
$postResponse->shouldReceive('getBody')
|
||||
->andReturn(
|
||||
'access_token=mock_access_token&expires=3600&refresh_token=mock_refresh_token&otherKey={1234}'
|
||||
$postResponse
|
||||
->shouldReceive('getBody')
|
||||
->andReturn($getAccessTokenResponseStream);
|
||||
$postResponse
|
||||
->shouldReceive('getHeader')
|
||||
->andReturn(['content-type' => 'json']);
|
||||
|
||||
$getResourceOwnerResponseStream = $this->createMock(StreamInterface::class);
|
||||
$getResourceOwnerResponseStream
|
||||
->method('__toString')
|
||||
->willReturn(
|
||||
sprintf(
|
||||
'{"sub": "%s", "name": "%s", "email": "%s", "preferred_username": "%s", "given_name": "%s", "family_name": "%s"}',
|
||||
$userId,
|
||||
$name,
|
||||
$email,
|
||||
$username,
|
||||
$firstName,
|
||||
$lastName
|
||||
)
|
||||
);
|
||||
$postResponse->shouldReceive('getHeader')
|
||||
->andReturn(['content-type' => 'application/x-www-form-urlencoded']);
|
||||
|
||||
$userResponse = m::mock('Psr\Http\Message\ResponseInterface');
|
||||
$userResponse->shouldReceive('getBody')
|
||||
->andReturn('{"sub": '.$userId.', "name": "'.$name.'", "email": "'.$email.'"}');
|
||||
$userResponse->shouldReceive('getHeader')
|
||||
$userResponse
|
||||
->shouldReceive('getBody')
|
||||
->andReturn($getResourceOwnerResponseStream);
|
||||
$userResponse
|
||||
->shouldReceive('getHeader')
|
||||
->andReturn(['content-type' => 'json']);
|
||||
|
||||
$client = m::mock('GuzzleHttp\ClientInterface');
|
||||
$client->shouldReceive('send')
|
||||
->times(2)
|
||||
$client
|
||||
->shouldReceive('send')
|
||||
->andReturn($postResponse, $userResponse);
|
||||
$this->provider->setHttpClient($client);
|
||||
|
||||
$token = $this->provider->getAccessToken('authorization_code', ['code' => 'mock_authorization_code']);
|
||||
$token = $this->provider->getAccessToken(
|
||||
'authorization_code',
|
||||
[
|
||||
'code' => 'mock_authorization_code',
|
||||
'access_token' => 'mock_access_token',
|
||||
]
|
||||
);
|
||||
$user = $this->provider->getResourceOwner($token);
|
||||
|
||||
$this->assertEquals($userId, $user->getId());
|
||||
@@ -230,113 +347,219 @@ namespace Stevenmaguire\OAuth2\Client\Test\Provider
|
||||
$this->assertEquals($name, $user->toArray()['name']);
|
||||
$this->assertEquals($email, $user->getEmail());
|
||||
$this->assertEquals($email, $user->toArray()['email']);
|
||||
$this->assertEquals($username, $user->getUsername());
|
||||
$this->assertEquals($username, $user->toArray()['preferred_username']);
|
||||
$this->assertEquals($firstName, $user->getFirstName());
|
||||
$this->assertEquals($firstName, $user->toArray()['given_name']);
|
||||
$this->assertEquals($lastName, $user->getLastName());
|
||||
$this->assertEquals($lastName, $user->toArray()['family_name']);
|
||||
}
|
||||
|
||||
public function testUserDataWithEncryption()
|
||||
public function testUserDataWithEncryption(): void
|
||||
{
|
||||
$userId = rand(1000, 9999);
|
||||
$name = uniqid();
|
||||
$nickname = uniqid();
|
||||
$email = uniqid();
|
||||
$jwt = uniqid();
|
||||
$algorithm = uniqid();
|
||||
$key = uniqid();
|
||||
$jwt = JWT::encode(
|
||||
json_decode(
|
||||
sprintf(
|
||||
$this->jwtTemplate,
|
||||
(new DateTimeImmutable())->add(new DateInterval('PT1H'))->getTimestamp(),
|
||||
(new DateTimeImmutable())->sub(new DateInterval('P1D'))->getTimestamp(),
|
||||
(new DateTimeImmutable())->sub(new DateInterval('P1D'))->getTimestamp()
|
||||
),
|
||||
true
|
||||
),
|
||||
self::ENCRYPTION_KEY,
|
||||
self::ENCRYPTION_ALGORITHM
|
||||
);
|
||||
|
||||
$postResponse = m::mock('Psr\Http\Message\ResponseInterface');
|
||||
$postResponse->shouldReceive('getBody')
|
||||
->andReturn(
|
||||
'access_token=mock_access_token&expires=3600&refresh_token=mock_refresh_token&otherKey={1234}'
|
||||
$getAccessTokenResponseStream = $this->createMock(StreamInterface::class);
|
||||
$getAccessTokenResponseStream
|
||||
->method('__toString')
|
||||
->willReturn(
|
||||
sprintf(
|
||||
'{"access_token":"%s","expires":"3600","refresh_token":"mock_refresh_token","otherKey":[1234]}',
|
||||
$jwt
|
||||
)
|
||||
);
|
||||
$postResponse->shouldReceive('getHeader')
|
||||
->andReturn(['content-type' => 'application/x-www-form-urlencoded']);
|
||||
$postResponse->shouldReceive('getStatusCode')
|
||||
|
||||
$accessTokenResponse = m::mock('Psr\Http\Message\ResponseInterface');
|
||||
$accessTokenResponse
|
||||
->shouldReceive('getBody')
|
||||
->andReturn($getAccessTokenResponseStream);
|
||||
$accessTokenResponse
|
||||
->shouldReceive('getHeader')
|
||||
->andReturn(['content-type' => 'json']);
|
||||
$accessTokenResponse
|
||||
->shouldReceive('getStatusCode')
|
||||
->andReturn(200);
|
||||
|
||||
$userResponse = m::mock('Psr\Http\Message\ResponseInterface');
|
||||
$userResponse->shouldReceive('getBody')
|
||||
->andReturn($jwt);
|
||||
$userResponse->shouldReceive('getHeader')
|
||||
$getResourceOwnerResponseStream = $this->createMock(StreamInterface::class);
|
||||
$getResourceOwnerResponseStream
|
||||
->method('__toString')
|
||||
->willReturn($jwt);
|
||||
|
||||
$resourceOwnerResponse = m::mock('Psr\Http\Message\ResponseInterface');
|
||||
$resourceOwnerResponse
|
||||
->shouldReceive('getBody')
|
||||
->andReturn($getResourceOwnerResponseStream);
|
||||
$resourceOwnerResponse
|
||||
->shouldReceive('getHeader')
|
||||
->andReturn(['content-type' => 'application/jwt']);
|
||||
$userResponse->shouldReceive('getStatusCode')
|
||||
$resourceOwnerResponse
|
||||
->shouldReceive('getStatusCode')
|
||||
->andReturn(200);
|
||||
|
||||
$decoder = \Mockery::mock('overload:Firebase\JWT\JWT');
|
||||
$decoder->shouldReceive('decode')
|
||||
->with($jwt, $key, [$algorithm])
|
||||
->andReturn([
|
||||
'sub' => $userId,
|
||||
'email' => $email,
|
||||
'name' => $name,
|
||||
]);
|
||||
|
||||
$client = m::mock('GuzzleHttp\ClientInterface');
|
||||
$client->shouldReceive('send')
|
||||
$client
|
||||
->shouldReceive('send')
|
||||
->times(2)
|
||||
->andReturn($postResponse, $userResponse);
|
||||
->andReturn($accessTokenResponse, $resourceOwnerResponse);
|
||||
$this->provider->setHttpClient($client);
|
||||
|
||||
$token = $this->provider->setEncryptionAlgorithm($algorithm)
|
||||
->setEncryptionKey($key)
|
||||
$token = $this
|
||||
->provider
|
||||
->setEncryptionAlgorithm(self::ENCRYPTION_ALGORITHM)
|
||||
->setEncryptionKey(self::ENCRYPTION_KEY)
|
||||
->getAccessToken('authorization_code', ['code' => 'mock_authorization_code']);
|
||||
$user = $this->provider->getResourceOwner($token);
|
||||
|
||||
$email = "test-user@example.org";
|
||||
$name = "Test User";
|
||||
$userId = "4332085e-b944-4acc-9eb1-27d8f5405f3e";
|
||||
$username = "test-user";
|
||||
$firstName = "Test";
|
||||
$lastName = "User";
|
||||
|
||||
$this->assertEquals($userId, $user->getId());
|
||||
$this->assertEquals($userId, $user->toArray()['sub']);
|
||||
$this->assertEquals($name, $user->getName());
|
||||
$this->assertEquals($name, $user->toArray()['name']);
|
||||
$this->assertEquals($email, $user->getEmail());
|
||||
$this->assertEquals($email, $user->toArray()['email']);
|
||||
$this->assertEquals($username, $user->getUsername());
|
||||
$this->assertEquals($username, $user->toArray()['preferred_username']);
|
||||
$this->assertEquals($firstName, $user->getFirstName());
|
||||
$this->assertEquals($firstName, $user->toArray()['given_name']);
|
||||
$this->assertEquals($lastName, $user->getLastName());
|
||||
$this->assertEquals($lastName, $user->toArray()['family_name']);
|
||||
}
|
||||
|
||||
public function testUserDataFailsWhenEncryptionEncounteredAndNotConfigured()
|
||||
public function testUserDataFailsWhenEncryptionEncounteredAndNotConfigured(): void
|
||||
{
|
||||
$this->expectException(EncryptionConfigurationException::class);
|
||||
|
||||
$postResponse = m::mock('Psr\Http\Message\ResponseInterface');
|
||||
$postResponse->shouldReceive('getBody')
|
||||
->andReturn(
|
||||
'access_token=mock_access_token&expires=3600&refresh_token=mock_refresh_token&otherKey={1234}'
|
||||
$accessTokenResponseStream = $this->createMock(StreamInterface::class);
|
||||
$accessTokenResponseStream
|
||||
->method('__toString')
|
||||
->willReturn(
|
||||
'{"access_token":"mock_access_token","expires":"3600","refresh_token":"mock_refresh_token","otherKey":[1234]}'
|
||||
);
|
||||
$postResponse->shouldReceive('getHeader')
|
||||
->andReturn(['content-type' => 'application/x-www-form-urlencoded']);
|
||||
$postResponse->shouldReceive('getStatusCode')
|
||||
|
||||
$getAccessTokenResponse = m::mock('Psr\Http\Message\ResponseInterface');
|
||||
$getAccessTokenResponse
|
||||
->shouldReceive('getBody')
|
||||
->andReturn($accessTokenResponseStream);
|
||||
$getAccessTokenResponse
|
||||
->shouldReceive('getHeader')
|
||||
->andReturn(['content-type' => 'json']);
|
||||
$getAccessTokenResponse
|
||||
->shouldReceive('getStatusCode')
|
||||
->andReturn(200);
|
||||
|
||||
$userResponse = m::mock('Psr\Http\Message\ResponseInterface');
|
||||
$userResponse->shouldReceive('getBody')
|
||||
->andReturn(uniqid());
|
||||
$userResponse->shouldReceive('getHeader')
|
||||
$resourceOwnerResponseStream = $this->createMock(StreamInterface::class);
|
||||
$resourceOwnerResponseStream
|
||||
->method('__toString')
|
||||
->willReturn(uniqid());
|
||||
|
||||
$getResourceOwnerResponse = m::mock('Psr\Http\Message\ResponseInterface');
|
||||
$getResourceOwnerResponse
|
||||
->shouldReceive('getBody')
|
||||
->andReturn($resourceOwnerResponseStream);
|
||||
$getResourceOwnerResponse
|
||||
->shouldReceive('getHeader')
|
||||
->andReturn(['content-type' => 'application/jwt']);
|
||||
$userResponse->shouldReceive('getStatusCode')
|
||||
$getResourceOwnerResponse
|
||||
->shouldReceive('getStatusCode')
|
||||
->andReturn(200);
|
||||
|
||||
$client = m::mock('GuzzleHttp\ClientInterface');
|
||||
$client->shouldReceive('send')
|
||||
$client
|
||||
->shouldReceive('send')
|
||||
->times(2)
|
||||
->andReturn($postResponse, $userResponse);
|
||||
->andReturn($getAccessTokenResponse, $getResourceOwnerResponse);
|
||||
$this->provider->setHttpClient($client);
|
||||
|
||||
$token = $this->provider->getAccessToken('authorization_code', ['code' => 'mock_authorization_code']);
|
||||
$token = $this->provider->getAccessToken(
|
||||
'authorization_code', #
|
||||
['code' => 'mock_authorization_code']
|
||||
);
|
||||
$user = $this->provider->getResourceOwner($token);
|
||||
}
|
||||
|
||||
public function testErrorResponse()
|
||||
public function testErrorResponse(): void
|
||||
{
|
||||
$this->expectException(IdentityProviderException::class);
|
||||
|
||||
$accessTokenResponseStream = $this->createMock(StreamInterface::class);
|
||||
$accessTokenResponseStream
|
||||
->method('__toString')
|
||||
->willReturn(
|
||||
'{"error": "invalid_grant", "error_description": "Code not found"}'
|
||||
);
|
||||
|
||||
$response = m::mock('Psr\Http\Message\ResponseInterface');
|
||||
$response->shouldReceive('getBody')
|
||||
->andReturn('{"error": "invalid_grant", "error_description": "Code not found"}');
|
||||
$response->shouldReceive('getHeader')
|
||||
$response
|
||||
->shouldReceive('getBody')
|
||||
->andReturn($accessTokenResponseStream);
|
||||
$response
|
||||
->shouldReceive('getHeader')
|
||||
->andReturn(['content-type' => 'json']);
|
||||
$response
|
||||
->shouldReceive('getStatusCode')
|
||||
->andReturn(401);
|
||||
|
||||
$client = m::mock('GuzzleHttp\ClientInterface');
|
||||
$client->shouldReceive('send')
|
||||
$client
|
||||
->shouldReceive('send')
|
||||
->times(1)
|
||||
->andReturn($response);
|
||||
$this->provider->setHttpClient($client);
|
||||
|
||||
$token = $this->provider->getAccessToken('authorization_code', ['code' => 'mock_authorization_code']);
|
||||
}
|
||||
|
||||
public function testCanDecryptResponseThrowsExceptionIfResponseIsNotAStringAndEncryptionIsNotUsed(): void
|
||||
{
|
||||
$this->expectException(EncryptionConfigurationException::class);
|
||||
|
||||
$this->provider->decryptResponse('');
|
||||
|
||||
$this->assertFalse($this->provider->usesEncryption());
|
||||
}
|
||||
|
||||
public function testCanDecryptResponseReturnsResponseWhenEncryptionIsUsed(): void
|
||||
{
|
||||
$jwtPayload = json_decode(
|
||||
sprintf(
|
||||
$this->jwtTemplate,
|
||||
(new DateTimeImmutable())->add(new DateInterval('PT1H'))->getTimestamp(),
|
||||
(new DateTimeImmutable())->sub(new DateInterval('P1D'))->getTimestamp(),
|
||||
(new DateTimeImmutable())->sub(new DateInterval('P1D'))->getTimestamp()
|
||||
),
|
||||
true
|
||||
);
|
||||
$jwt = JWT::encode(
|
||||
$jwtPayload,
|
||||
self::ENCRYPTION_KEY,
|
||||
self::ENCRYPTION_ALGORITHM
|
||||
);
|
||||
|
||||
$this->provider
|
||||
->setEncryptionAlgorithm(self::ENCRYPTION_ALGORITHM)
|
||||
->setEncryptionKey(self::ENCRYPTION_KEY);
|
||||
|
||||
$response = $this->provider->decryptResponse($jwt);
|
||||
|
||||
$this->assertSame($jwtPayload, $response);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user