3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

CakePHP4 で Auth0 を使う。まずは Auth0 に慣れる

Last updated at Posted at 2020-04-25

Auth0 を使った認証を試してみたいと思います。
まずは、Auth0 の認証(Authentication API)でどのようなことをできるかを試します。

この記事でわかること

  • CakePHP4 での Auth0 の認証(Authentication API)使い方。実践で使うことはできないけど、動きをみることはできる。
  • この記事内のソースは以下で公開しています。

バージョン情報

バージョン
CakePHP4 4.0.5
auth0/auth0-php 7.1.0

事前準備

docker-compose up -d

Auth0 の設定

Auth0 のアカウント作成方法は、他サイトを参考にしてください。
アカウント作成後からの手順をメモがわりに残します。

Application の作成

  • 左メニュー「Applications」をクリック
  • 画面右「+ CREATE APPLICATION」ボタンをクリック
  • 以下を入力&選んで「CREATE」ボタンをクリック
    • Name:自身で決めたアプリケーション名を入力
    • Choose an application type:「Regular Web Applications」を選択

これで Application が作成されます。

Application の設定

  • 左メニュー「Applications」をクリック
  • 一覧から該当するアプリケーション名リンクをクリック
  • タブ「Settings」をクリック
  • 以下を入力して「SAVE CHANGES」ボタンをクリック
    • Allowed Callback URLs:「 http://localhost/auth/callback 」を入力
      • Authorization Code Flow での処理で Code を受け取る URL を記載する。
    • Allowed Logout URLs:「 http://localhost/ 」を入力
      • ログアウト後にリダイレクトする(リダイレクト先として指定する) URL を記載する。

CakePHP4 の実装

Auth0 のライブラリ追加

Composer を使って Auth0 のライブラリを追加します。
(下の例では、Docker経由で Composer を実行しています。)

docker exec -it app php composer.phar require auth0/auth0-php

envファイル

Auth0 に関する設定情報を ./config/.env ファイルへ追加します。
追加する設定情報は以下です。

AUTH0_DOMAIN、AUTH0_CLIENT_ID、AUTH0_CLIENT_SECRET は、 Auth0 管理画面( Auth0 左メニュー「Applications」 > 該当アプリケーション名リンク > タブ「Settings」 )の Domain、Client ID、Client Secret を記載します。

AUTH0_CALLBACK_URL、AUTH0_LOGOUT_URL は、Auth0 Application 設定でセットした内容です。

config/.env
# Auth0
export AUTH0_DOMAIN="xxxx.auth0.com"
export AUTH0_CLIENT_ID="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
export AUTH0_CLIENT_SECRET="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
export AUTH0_CALLBACK_URL="http://localhost/auth/callback"
export AUTH0_LOGOUT_URL="http://localhost/"

他の人にも展開することを考えて、 ./config/.env.example へも同様の記述を残していきます。
秘密的な情報はあまり記載したくないので、自分は以下の内容にしています。

config/.env.example
# Auth0
export AUTH0_DOMAIN="example.auth0.com"
export AUTH0_CLIENT_ID=""
export AUTH0_CLIENT_SECRET=""
export AUTH0_CALLBACK_URL="http://localhost/auth/callback"
export AUTH0_LOGOUT_URL="http://localhost/"

AuthController の実装

最後に Controller の実装の全体は載せます。ここではメソッド単位で説明します。

Auth0 インスタンス作成

Auth0 のインスタンスを作るメソッドです。

RefreshTokenが必要な場合、 scopeoffline_access を追加する必要があります。

persist_* のパラメータは、Auth0から取得した情報を Store(デフォルトだとセッション情報)へ保持するかです。
user はデフォルト保持なので、保持したくない場合 false を指定します。
その他の token 系情報は、デフォルト保持しないなので、保持したい場合 true を指定します。

Auth0 のインスタンスを作るときのパラメータはたくさんあります。詳しくはソースを見るのが手っ取り早いです。

src/Controller/AuthController.php
    /**
     * @return \Auth0\SDK\Auth0
     */
    private function auth0(): Auth0
    {
        return new Auth0([
            'domain' => env('AUTH0_DOMAIN', ''),
            'client_id' => env('AUTH0_CLIENT_ID', ''),
            'client_secret' => env('AUTH0_CLIENT_SECRET', ''),
            'redirect_uri' => env('AUTH0_CALLBACK_URL', ''),

            // refresh token が必要な場合は、scope へ offline_access を追加する
            // @see https://auth0.com/docs/tokens/guides/get-refresh-tokens
            'scope' => 'openid profile email',
            // 'scope' => 'openid profile email offline_access',

            // exchange (code から access token を取得) 後、ユーザー情報を取得する場合は true とする
            // access token と一緒に返却される id token(JWT) を decode したユーザー情報でよければ false (デフォルト) とする
            // 'skip_userinfo' => false,

            // access token を Store (Session情報) へ保持しない場合は false とする
            // 'persist_user' => false,

            // access token を Store (Session情報) へ保持する場合は true とする
            'persist_access_token' => true,

            // refresh token を Store (Session情報) へ保持する場合は true とする
            // 'persist_refresh_token' => true,

            // id token を Store (Session情報) へ保持する場合は true とする
            // 'persist_id_token' => true,
        ]);
    }

