Skip to content

Commit 18cad42

Browse files
committed
Update v2.x-automation-test for micro/plugin-oauth2-client-keycloak
1 parent a794ebb commit 18cad42

File tree

8 files changed

+488
-0
lines changed

8 files changed

+488
-0
lines changed
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* This file is part of the Micro framework package.
7+
*
8+
* (c) Stanislau Komar <head.trackingsoft@gmail.com>
9+
*
10+
* For the full copyright and license information, please view the LICENSE
11+
* file that was distributed with this source code.
12+
*/
13+
14+
namespace Micro\Plugin\OAuth2\Keycloak\Client\Configuration\Provider;
15+
16+
use Micro\Plugin\OAuth2\Client\Configuration\Provider\OAuth2ClientProviderConfiguration;
17+
use Micro\Plugin\OAuth2\Keycloak\Client\Provider\KeycloakProviderConfigurationInterface;
18+
19+
/**
20+
* @author Stanislau Komar <head.trackingsoft@gmail.com>
21+
*/
22+
class ProviderConfiguration extends OAuth2ClientProviderConfiguration implements KeycloakProviderConfigurationInterface
23+
{
24+
const CFG_REALM = 'MICRO_OAUTH2_%s_REALM';
25+
const CFG_SCOPES = 'MICRO_OAUTH2_%s_SCOPES';
26+
27+
/**
28+
* {@inheritDoc}
29+
*/
30+
public function getRealm(): string
31+
{
32+
return $this->get(self::CFG_REALM, 'micro');
33+
}
34+
35+
/**
36+
* {@inheritDoc}
37+
*/
38+
public function getScopesDefault(): array
39+
{
40+
return $this->explodeStringToArray($this->get(self::CFG_SCOPES, 'email,profile'));
41+
}
42+
43+
/**
44+
* {@inheritDoc}
45+
*/
46+
public function getScopesSeparator(): string
47+
{
48+
return ' ';
49+
}
50+
51+
public function getSecurityProvider(): string|null
52+
{
53+
return null;
54+
}
55+
}

