PHP code example of dmstr / yii2-usuario-keycloak

1. Go to this page and download the library: Download dmstr/yii2-usuario-keycloak library. Choose the download type require.

2. Extract the ZIP file and open the index.php.

3. Add this code to the index.php.
    
        
<?php
require_once('vendor/autoload.php');

/* Start to develop here. Best regards https://php-download.com/ */

    

dmstr / yii2-usuario-keycloak example snippets


use yii\authclient\Collection;
use Da\User\AuthClient\Keycloak;

return [
    'components' => [
        'authClientCollection' => [
            'class' => Collection::class,
            'clients' => [
                'keycloak' => [
                    'class' => Keycloak::class,
                    'title' => getenv('KEYCLOAK_CLIENT_NAME'),
                    'clientId' => getenv('KEYCLOAK_CLIENT_ID'),
                    'clientSecret' => getenv('KEYCLOAK_CLIENT_SECRET'),
                    'issuerUrl' => getenv('KEYCLOAK_ISSUER_URL')
                ]
            ]
        ],
        'user' => [
            // So that the session do not get mixed up
            'enableAutoLogin' => false
        ]
    ]
]

use dmstr\usuario\keycloak\controllers\SecurityController;

return [
    'modules' => [
        'user' => [
            'controllerMap' => [
                'security' => [
                    'class' => SecurityController::class
                ]
            ]
        ]
    ] 
]

use Da\User\Event\SocialNetworkAuthEvent;
use dmstr\usuario\keycloak\controllers\SecurityController;
use yii\web\ForbiddenHttpException;


return [
    'modules' => [
        'user' => [
            'controllerMap' => [
                'security' => [
                    'class' => SecurityController::class,
                    'on ' . SocialNetworkAuthEvent::EVENT_BEFORE_AUTHENTICATE => function (SocialNetworkAuthEvent $event) {
                        if (isset($event->getClient()->getUserAttributes()['email_verified']) && $event->getClient()->getUserAttributes()['email_verified'] === false) {
                            throw new ForbiddenHttpException(Yii::t('usuario-keycloak', 'Account is not verified. Please confirm your registration email.'));
                        }
                    }
                ]
            ]
        ]
    ]   
]

return [
    'modules' => [
        'user' => [
            'sendWelcomeMailAfterSocialNetworkRegistration' => false
        ]
    ] 
]

return [
    'modules' => [
        'user' => [
            'enableSwitchIdentities' => false
        ]
    ] 
]



namespace app\components;

use Yii;
use yii\base\InvalidConfigException;

/**
 * @property-read string|null $authSource
 */
class User extends yii\web\User
{
    protected const AUTH_SOURCE_CLIENT_ID_SESSION_KEY = 'authSourceClientId';

    /**
     * @throws InvalidConfigException
     */
    public function setAuthSource(string $clientId): void
    {
        Yii::$app->getSession()->set(self::AUTH_SOURCE_CLIENT_ID_SESSION_KEY, $clientId);
    }

    /**
     * Returns the name of the auth client with which the user has authenticated himself.
     *
     * - null means not authenticated.
     * - 'app' means, not authenticated via an auth client
     *
     * @return string|null
     */
    public function getAuthSource(): ?string
    {
        if ($this->getIsGuest()) {
            return null;
        }

        return Yii::$app->getSession()->get(self::AUTH_SOURCE_CLIENT_ID_SESSION_KEY, 'app');
    }
}

use app\components\User;
use Da\User\AuthClient\Keycloak;
use Da\User\Event\SocialNetworkAuthEvent;
use dmstr\usuario\keycloak\controllers\SecurityController;
use yii\base\Exception;
use yii\base\InvalidArgumentException;
use yii\web\Application;

return [
        'on ' . Application::EVENT_BEFORE_REQUEST => function () {
        $user = Yii::$app->getUser();
        $keycloakClientId = 'keycloak';
        if ($user && !$user->getIsGuest() && Yii::$app->getUser()->getAuthSource() === $keycloakClientId) {
            try {
                $jwt = Yii::$app->jwt;
                /** @var Keycloak $keycloak */
                $keycloak = Yii::$app->authClientCollection->getClient($keycloakClientId);
                // Check if token is valid
                if (!$jwt->validate($keycloak->getAccessToken()->getToken())) {
                    // If token is invalid log out the user
                    throw new Exception('Access token invalid.');
                }
            } catch (Exception $exception) {
                Yii::error($exception->getMessage());
                // Logout user if token cannot be revalidated or is revoked
                $user->logout();
            }
        }
    },
    'components' => [
        'user' => [
            'class' => User::class 
        ]
    ],
    'modules' => [
        'user' => [
            'controllerMap' => [
                'security' => [
                    'class' => SecurityController::class,
                    'on ' . SocialNetworkAuthEvent::EVENT_AFTER_AUTHENTICATE => function (SocialNetworkAuthEvent $event) {
                        // Save the auth client info to differentiate afterward from which auth client the user was authenticated
                        Yii::$app->getUser()->setAuthSource($event->getClient()->getId());
                    }
                ]
            ]
        ]
    ] 
];

