Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
eluhr committed Jan 13, 2023
0 parents commit 316b3b1
Show file tree
Hide file tree
Showing 4 changed files with 250 additions and 0 deletions.
73 changes: 73 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# Yii2 usuario keycloak client

### Installation and Setup

For the installation and setup see [usuario docs](https://yii2-usuario.readthedocs.io/en/latest/)

Install the package via composer
```bash
composer require dmstr/yii2-usuario-keycloak
```

Update your yii2 app config
```php
use bizley\jwt\Jwt;
use dmstr\tokenManager\components\TokenManager;
use dmstr\usuario\keycloak\Bootstrap;
use Lcobucci\Clock\SystemClock;
use Lcobucci\JWT\Validation\Constraint\IssuedBy;
use Lcobucci\JWT\Validation\Constraint\LooseValidAt;
use Lcobucci\JWT\Validation\Constraint\SignedWith;

return [
...
'bootstrap' => [
[
'class' => Bootstrap::class,
'clientAuthUrl' => getenv('KEYCLOAK_AUTH_URL'),
'clientIssuerUrl' => getenv('KEYCLOAK_ISSUER_URL'),
'clientClientId' => getenv('KEYCLOAK_CLIENT'),
'clientClientSecret' => getenv('KEYCLOAK_CLIENT_SECRET')
]
],
'components' => [
'jwt' => [ // example
'class' => Jwt::class,
'signer' => Jwt::RS256,
'signingKey' => [
'key' => getenv('JWT_PUBLIC_KEY_FILE'),
'method' => Jwt::METHOD_FILE,
],
'verifyingKey' => [
'key' => getenv('JWT_PUBLIC_KEY_FILE'),
'method' => Jwt::METHOD_FILE,
],
'validationConstraints' => function ($jwt) {
$config = $jwt->getConfiguration();
return [
new SignedWith($config->signer(), $config->signingKey()),
new IssuedBy(getenv('JWT_TOKEN_ISSUER')),
new LooseValidAt(SystemClock::fromUTC()),
];
}
],
'tokenManager' => [
'class' => TokenManager::class
]
],
'modules' => [
'user' => [
'enableRegistration' => true
]
]
...
];
```

After logging in to an account via social account login you can access to token like this

```php
use dmstr\tokenManager\components\TokenManager;

$token = Yii::$app->tokenManager->getToken();
```
26 changes: 26 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"name": "dmstr/yii2-usuario-keycloak",
"description": "Yii2 usuario keycloak plugin",
"authors": [
{
"name": "Andres Klapper",
"email": "a.klapper@herzogkommunikation.de"
},
{
"name": "Elias Luhr",
"email": "e.luhr@herzogkommunikation.de"
}
],
"require": {
"yiisoft/yii2-authclient": "^2.2",
"2amigos/yii2-usuario": "^1.5.1",
"web-token/jwt-checker": ">=1.0 <3.0",
"web-token/jwt-signature": ">=1.0 <3.0",
"web-token/jwt-signature-algorithm-hmac": ">=1.0 <3.0",
"web-token/jwt-signature-algorithm-ecdsa": ">=1.0 <3.0",
"web-token/jwt-signature-algorithm-rsa": ">=1.0 <3.0",
"web-token/jwt-key-mgmt": ">=1.0 <3.0",
"dmstr/yii2-token-manager": "^1.0",
"bizley/jwt": "^3.4"
}
}
96 changes: 96 additions & 0 deletions src/Bootstrap.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
<?php

namespace dmstr\usuario\keycloak;

use bizley\jwt\Jwt;
use Da\User\Controller\SecurityController;
use Da\User\Event\SocialNetworkAuthEvent;
use dmstr\tokenManager\components\TokenManager;
use dmstr\usuario\keycloak\clients\Keycloak;
use Lcobucci\JWT\Token;
use Lcobucci\JWT\UnencryptedToken;
use yii\authclient\OAuthToken;
use yii\base\BootstrapInterface;
use yii\base\Event;
use yii\helpers\VarDumper;
use yii\web\BadRequestHttpException;
use Yii;