login の実装

Auth0 のログイン画面を表示するアクションです。

$auth0->login() でも良いのですが、呼び出したメソッド先で勝手に exit されるのが気持ち悪く感じたので、あえて $auth0->getLoginUrl() でリダイレクトURLを取得して、自身でリダイレクトを実行させています。

それと、 Code を受け取るときの Callback でエラーを発生させないために、ここで Session クリアを実施しています。

src/Controller/AuthController.php
    /**
     * @return \Cake\Http\Response|null
     */
    public function login(): ?Response
    {
        // Session を破棄して、Auth0のユーザー情報を空にする。
        // そうしないと、Auth0->exchange() で
        // Can't initialize a new session while there is one active session already
        // が発生する。
        $this->getRequest()->getSession()->destroy();

        $auth0 = $this->auth0();

        $loginUrl = $auth0->getLoginUrl();

        return $this->redirect($loginUrl);
    }

callback の実装

Authorization Code Flow での処理で Code を受け取るアクションです。

Code から AccessToken / IdToken を取得する $auth0->exchange() を呼び出します。
queryパラメータからの Code の取得や queryパラメータの State のチェックは $auth0->exchange() のメソッド内で適切に実施してくれます。

デフォルトの場合、State のチェックは Cookie を使っており、チェック時に Cookie から値を削除するため、2度 $auth0->exchange() を呼び出すと Stateが不正である旨のエラーが発生します。気を付けてください。

src/Controller/AuthController.php
    /**
     * @return \Cake\Http\Response|null
     */
    public function callback(): ?Response
    {
        $auth0 = $this->auth0();

        $auth0->exchange();

        return $this->render();
    }

cache の実装

Auth0 が Store(デフォルトセッション情報)に保持している内容を確認します。
今回の Auth0 のインスタンスを作るときの設定であれば、 $auth0->getUser() $auth0->getAccessToken() は Store に保持されており、残りは保持されていません。

src/Controller/AuthController.php
    /**
     * @return \Cake\Http\Response|null
     */
    public function cache(): ?Response
    {
        $auth0 = $this->auth0();

        debug($auth0->getUser());
        debug($auth0->getAccessToken());
        debug($auth0->getIdToken());
        debug($auth0->getRefreshToken());

        return $this->render();
    }

user の実装

Authentication API を使ってユーザー情報を取得します。
このときにログインユーザーの AccessToken を使います。

src/Controller/AuthController.php
    /**
     * @return \Cake\Http\Response|null
     */
    public function user(): ?Response
    {
        $auth0 = $this->auth0();

        /** @var string $domain */
        $domain = env('AUTH0_DOMAIN', '');
        /** @var string $clientId */
        $clientId = env('AUTH0_CLIENT_ID', '');
        $authApi = new Authentication($domain, $clientId);

        /** @var string $accessToken */
        $accessToken = $auth0->getAccessToken();
        $user = $authApi->userinfo($accessToken);

        debug($user);

        return $this->render();
    }

logout の実装

Authentication API を使ってログアウトURLを取得します。

src/Controller/AuthController.php
    /**
     * @return \Cake\Http\Response|null
     */
    public function logout(): ?Response
    {
        $auth0 = $this->auth0();

        $auth0->logout();

        /** @var string $domain */
        $domain = env('AUTH0_DOMAIN', '');
        /** @var string $clientId */
        $clientId = env('AUTH0_CLIENT_ID', '');
        $authApi = new Authentication($domain, $clientId);

        /** @var string $returnTo */
        $returnTo = env('AUTH0_LOGOUT_URL', '');
        $logoutUrl = $authApi->get_logout_link($returnTo, $clientId);

        return $this->redirect($logoutUrl);
    }

AuthController の実装は以上です。

routes の定義

AuthContoller のルートを定義します。
Code を受け取る callback URL は、Auth0 Application にセットした内容に合わせる必要があります。

config/routes.php
// ... snip
$routes->scope('/', function (RouteBuilder $builder) {
    // Register scoped middleware for in scopes.
    $builder->registerMiddleware('csrf', new CsrfProtectionMiddleware([
        'httpOnly' => true,
    ]));

    $builder->applyMiddleware('csrf');

    $builder->connect('/login', ['controller' => 'Auth', 'action' => 'login']);
    $builder->connect('/logout', ['controller' => 'Auth', 'action' => 'logout']);
    $builder->connect('/auth/callback', ['controller' => 'Auth', 'action' => 'callback']);
    $builder->connect('/auth/user', ['controller' => 'Auth', 'action' => 'user']);
    $builder->connect('/auth/cache', ['controller' => 'Auth', 'action' => 'cache']);
    // ... snip
});
// ... snip