return [
    'components' => [
        'user' => [
            'loginUrl' => '/user/security/auth?authclient=keycloak'
        ]
    ]
];



namespace app\models;

use bizley\jwt\JwtHttpBearerAuth;
use Da\User\Model\SocialNetworkAccount;
use Lcobucci\JWT\Token\Plain;
use yii\base\NotSupportedException;
use Yii;

class User extends \Da\User\Model\User {

    /**
     * @inheritdoc  
     */
    public static function findIdentityByAccessToken($token, $type = null)
    {
        if ($type === JwtHttpBearerAuth::class) {
            /** @var Plain $jwtToken */
            $jwtToken = Yii::$app->jwt->getParser()->parse((string)$token);
                
            $claims = $jwtToken->claims();
            $userClientId = $claims->get('sub');

            /** @var SocialNetworkAccount|null $socialAccount */
            $socialAccount = SocialNetworkAccount::find()->andWhere([
                'provider' => 'keycloak',
                'client_id' => $userClientId
            ])->one();

            if ($socialAccount) {
                return static::find()
                    ->whereId($socialAccount->user_id)
                    ->andWhere(['blocked_at' => null])
                    ->andWhere(['NOT', ['confirmed_at' => null]])
                    ->andWhere(['gdpr_deleted' => 0])
                    ->one();
            }
            
            return null;
        }
        throw new NotSupportedException("Type '$type' is not implemented.");
    }
}

use app\models\User as UserModel;

return [
    'components' => [
        'user' => [
            'identityClass' => UserModel::class
        ]
    ]
]

use bizley\jwt\Jwt;
use Lcobucci\JWT\Validation\Constraint\IssuedBy;
use Lcobucci\JWT\Validation\Constraint\SignedWith;
use Lcobucci\JWT\Validation\Constraint\LooseValidAt;
use Lcobucci\Clock\SystemClock;

return [
    'components' => [
        'jwt' => [
            'class' => Jwt::class,
            'signer' => Jwt::RS256,
            'signingKey' => [
                'key' => getenv('KEYCLOAK_PRIVATE_KEY_FILE'),
                'method' => Jwt::METHOD_FILE,
            ],
            'verifyingKey' => [
                'key' => getenv('KEYCLOAK_PUBLIC_KEY_FILE'),
                'method' => Jwt::METHOD_FILE,
            ],
            'validationConstraints' => function (Jwt $jwt) {
                $config = $jwt->getConfiguration();
                return [
                    new SignedWith($config->signer(), $config->verificationKey()),
                    new IssuedBy(getenv('KEYCLOAK_ISSUER_URL')),
                    new LooseValidAt(SystemClock::fromUTC()),
                ];
            }
        ]
    ]
];

use bizley\jwt\JwtTools;
use Lcobucci\JWT\Validation\Constraint\IssuedBy;
use Lcobucci\JWT\Validation\Constraint\SignedWith;
use Lcobucci\JWT\Validation\Constraint\LooseValidAt;
use Lcobucci\Clock\SystemClock;

return [
    'components' => [
        'jwt' => [
            'class' => JwtTools::class,
            'validationConstraints' => function (JwtTools $jwt) {
                return [
                    new SignedWith($jwt->buildSigner(Jwt::RS256), InMemory::plainText(getenv('KEYCLOAK_PUBLIC_KEY_FILE'))),
                    // You could also use this line if you do not want to use a separate public key file
                    // new SignedWith($jwt->buildSigner(Jwt::RS256), InMemory::plainText(KeycloakHelper::publicKeyFromIssuer(getenv('KEYCLOAK_ISSUER_URL')))),
                    new IssuedBy(getenv('KEYCLOAK_ISSUER_URL')),
                    new LooseValidAt(SystemClock::fromUTC()),
                ];
            }
        ]
    ]
];

use Da\User\Controller\RegistrationController;
use ActionEvent;

return [
    'modules' => [
        'user' => [
            'controllerMap' => [
                'registration' => [
                    'class' => RegistrationController::class,
                    'on ' . RegistrationController::EVENT_BEFORE_ACTION => function (ActionEvent $event) {
                        if ($event->action->id === 'connect') {
                            // You may need to change the form id but this is the default
                            $event->action->controller->view->registerJs('if ($(".has-error").length === 0){$("form#User").submit()};');
                        }
                    }
                ]
            ]
        ]
    ] 
]