Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to use this extension with official Yii2 AuthClient ? #155

Open
ahmadfadlydziljalal opened this issue Dec 12, 2022 · 1 comment
Open

Comments

@ahmadfadlydziljalal
Copy link

I have 2 apps:

Everything is looked good in every step at Yii2 server side:

 'components' => [
        'urlManager' => [
            'enablePrettyUrl' => true,
            'showScriptName' => false,
            'rules' => [
                '<alias:\w+>' => 'site/<alias>',
                'POST oauth2/<action:\w+>' => 'oauth2/rest/<action>',
            ]
        ],
],
 'modules' => [
        'oauth2' => [
           'class' => 'filsh\yii2\oauth2server\Module',
           'components' => [
              'request' => function () {
                 return \filsh\yii2\oauth2server\Request::createFromGlobals();
              },
              'response' => [
                 'class' => \filsh\yii2\oauth2server\Response::class,
              ],
           ],
            'tokenParamName' => 'accessToken',
            'tokenAccessLifetime' => 3600 * 24,
            'storageMap' => [
                'user_credentials' => 'app\models\User',
            ],
            'grantTypes' => [
                'user_credentials' => [
                    'class' => 'OAuth2\GrantType\UserCredentials',
                ],
                'authorization_code' => [
                   'class' => 'OAuth2\GrantType\AuthorizationCode'
                ],
                'refresh_token' => [
                    'class' => 'OAuth2\GrantType\RefreshToken',
                    //'always_issue_new_refresh_token' => true
                ]
            ],
            'options' => [
                'allow_implicit' => true
            ]
        ],
  ....

But the problem is, when client side successfully login, why userAttributes is null ?
In Yii2 client side, I set the SiteController like this:

 /**
     * {@inheritdoc}
     */
    public function actions()
    {
        return [
            'error' => [
                'class' => 'yii\web\ErrorAction',
            ],
            'captcha' => [
                'class' => 'yii\captcha\CaptchaAction',
                'fixedVerifyCode' => YII_ENV_TEST ? 'testme' : null,
            ],
            'auth' => [
                'class' => 'yii\authclient\AuthAction',
                'successCallback' => [$this, 'onAuthSuccess'],
            ],
        ];
    }

    public function onAuthSuccess($client)
    {
        (new AuthHandler($client))->handle();
    }

Then, in AuthHandler:

<?php

namespace app\components;

use app\models\Auth;
use app\models\User;
use Exception;
use Yii;
use yii\authclient\ClientInterface;
use yii\helpers\ArrayHelper;
use yii\helpers\Html;
use yii\helpers\StringHelper;
use yii\helpers\VarDumper;

/**
 * AuthHandler handles successful authentication via Yii auth component
 */
class AuthHandler
{
    /**
     * @var ClientInterface
     */
    private ClientInterface $client;

    public function __construct(ClientInterface $client)
    {
        $this->client = $client;
    }

    /**
     * @return void
     * @throws \yii\base\Exception
     * @throws \yii\db\Exception
     * @throws Exception
     */
    public function handle()
    {
       
        $attributes = $this->client->getUserAttributes();

        /*
         * for debugging
         * die(Html::tag('pre', VarDumper::dumpAsString($attributes)));
         * */

        $email = ArrayHelper::getValue($attributes, 'email');
        $id = ArrayHelper::getValue($attributes, 'id');
        $nickname = str_replace(" ", "-", ArrayHelper::getValue($attributes, 'name'));

        /* @var Auth $auth */
        $auth = Auth::find()->where([
            'source' => $this->client->getId(),
            'source_id' => $id,
        ])->one();

        if (Yii::$app->user->isGuest) {

            if ($auth) { // login

                $user = $auth->user;
                $this->updateUserInfo($user);
                Yii::$app->user->login($user, Yii::$app->params['user.rememberMeDuration']);
                Yii::$app->getSession()->setFlash(
                    'success',
                    'Login by ' . ucfirst($this->client->getId()) . ' Welcome! '
                );
            } else { // signup

                if ($email !== null && User::find()->where(['email' => $email])->exists()) {

                    Yii::$app->getSession()->setFlash('error', [
                        Yii::t('app', "User with the same email as in {client} account already exists but isn't linked to it. Login using email first to link it.", ['client' => $this->client->getTitle()]),
                    ]);
                } else {

                    $password = Yii::$app->security->generateRandomString(Yii::$app->params['user.passwordMinLength']);
                    $user = new User([
                        'username' => $nickname,
                        //'github' => $nickname,
                        'email' => $email,
                        'password' => $password,
                    ]);
                    $user->generateAuthKey();
                    $user->generatePasswordResetToken();

                    $transaction = User::getDb()->beginTransaction();

                    if ($user->save()) {

                        $auth = new Auth([
                            'user_id' => $user->id,
                            'source' => $this->client->getId(),
                            'source_id' => (string)$id,
                        ]);

                        if ($auth->save()) {

                            $transaction->commit();

                            Yii::$app->getSession()->setFlash(
                                'success',
                                'Joined as ' . ucfirst($this->client->getId()) . ', and welcome...! '
                            );
                            Yii::$app->user->login($user, Yii::$app->params['user.rememberMeDuration']);
                        } else {

                            Yii::$app->getSession()->setFlash('error', [
                                Yii::t('app', 'Unable to save {client} account: {errors}', [
                                    'client' => $this->client->getTitle(),
                                    'errors' =>  yii\helpers\Json::encode($auth->getErrors()),
                                ]),
                            ]);
                        }
                    } else {

                        Yii::$app->getSession()->setFlash('error', [
                            Yii::t('app', 'Unable to save user: {errors}', [
                                'client' => $this->client->getTitle(),
                                'errors' => yii\helpers\Json::encode($user->getErrors()),
                            ]),
                        ]);
                    }
                }
            }
        } else { // user already logged in
            if (!$auth) { // add auth provider

                $auth = new Auth([
                    'user_id' => Yii::$app->user->id,
                    'source' => $this->client->getId(),
                    'source_id' => (string)$attributes['id'],
                ]);

                if ($auth->save()) {

                    $user = $auth->user;
                    $this->updateUserInfo($user);
                    Yii::$app->getSession()->setFlash('success', [
                        Yii::t('app', 'Linked {client} account.', [
                            'client' => $this->client->getTitle()
                        ]),
                    ]);
                } else {

                    Yii::$app->getSession()->setFlash('error', [
                        Yii::t('app', 'Unable to link {client} account: {errors}', [
                            'client' => $this->client->getTitle(),
                            'errors' => yii\helpers\Json::encode($auth->getErrors()),
                        ]),
                    ]);
                }
            } else { // there's existing auth
                Yii::$app->getSession()->setFlash('error', [
                    Yii::t(
                        'app',
                        'Unable to link {client} account. There is another user using it.',
                        ['client' => $this->client->getTitle()]
                    ),
                ]);
            }
        }
    }


    /**
     * @param User $user
     * @return void
     * @throws Exception
     */
    private function updateUserInfo(User $user)
    {
        $attributes = $this->client->getUserAttributes();
        $github = ArrayHelper::getValue($attributes, 'login');
        if ($github) {
            if ($user->github === null) {
                $user->github = $github;
                $user->save();
            }
        }
    }
}

Here is the config that I used in Yii2 client side:

<?php

namespace app\components;

use yii\authclient\OAuth2;

class MyAuthClient extends OAuth2
{

    protected function defaultName()
    {
        return 'my_auth_client';
    }

    protected function defaultTitle()
    {
        return 'My Auth Client';
    }

    protected function initUserAttributes()
    {
        return $this->api('userinfo', 'GET');
    }
}


 'components' => [
        'authClientCollection' => [
            'class' => 'yii\authclient\Collection',
            'clients' => [
                'myauth' => [
                    'class' => '\app\components\MyAuthClient',
                    'clientId' => 'testclient',
                    'clientSecret' => 'testpass',
                    'authUrl' => 'http://10.60.36.60:8080/authorize',
                    'tokenUrl' => 'http://10.60.36.60:8080/oauth2/token',
                    'apiBaseUrl' => 'http://10.60.36.60:8080/apis/oauth2/v1'
                ],

            ],
        ],
        ....

And, Here is the info:

app\components\MyAuthClient#1
(
    [yii\base\Component:_events] => []
    [yii\base\Component:_eventWildcards] => []
    [yii\base\Component:_behaviors] => null
    [yii\authclient\BaseClient:_id] => 'myauth'
    [yii\authclient\BaseClient:_name] => null
    [yii\authclient\BaseClient:_title] => null
    [yii\authclient\BaseClient:_userAttributes] => null
    [yii\authclient\BaseClient:_normalizeUserAttributeMap] => null
    [yii\authclient\BaseClient:_viewOptions] => null
    [yii\authclient\BaseClient:_httpClient] => yii\httpclient\Client#2
    (
        [yii\base\Component:_events] => []
        [yii\base\Component:_eventWildcards] => []
        [yii\base\Component:_behaviors] => []
        [baseUrl] => 'http://10.60.36.60:8080/apis/oauth2/v1'
        [formatters] => [
            'urlencoded' => yii\httpclient\UrlEncodedFormatter#3
            (
                [encodingType] => 1
                [charset] => null
            )
        ]
        [parsers] => [
            'json' => yii\httpclient\JsonParser#4
            (
                [asArray] => true
            )
        ]
        [requestConfig] => []
        [responseConfig] => []
        [contentLoggingMaxSize] => 2000
        [yii\httpclient\Client:_transport] => yii\httpclient\StreamTransport#5
        (
            [yii\base\Component:_events] => []
            [yii\base\Component:_eventWildcards] => []
            [yii\base\Component:_behaviors] => null
        )
    )
    [yii\authclient\BaseClient:_requestOptions] => []
    [yii\authclient\BaseClient:_stateStorage] => yii\authclient\SessionStateStorage#6
    (
        [yii\base\Component:_events] => []
        [yii\base\Component:_eventWildcards] => []
        [yii\base\Component:_behaviors] => null
        [session] => yii\web\Session#7
        (
            [yii\base\Component:_events] => []
            [yii\base\Component:_eventWildcards] => []
            [yii\base\Component:_behaviors] => null
            [flashParam] => '__flash'
            [handler] => null
            [*:_forceRegenerateId] => null
            [yii\web\Session:_cookieParams] => [
                'httponly' => true
            ]
            [yii\web\Session:frozenSessionData] => null
            [yii\web\Session:_hasSessionId] => true
        )
    )
    [version] => '2.0'
    [apiBaseUrl] => 'http://10.60.36.60:8080/apis/oauth2/v1'
    [authUrl] => 'http://10.60.36.60:8080/authorize'
    [scope] => null
    [autoRefreshAccessToken] => true
    [parametersToKeepInReturnUrl] => [
        0 => 'authclient'
    ]
    [yii\authclient\BaseOAuth:_returnUrl] => 'http://10.60.36.60:8081/auth?authclient=myauth'
    [yii\authclient\BaseOAuth:_accessToken] => yii\authclient\OAuthToken#8
    (
        [tokenParamKey] => 'access_token'
        [tokenSecretParamKey] => 'oauth_token_secret'
        [createTimestamp] => 1670846067
        [yii\authclient\OAuthToken:_expireDurationParamKey] => null
        [yii\authclient\OAuthToken:_params] => [
            'access_token' => '358d8f2831a027f9e815a5b44f569678445a9429'
            'expires_in' => 86400
            'token_type' => 'Bearer'
            'scope' => null
            'refresh_token' => 'da3f5c8c33cd694479ee4a4fb51ccdd7943cf478'
        ]
    )
    [yii\authclient\BaseOAuth:_signatureMethod] => []
    [clientId] => 'testclient'
    [clientSecret] => 'testpass'
    [tokenUrl] => 'http://10.60.36.60:8080/oauth2/token'
    [validateAuthState] => true
    [enablePkce] => false
)
@ahmadfadlydziljalal
Copy link
Author

Maybe we need, how to implement the OpenID ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant