はじめに
この記事はOPENLOGI Advent Calendar 2022の9日目の記事です。
業務と全く関係ないのですが、今回は個人的に気になっていたSSOの具体的な流れについて手元で環境を作り処理を追っていきたいと思います。
Auth0やSSO、またその他利用しているツール類の説明は、詳しく解説されてる記事がありますので割愛させていただきます。
概要
SSOの分かりやすいフロー図がAuth0 blogにありますので、Auth0を使ったSSOの1〜13までの流れを確認します。
実際に利用する際は意識する必要はない項目もありそうですが、今回はざっと見ていければと思います。
確認準備
1. ローカル環境作成
SSOの確認なのでまずは2環境作っていきます。
作成は以下のコマンドをport番号違いで2回叩くだけです。
$ php -S localhost:8888
PHP 8.1.11 Development Server (http://localhost:8888) started
2. 外部からローカル環境へアクセスできるようにする
ngrokを利用します。
service1と2は先ほど立ち上げたローカル環境をポート番号違いで紐づけてます。
$ ngrok start service1 service2
ngrok (Ctrl+C to quit)
Try our new native Go library: https://github.com/ngrok/ngrok-go
Session Status online
Account terumichi1209@gmail.com (Plan: Free)
Version 3.1.0
Region United States (us)
Latency -
Web Interface http://127.0.0.1:4040
Forwarding https://6fef-60-61-127-42.ngrok.io -> http://localhost:8888
Forwarding https://c78d-60-61-127-42.ngrok.io -> http://localhost:8889
これで外部からアクセスできる2つのローカル環境が作成できました。
3. Auth0へ生成したドメインを反映する
Auth0管理画面にて、ご自身の環境でアプリを以下のように2つ作成します。
データベースを利用せず、ソーシャルログインだけの場合はこれでAuth0側は準備完了です。
4. Auth0のアプリ情報を設定する
サービスごとにディレクトリを切っただけのアプリを用意します。
index.php
はAuth0クイックスタートのサンプルコードの寄せ集めになります。
ドメインやパス、Auth0のアプリ情報を変えて、2つの index.php
に設定します。
<?php
use Steampixel\Route;
const ROUTE_URL_INDEX = 'https://f272-60-61-127-42.ngrok.io/service1';
const ROUTE_URL_LOGIN = ROUTE_URL_INDEX . '/login';
const ROUTE_URL_CALLBACK = ROUTE_URL_INDEX . '/callback';
const ROUTE_URL_LOGOUT = ROUTE_URL_INDEX . '/logout';
require '../vendor/autoload.php';
$auth0 = new \Auth0\SDK\Auth0([
'domain' => 'Auth0で作成したアプリのdomain',
'clientId' => 'Auth0で作成したアプリのclientId',
'clientSecret' => 'Auth0で作成したアプリのclientSecret',
'cookieSecret' => 'test',
]);
Route::add('/service1', function() use ($auth0) {
$session = $auth0->getCredentials();
if ($session === null) {
echo '<p>Please <a href="' . ROUTE_URL_LOGIN . '">log in</a>.</p>';
return;
}
echo '<pre>';
print_r($session->user);
echo '</pre>';
echo '<p>You can now <a href="' . ROUTE_URL_LOGOUT . '">log out</a>.</p>';
});
Route::add('/service1/login', function() use ($auth0) {
$auth0->clear();
header("Location: " . $auth0->login(ROUTE_URL_CALLBACK));
});
Route::add('/service1/callback', function() use ($auth0) {
$auth0->exchange(ROUTE_URL_CALLBACK);
header("Location: " . ROUTE_URL_INDEX);
});
Route::add('/service1/logout', function() use ($auth0) {
header("Location: " . $auth0->logout(ROUTE_URL_INDEX));
});
Route::run('/');
以上で設定は完了ですので実際に見ていきましょう。
結果
1. Browses to
サービス1にアクセスします。
2. Redirects to
ログインボタンを押すとユニバーサルログイン画面(Auth Server)にリダイレクトします。
3. Enter user logs in, or cookie is avaliable
ここではまだログインした記録となるCookie情報はないため、ユーザ情報をユニバーサルログイン画面上で入力(またはソーシャルログインなど)します。
4. Store Cookie
入力したユーザ情報がAuth0が保持するユーザと一致(もしくは新規作成に成功)した場合、Auth0側の認証はOKなのでその旨をCookieに保持します。
5. Sends token and redirects
この後の認証に必要な code(token)
やstate
が付与されてAuth0アプリで設定したcallback先にリダイレクトします。
6. Uses Token to Authenticate
codeを利用して認証します。
ライブラリ内で良しなにやってくれてますね。
https://github.com/auth0/auth0-PHP/blob/main/src/Auth0.php#L300
付与されてるパラメータを利用してaccess_tokenやid_token(jwt)を生成します。
認証後に取得できるトークンは復号することで、どういったデータ持ってるかの参考になります。
7. Stores domain1 Cookie
アプリ側では以上で無事ログインできたので、ユーザ情報が出力できました。
先ほど確認したトークン内容がそのまま出てることが分かります。
8. Browsers to
次にドメインを変えてサービス2にアクセスします。
9. Redirects to
ログインボタンを押して、Auth Serverにリダイレクトします。
10. Enter user logs in, or cookie is avaliable
今度はサービス1のフローで登録したCookieを保持しているため、どのユーザかAuth0上では特定できてます。
そのため、入力を省略して次のステップへ進みます。
11. Sends token and redirects
同じようにcode(token)
やstate
が付与されてサービス2へリダイレクトします。
12. Uses Token to Authenticate
省略
13. Stores domain1 Cookie
サービス1と同じようにサービス2のドメインでCookieが作成されていることが分かります。
最後に
いかがでしたでしょうか?
図で見るだけより実際に動かした方が理解できて(特にトークン生成周り)、記事を書いて良かったと思います。
ライブラリ内深掘りするともう少しありますが、冒頭の図の流れは追えたので今回はこの辺りで終わります。
今回の実験用に作成したコードはGithubへ上げてますので、興味がある方は手元の環境でもお試しいただけます。
https://github.com/terumichi1209/auth0_sso_sample/