Client/LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2022 Komar Stanislau
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* This file is part of the Micro framework package.
7+
*
8+
* (c) Stanislau Komar <head.trackingsoft@gmail.com>
9+
*
10+
* For the full copyright and license information, please view the LICENSE
11+
* file that was distributed with this source code.
12+
*/
13+
14+
namespace Micro\Plugin\OAuth2\Keycloak\Client;
15+
16+
use League\OAuth2\Client\Provider\AbstractProvider;
17+
use Micro\Framework\DependencyInjection\Container;
18+
use Micro\Framework\BootConfiguration\Plugin\ConfigurableInterface;
19+
use Micro\Framework\BootDependency\Plugin\DependencyProviderInterface;
20+
use Micro\Framework\BootConfiguration\Plugin\PluginConfigurationTrait;
21+
use Micro\Plugin\OAuth2\Client\Configuration\OAuth2ClientPluginConfigurationInterface;
22+
use Micro\Plugin\OAuth2\Keycloak\Client\Provider\OAuth2Provider;
23+
use Micro\Plugin\OAuth2\Client\Provider\OAuth2ClientProviderPluginInterface;
24+
use Micro\Plugin\Security\Facade\SecurityFacadeInterface;
25+
26+
/**
27+
* @author Stanislau Komar <head.trackingsoft@gmail.com>
28+
*
29+
* @method OAuth2ClientPluginConfigurationInterface configuration()
30+
*/
31+
class OAuth2KeycloakProviderPlugin implements OAuth2ClientProviderPluginInterface, DependencyProviderInterface, ConfigurableInterface
32+
{
33+
34+
use PluginConfigurationTrait;
35+
36+
const PROVIDER_TYPE = 'keycloak';
37+
38+
/**
39+
* @var Container
40+
*/
41+
private readonly Container $container;
42+
43+
/**
44+
* {@inheritDoc}
45+
*/
46+
public function createProvider(string $providerName): AbstractProvider
47+
{
48+
return new OAuth2Provider(
49+
$this->configuration()->getProviderConfiguration($providerName),
50+
$this->container->get(SecurityFacadeInterface::class),
51+
);
52+
}
53+
54+
/**
55+
* {@inheritDoc}
56+
*/
57+
public function getType(): string
58+
{
59+
return self::PROVIDER_TYPE;
60+
}
61+
62+
/**
63+
* {@inheritDoc}
64+
*/
65+
public function provideDependencies(Container $container): void
66+
{
67+
$this->container = $container;
68+
}
69+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* This file is part of the Micro framework package.
7+
*
8+
* (c) Stanislau Komar <head.trackingsoft@gmail.com>
9+
*
10+
* For the full copyright and license information, please view the LICENSE
11+
* file that was distributed with this source code.
12+
*/
13+
14+
namespace Micro\Plugin\OAuth2\Keycloak\Client;
15+
16+
use Micro\Plugin\OAuth2\Client\Configuration\Provider\OAuth2ClientProviderConfigurationInterface;
17+
use Micro\Plugin\OAuth2\Client\OAuth2ClientPluginConfiguration;
18+
use Micro\Plugin\OAuth2\Keycloak\Client\Configuration\Provider\ProviderConfiguration;
19+
20+
/**
21+
* @author Stanislau Komar <head.trackingsoft@gmail.com>
22+
*/
23+
class OAuth2KeycloakProviderPluginConfiguration extends OAuth2ClientPluginConfiguration
24+
{
25+
/**
26+
* @param string $providerName
27+
*
28+
* @return OAuth2ClientProviderConfigurationInterface
29+
*/
30+
public function getProviderConfiguration(string $providerName): OAuth2ClientProviderConfigurationInterface
31+
{
32+
return new ProviderConfiguration($this->configuration, $providerName);
33+
}
34+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* This file is part of the Micro framework package.
7+
*
8+
* (c) Stanislau Komar <head.trackingsoft@gmail.com>
9+
*
10+
* For the full copyright and license information, please view the LICENSE
11+
* file that was distributed with this source code.
12+
*/
13+
14+
namespace Micro\Plugin\OAuth2\Keycloak\Client\Provider;
15+
16+
use Micro\Plugin\OAuth2\Client\Configuration\Provider\OAuth2ClientProviderConfigurationInterface;
17+
18+
/**
19+
* @author Stanislau Komar <head.trackingsoft@gmail.com>
20+
*/
21+
interface KeycloakProviderConfigurationInterface extends OAuth2ClientProviderConfigurationInterface
22+
{
23+
/**
24+
* @return string
25+
*/
26+
public function getRealm(): string;
27+
28+
/**
29+
* @return array
30+
*/
31+
public function getScopesDefault(): array;
32+
33+
/**
34+
* @return string
35+
*/
36+
public function getScopesSeparator(): string;
37+
38+
/**
39+
* @return string|null
40+
*/
41+
public function getSecurityProvider(): string|null;
42+
}

Client/Provider/OAuth2Provider.php

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* This file is part of the Micro framework package.
7+
*
8+
* (c) Stanislau Komar <head.trackingsoft@gmail.com>
9+
*
10+
* For the full copyright and license information, please view the LICENSE
11+
* file that was distributed with this source code.
12+
*/
13+
14+
namespace Micro\Plugin\OAuth2\Keycloak\Client\Provider;
15+
16+
use League\OAuth2\Client\Provider\AbstractProvider;
17+
use League\OAuth2\Client\Provider\Exception\IdentityProviderException;
18+
use League\OAuth2\Client\Provider\ResourceOwnerInterface;
19+
use League\OAuth2\Client\Token\AccessToken;
20+
use League\OAuth2\Client\Tool\BearerAuthorizationTrait;
21+
use Micro\Plugin\OAuth2\Client\Configuration\Provider\OAuth2ClientProviderConfigurationInterface;
22+
use Micro\Plugin\Security\Facade\SecurityFacadeInterface;
23+
use Psr\Http\Message\ResponseInterface;
24+
25+
/**
26+
* @author Stanislau Komar <head.trackingsoft@gmail.com>
27+
*/
28+
class OAuth2Provider extends AbstractProvider
29+
{
30+
use BearerAuthorizationTrait;
31+
32+
/**
33+
* @param OAuth2ClientProviderConfigurationInterface $providerConfiguration
34+
* @param SecurityFacadeInterface $securityFacade
35+
*/
36+
public function __construct(
37+
private readonly OAuth2ClientProviderConfigurationInterface $providerConfiguration,
38+
private readonly SecurityFacadeInterface $securityFacade
39+
) {
40+
parent::__construct([
41+
'authServerUrl' => $this->providerConfiguration->getUrlAuthorization(),
42+
'clientId' => $providerConfiguration->getClientId(),
43+
'clientSecret' => $this->providerConfiguration->getClientSecret(),
44+
'redirectUri' => $this->providerConfiguration->getUrlRedirect(),
45+
],[]);
46+
}
47+
48+
/**
49+
* {@inheritDoc}
50+
*/
51+
public function getBaseAuthorizationUrl(): string
52+
{
53+
return $this->getBaseUrlWithRealm() . '/protocol/openid-connect/auth';
54+
}
55+
56+
/**
57+
* {@inheritDoc}
58+
*/
59+
public function getBaseAccessTokenUrl(array $params): string
60+
{
61+
return $this->getBaseUrlWithRealm() . '/protocol/openid-connect/token';
62+
}
63+
64+
/**
65+
* {@inheritDoc}
66+
*/
67+
public function getResourceOwnerDetailsUrl(AccessToken $token): string
68+
{
69+
return $this->getBaseUrlWithRealm() . '/protocol/openid-connect/userinfo';
70+
}
71+
72+
/**
73+
* @return string
74+
*/
75+
protected function getBaseUrlWithRealm(): string
76+
{
77+
return
78+
$this->providerConfiguration->getUrlAuthorization() .
79+
'/realms/' .
80+
$this->providerConfiguration->getRealm();
81+
}
82+
83+
/**
84+
* {@inheritDoc}
85+
*/
86+
protected function getDefaultScopes(): array
87+
{
88+
return $this->providerConfiguration->getScopesDefault();
89+
}
90+
91+
/**
92+
* {@inheritDoc}
93+
*/
94+
protected function checkResponse(ResponseInterface $response, $data): void
95+
{
96+
if(is_string($data)) {
97+
throw new IdentityProviderException('Invalid response data', 0, $data);
98+
}
99+
100+
if (empty($data['error'])) {
101+
return;
102+
}
103+
104+
$error = $data['error'];
105+
106+
if(isset($data['error_description'])){
107+
$error .= ': ' . $data['error_description'];
108+
}
109+
110+
throw new IdentityProviderException($error, 0, $data);
111+
}
112+
113+
/**
114+
* {@inheritDoc}
115+
*/
116+
public function getResourceOwner(AccessToken $token): ResourceOwnerInterface
117+
{
118+
$response = $this->fetchResourceOwnerDetails($token);
119+
if (array_key_exists('jwt', $response)) {
120+
$response = $response['jwt'];
121+
}
122+
123+
$response = $this->decryptResponse($response);
124+
125+
return $this->createResourceOwner($response, $token);
126+
}
127+
128+
/**
129+
* Attempts to decrypt the given response.
130+
*
131+
* @param string|array $response
132+
*
133+
* @return array|null
134+
*/
135+
public function decryptResponse(string|array $response): array|null
136+
{
137+
if (!is_string($response)) {
138+
return $response;
139+
}
140+
141+
return $this->securityFacade
142+
->decodeToken($response)
143+
->getParameters();
144+
}
145+
146+
/**
147+
* {@inheritDoc}
148+
*/
149+
protected function getScopeSeparator(): string
150+
{
151+
return $this->providerConfiguration->getScopesSeparator();
152+
}
153+
154+
/**
155+
* {@inheritDoc}
156+
*/
157+
protected function createResourceOwner(array $response, AccessToken $token): ResourceOwnerInterface
158+
{
159+
return new ResourceOwner($response);
160+
}
161+
}

0 commit comments

Comments
 (0)