/**
* --- PROPERTIES ---
*
* @author Elias Luhr
*/
class Bootstrap implements BootstrapInterface
{
public string $clientName;
public string $clientTitle;
public string $clientAuthUrl;
public string $clientTokenUrl;
public string $clientIssuerUrl;
public string $clientClientId;
public string $clientClientSecret;

public string $jwtComponentId = 'jwt';
public string $tokenManagerComponentId = 'tokenManager';

public function __construct()
{
$this->clientName = getenv('KEYCLOAK_CLIENT_ID');
$this->clientTitle = getenv('KEYCLOAK_CLIENT_TITLE');
}

/**
* @param \yii\base\Application $app
* @return void
*/
public function bootstrap($app)
{
// Configure keycloak client
$clients = $app->authClientCollection->getClients();
$clients[$this->clientName] = [
'class' => Keycloak::class,
'authUrl' => $this->clientAuthUrl,
'issuerUrl' => $this->clientIssuerUrl,
'tokenUrl' => $this->clientTokenUrl,
'clientId' => $this->clientClientId,
'clientSecret' => $this->clientClientSecret,
'name' => $this->clientName,
'title' => $this->clientTitle
];
$app->authClientCollection->setClients($clients);


// Create/update attached account
Event::on(SecurityController::class, SocialNetworkAuthEvent::EVENT_BEFORE_AUTHENTICATE, function (SocialNetworkAuthEvent $event) {
if ($event->getClient()->getName() === $this->clientName) {
$event->account->save(false);
}
});

// Retrieve and process and save token
Event::on(SecurityController::class, SocialNetworkAuthEvent::EVENT_AFTER_AUTHENTICATE, function (SocialNetworkAuthEvent $event) {
$oauthAccessToken = $event->getClient()->getAccessToken();
// check ig access token is in expected format
if ($oauthAccessToken instanceof OAuthToken) {
// get token as string
$token = $oauthAccessToken->getToken();
/** @var Jwt $jwtComponent */
$jwtComponent = Yii::$app->get($this->jwtComponentId);
// parse string representation of the token to a token object
$parsedToken = $jwtComponent->getParser()->parse($token);
// validate the token
if ($jwtComponent->validate($parsedToken)) {
/** @var TokenManager $tokenManager */
$tokenManager = Yii::$app->get($this->tokenManagerComponentId);
// save parsed token via token manager
$tokenManager->setToken($parsedToken);
return; // get out of here
}

}
Yii::$app->getUser()->logout();
throw new BadRequestHttpException(\Yii::t('usuario.keycloak','Invalid token'));
});
}
}
55 changes: 55 additions & 0 deletions src/clients/Keycloak.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<?php

namespace dmstr\usuario\keycloak\clients;

use Da\User\Contracts\AuthClientInterface;
use yii\authclient\OpenIdConnect;
use yii\web\HttpException;

/**
* --- PROPERTIES ---
*
* @author Elias Luhr
*/
class Keycloak extends OpenIdConnect implements AuthClientInterface
{
/**
* {@inheritdoc}
*/
protected function initUserAttributes()
{
$token = $this->getAccessToken()->getToken();
return $this->loadJws($token);
}

/**
* {@inheritdoc}
*/
public function getEmail()
{
return $this->getUserAttributes()['email'] ?? null;
}

/**
* {@inheritdoc}
*/
public function getUsername()
{
// returns the e-mail as it corresponds with the username
return $this->getEmail();
}

/**
* @throws HttpException
* @return mixed
*/
public function getRealmAccess(): mixed
{
$accessToken = $this->getAccessToken();
if ($accessToken) {
$token = $accessToken->getToken();
return $this->loadJws($token)['realm_access'] ?? null;
}
return null;
}
}

0 comments on commit 316b3b1

Please sign in to comment.