これで

の動きを確認することができます。

補足

Auth0 でGoogleソーシャルログインをさせない

Auth0 の Applications から設定できます。

  • 左メニュー「Applications」をクリック
  • 一覧から該当する Applicationリンクをクリック
  • タブ「Connections」をクリック
  • Social > google-oauth2 を OFF にする。

Auth0 でサインアップをさせない

Auth0 の Connections から設定できます。

  • 左メニュー「Connections」 > 「Database」をクリック
  • 一覧から該当するDatabase Connections(「Username-Password-Authentication」)をクリック
  • 「Disable Sign Ups」を ON にする。

Auth0 のログイン画面を日本語にする

Auth0 の Tenant Settings から設定できます。

  • 右上ログインアカウントプルダウンメニュー「Settings」をクリック
  • Default Language:「Japanese(ja)」を選択して「SAVE」をクリック

AuthController の全体ソース

src/Controller/AuthController.php
<?php
declare(strict_types=1);

namespace App\Controller;

use Auth0\SDK\API\Authentication;
use Auth0\SDK\Auth0;
use Cake\Http\Response;

/**
 * Auth Controller
 */
class AuthController extends AppController
{
    /**
     * @return \Auth0\SDK\Auth0
     */
    private function auth0(): Auth0
    {
        return new Auth0([
            'domain' => env('AUTH0_DOMAIN', ''),
            'client_id' => env('AUTH0_CLIENT_ID', ''),
            'client_secret' => env('AUTH0_CLIENT_SECRET', ''),
            'redirect_uri' => env('AUTH0_CALLBACK_URL', ''),

            // refresh token が必要な場合は、scope へ offline_access を追加する
            // @see https://auth0.com/docs/tokens/guides/get-refresh-tokens
            'scope' => 'openid profile email',
            // 'scope' => 'openid profile email offline_access',

            // exchange (code から access token を取得) 後、ユーザー情報を取得する場合は true とする
            // access token と一緒に返却される id token(JWT) を decode したユーザー情報でよければ false (デフォルト) とする
            // 'skip_userinfo' => false,

            // access token を Store (Session情報) へ保持しない場合は false とする
            // 'persist_user' => false,

            // access token を Store (Session情報) へ保持する場合は true とする
            'persist_access_token' => true,

            // refresh token を Store (Session情報) へ保持する場合は true とする
            // 'persist_refresh_token' => true,

            // id token を Store (Session情報) へ保持する場合は true とする
            // 'persist_id_token' => true,
        ]);
    }

    /**
     * @return \Cake\Http\Response|null
     */
    public function login(): ?Response
    {
        // Session を破棄して、Auth0のユーザー情報を空にする。
        // そうしないと、Auth0->exchange() で
        // Can't initialize a new session while there is one active session already
        // が発生する。
        $this->getRequest()->getSession()->destroy();

        $auth0 = $this->auth0();

        $loginUrl = $auth0->getLoginUrl();

        return $this->redirect($loginUrl);
    }

    /**
     * @return \Cake\Http\Response|null
     */
    public function logout(): ?Response
    {
        $auth0 = $this->auth0();

        $auth0->logout();

        /** @var string $domain */
        $domain = env('AUTH0_DOMAIN', '');
        /** @var string $clientId */
        $clientId = env('AUTH0_CLIENT_ID', '');
        $authApi = new Authentication($domain, $clientId);

        /** @var string $returnTo */
        $returnTo = env('AUTH0_LOGOUT_URL', '');
        $logoutUrl = $authApi->get_logout_link($returnTo, $clientId);

        return $this->redirect($logoutUrl);
    }

    /**
     * @return \Cake\Http\Response|null
     */
    public function callback(): ?Response
    {
        $auth0 = $this->auth0();

        $auth0->exchange();

        return $this->render();
    }

    /**
     * @return \Cake\Http\Response|null
     */
    public function user(): ?Response
    {
        $auth0 = $this->auth0();

        /** @var string $domain */
        $domain = env('AUTH0_DOMAIN', '');
        /** @var string $clientId */
        $clientId = env('AUTH0_CLIENT_ID', '');
        $authApi = new Authentication($domain, $clientId);

        /** @var string $accessToken */
        $accessToken = $auth0->getAccessToken();
        $user = $authApi->userinfo($accessToken);

        debug($user);

        return $this->render();
    }

    /**
     * @return \Cake\Http\Response|null
     */
    public function cache(): ?Response
    {
        $auth0 = $this->auth0();

        debug($auth0->getUser());
        debug($auth0->getAccessToken());
        debug($auth0->getIdToken());
        debug($auth0->getRefreshToken());

        return $this->render();
    }
}
3
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?