granch
granch

Reputation: 225

How save access_token to db using yii2-dektrium facebook login?

I'm using yii2-dektrium to allow users login with their facebook's accounts.

After the login is done, I need to make API request from my server to get data of the user's accounts. One example of request is:

$client = Yii::$app->authClientCollection->getClient('facebook');
$response = $client->createApiRequest()
        ->setMethod('GET')
        ->setUrl('v2.12/me/accounts')
        ->send();

The access_token is saved on session so I need to persist it to the database.

I already added a column access_token to the social_account default table of yii2-dektrium but I don't know how to get and save it, and further more, how to apply it to the requests.

After reading for a while. I think the way to save it is overriding the method connect in dektrium\user\controllers\SecurityController.

public function connect(ClientInterface $client)
{
    /** @var Account $account */
    $account = \Yii::createObject(Account::className());
    $event   = $this->getAuthEvent($account, $client);
    $this->trigger(self::EVENT_BEFORE_CONNECT, $event);
    $account->connectWithUser($client);
    $this->trigger(self::EVENT_AFTER_CONNECT, $event);
    $this->action->successUrl = Url::to(['/user/settings/networks']);
}

And for applying to the request, override applyAccessTokenToRequest on yii\authclient\clients\Facebook

public function applyAccessTokenToRequest($request, $accessToken)
{
    parent::applyAccessTokenToRequest($request, $accessToken);
    $data = $request->getData();
    if (($machineId = $accessToken->getParam('machine_id')) !== null) {
        $data['machine_id'] = $machineId;
    }
    $data['appsecret_proof'] = hash_hmac('sha256', $accessToken->getToken(), $this->clientSecret);
    $request->setData($data);
}

I can't get it done. And I'm not sure if it is the right way to do it. What I'm missing?

Upvotes: 0

Views: 589

Answers (1)

granch
granch

Reputation: 225

For save the access_token the first time you have to overwrite the connect action from \dektrium\user\controllers\SecurityController.

class SecurityController extends \dektrium\user\controllers\SecurityController
{
    public function connect(ClientInterface $client)
    {
        // default implementation of connect
        $account = \Yii::createObject(Account::className());
        $event   = $this->getAuthEvent($account, $client);
        $this->trigger(self::EVENT_BEFORE_CONNECT, $event);
        $account->connectWithUser($client);
        $this->trigger(self::EVENT_AFTER_CONNECT, $event);

        // get acess_token from $client
        $access_token['tokenParamKey'] = $client->getAccessToken()->tokenParamKey;
        $access_token['tokenSecretParamKey'] = $client->getAccessToken()->tokenSecretParamKey;
        $access_token['createTimestamp'] = $client->getAccessToken()->createTimestamp;
        $access_token['_expireDurationParamKey'] = $client->getAccessToken()->getExpireDurationParamKey();
        $access_token['_params'] = $client->getAccessToken()->getParams();

        // save acess_token to social_account table
        $model = SocialAccount::find()->where(['provider' => $client->getName()])->andWhere(['user_id' => Yii::$app->user->id])->one();
        $model->access_token = \yii\helpers\Json::encode($access_token);
        $model->save(false);

        $this->action->successUrl = Url::to(['/user/settings/networks']);
    }
}

To get the access_token store in the database for further API Requests create a class that extends yii\authclient\SessionStateStorage and overwrite get method.

namespace app\models\authclient;

class DbStateStorage extends SessionStateStorage
{
    public function get($key)
    {
        // $key is a complex string that ends with 'token' if the value to get is the actual access_token
        $part = explode('_', $key);
        if (count($part) == 3 && $part[2] == 'token') {
            $account = SocialAccount::find()
                ->where(['provider' => $part[1]])
                ->andWhere(['user_id' => Yii::$app->user->id])
                ->one();
            if ($account != null) {
                $access_token = json_decode($account->access_token);
                $token = new \yii\authclient\OAuthToken();
                $token->createTimestamp = $access_token->createTimestamp;
                $token->tokenParamKey = $access_token->tokenParamKey;
                $token->tokenSecretParamKey = $access_token->tokenSecretParamKey;
                $token->setParams((array)$access_token->_params);
                $token->setExpireDurationParamKey($access_token->_expireDurationParamKey);
                return $token;
            }
        }
        if ($this->session !== null) {
            return $this->session->get($key);
        }
        return null;
    }
}

Finally set the DbStateStorage to your authclient

class Facebook extends \dektrium\user\clients\Facebook
{
    public function __construct()
    {
        $this->setStateStorage('app\models\authclient\DbStateStorage');
    }
}

Upvotes: 0

Related Questions