在 laravel 中使用第三方登入並整合 Auth Facade

在 legency 專案上要變換登入要考量的事很多,其中一個情況就是 blade 大量使用 Auth Facade 做 UI 的呈現變化,加上第三方登入都跟你 token 來 token 去,不熟 auth 還真不知道怎麼下手。

這邊提出在這種情況下比較無痛轉移的方法

在 laravel 中修改驗證時有幾個地方要改,以下分別列出並提供範例

  • config/auth.php
<?php
...
'providers' => [
    'users' => [
        'driver' => 'custom-driver',
    ]
]
...
  • AuthServiceProvider.php

you can use php artisan to create provider

<?php
public function boot() {
    ...
    Auth::provider('custom-driver', function ($app, array $config) {
        return new YourCustomAuthProvider();
    });
}
  • YourCustomAuthProvider.php
<?php
use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Auth\GenericUser;

class YourCustomAuthProvider implements \Illuminate\Contracts\Auth\UserProvider
{
    // implement all contrat
    // below is sample
    public function retrieveById($identifier)
    {
        return $this->getUser($identifier);
    }

    public function retrieveByToken($identifier, $token)
    {
        $user = $this->getUser($identifier);
        return isset($user['remember_token']) && $user['remember_token'] == $token ? $user : null;
    }

    public function updateRememberToken(Authenticatable $user, $token)
    {
        Session::put('remember_token', $token);
    }

    public function retrieveByCredentials(array $credentials)
    {
        $token = $this->getAccessToken();
        if(!$token) {
            // invalid token actions here
        }
        $data = $this->getUserData($token);
        return new GenericUser($data);
    }

    public function validateCredentials(Authenticatable $user, array $credentials)
    {
        return yourCredentialsIsValidate() ? true: false;
    }
}
  • YourLoginController.php
<?php
...
use Illuminate\Auth\GenericUser;
...
public function login(Request $request)
{
    $data = getTokenAndUserDataFromThirdParty();

    // the key-point is here
    Auth::guard('web')->login(new GenericUser(['id' => $data['email']]))
    // then you keep your facade works as usual
}

補充:使用 redis 實現 sso

  • redis retrive session
<?php
...
use Illuminate\Support\Facades\Redis;
use Illuminate\Support\Facades\Auth;
use SessionHandlerInterface;

class RedisSessionServices implements SessionHandlerInterface
{
    public function open($savePath, $sessionName)
    {
        return true;
    }

    public function close()
    {
        return true;
    }

    public function read($sessionId)
    {
        return Redis::get($this->prefixCheck($sessionId));
    }

    public function write($sessionId, $data)
    {
        // can add auth check if needed
        Redis::set($this->prefixCheck($sessionId), $data, 'EX', $expireTime);

        return true;
    }

    public function destroy($sessionId)
    {
        Redis::del($this->prefixCheck($sessionId));

        return true;
    }

    public function gc($lifetime)
    {
        return true;
    }

    /**
    * laravel 原生會帶 :session:
    */
    private function prefixCheck($sessionId)
    {
        $prefix = env('REDIS_PREFIX') . ':session:';
        if (strpos($sessionId, $prefix) === false) {
            $sessionId = $prefix . $sessionId;
        }

        return $sessionId;
    }
}

Reference

official doc

cmd + /