はじめに
最近Auth0を使い始めまして、久しぶりに素晴らしいサービスに出会えたと感謝している毎日です。
その中で、Auth0を使って少々詰まったユースケースがあったのでご紹介しようと思います。
実装例にはLaravelを用いていますが、ポイントはどの言語でも変わらないと思われます(たぶん)。
概要
Auth0は認証を中心としたエコシステムを提供しているマネージドサービスになります。
ユーザーの登録、ログイン、ログアウト、メール検証、二段階認証など様々なユースケースに対応しています。
僕がAuth0を使って実現したかったのは、SignUp時の追加情報入力フローです。
というのも、Auth0のデフォルトのSignUpでは、Emailとパスワードか、Social Providerに登録済みの情報しか取得することができません。
アプリケーションによっては足りない情報の入力を促したり、SignUp時に必須で取得したい情報が存在すると思います。
そのようなSignUp時の追加情報入力フローをAuth0 Rulesを使って実装してみました。
Custom SignUpのフロー
Auth0でCustom SignUpを実現するにはいくつか方法が存在します。
公式ドキュメントに記載されてはいる方法だと以下の3つです。
- Auth0のSignUpページをカスタマイズする
- 自分でログインページを作成しホスティングする
- 別ページにRedirectさせる
- Progressive profilingをする
各方法について見ていきます。
1. Auth0のSignUpページをカスタマイズする
1.のやり方はAuth0がホスティングしているセントラル認証サーバー上で入力する項目をカスタマイズすることができます。
Auth0の提供しているLockというライブラリを使えば簡単に項目を追加することができます。
しかし、複数アプリケーションから利用されるようなユースケースの場合だと、アプリケーション固有の項目を入れるとなると、かなり複雑な処理を書く必要が出てきます。
また、Social ProviderでSignUpされた場合には、対応することができないので注意が必要です。
2. 自分でログインページを作成しホスティングする
Lockでは、求めるUI/UXを実現できない!となった場合は、自分でログインページを作成することも可能です。その場合、Auth0のAPIを用いて、認証を行うことになります。
自分でUIを作成してしまえば、複雑な認証プロセスをいくらでも実現することが可能となります。
しかし、Auth0が用意している認証エコシステムが一部利用不可能になったり、実装コストがかかってしまうというデメリットも抱えています。
ちなみに、LockとCustom UIについては、公式ドキュメントで比較されているので、ご興味がある方は参照してみてください。
3. 別ページにRedirectさせる
今回採用したのはこの方法です。
Auth0のホスティングするログインページでSignUp後、自分がホスティングする追加情報入力ページにリダイレクトさせ、情報を入力してもらうという方法です。
この方法を用いると、Auth0の機能をふんだんに使いつつ、自分のアプリケーション用にカスタマイズされた追加項目の入力をユーザーに求めることが可能です。
また、Social Provider経由のSignUpでもEmail/PWでのSignUpでも、同じフローで追加項目の入力を求めることが可能になります。
これの実現方法についても。公式ドキュメントにかかれています。
4. Progressive profilingをする
SignUp時には最低限の情報しか求めず、次回ログイン時や、アクセス時に追加で情報を求めるというのが、Progressive profilingです。
名前もかっこいいし、登録率も増えそうな方法ですが、UX設計のハードルが少々高そうだったので、今回は見送りました。
Progressive profilingの例もAuth0の公式ドキュメントで提供されています。
ご興味がある方はご参照ください。
RedirectでCustom SignUp
Auth0でSignUp中にRedirectする場合には、Auth0 Rulesというサービスを使います。
Auth0 Rulesは認証トークンを発行する前に呼び出されるサーバレスな関数だと思って頂ければよいかと思います。
Rulesを使ったRedirectのSignUpのフローを図にすると以下のような感じです。
図の7.
でAuth0 Rulesを使って、Custom SignUpページにRedirectされています。
この時、認証は一度中断されるため、ここでユーザーが離脱した場合には、認証状態はなりません。
(実際には6.
の時点で、Auth0上にユーザーが作成されるようですが)
認証フローを再開する場合には、https://{YOUR_AUTH0_DOMAIN}/continue?state={STATE}
にリクエストを送る必要があります。
(state
パラメーターについては、7.
でRedirectされる際に、URLのクエリパラメーターにくっついてきます。)
認証フローが再開し、その後の処理が無事終了すると、認証済みになり、トークンが発行され、ログイン状態になります。
Rulesの作成
実際にRulesを作成してみます。
既にAuth0のアカウントは作成済みという前提で進めます。
- Auth0のDashboardのサイドメニューにあるRulesをクリックします。
-
CREATE RULE
をクリックし、Empty rule
を選択します - Scriptに以下のコードを貼り付けます
function (user, context, callback) {
const hasConsented = user.app_metadata && user.app_metadata.has_consented;
if (!hasConsented && context.protocol !== 'redirect-callback') {
// urlはアプリケーションのURLに差し替える
context.redirect = { url: 'http://localhost:3000/signup' };
}
if (!hasConsented && context.protocol === 'redirect-callback') {
user.app_metadata = { has_consented: true };
user.user_metadata = context.request.body;
auth0.users.updateAppMetadata(user.user_id, user.app_metadata);
auth0.users.updateUserMetadata(user.user_id, user.user_metadata);
}
callback(null, user, context);
}
Saveすると準備完了です。
6行目のcontext.redirect = {url }
でRedirect先のURLを指定しています。
2つ目のifの中身は https://{YOUR_AUTH0_DOMAIN}/continue?state={STATE}
にリクエストが送られた後の処理になります。
認証プロセスが再開されると、 context.protocol
にredirect-callback
という値が挿入されます。
ここで、Custom Signupが完了しているユーザーなのか判別するために、app_metadata
に{ has_consented: true }
という値を入れています。
Laravel Application側
Redirectの受け先のLaravel Applicationを作成します。
既にLaravelはセットアップ済みという前提で進めます。
(セットアップがまだの方はこちらを参考にしてください。)
<?php
# routes/web.php
Route::get('/signup', SignupController@main)->name('signup');
Route::post('/signup', SignupController@signup);
<?php
# app/Http/Controllers/SignupController.php
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
class SignupController extends Controller
{
public function main(Request $request) {
$state = $request->input('state');
// formにstateの値をhiddenで入れる
return view('signup', ['state' => $state]);
}
public function signup(Request $request) {
$state = $request->input('state');
$config = config('laravel-auth0');
$url = 'https://'.$config['domain'].'/continue?state='.$state;
return redirect($url, '307');
}
}
ここでの注意点は、 /continue
にRedirectさせる時に、 redirect($url, '307')
とすることです。
第2引数はHttpステータスコードを指定しています。
laravelのredirect()
はデフォルトでは302
でリダイレクトされるのですが、302
だとリダイレクトする際にブラウザ側で勝手にGETリクエストに変換されてリクエストが送信されてしまうので、POSTのBodyが全てなくなってしまいます。
そこで、Httpステータスコードを307
でリダイレクトさせることで、POSTのままリダイレクト先にリクエストを送信でき、Bodyの情報も無事渡すことが可能になります。
signup()
でリダイレクトされた後は、再びAuth0のRulesが発火されるといった寸法です。
ここで、無事認証が終了すると指定したコールバックURLにトークン付きでリダイレクトされ、ログイン状態になるといった寸法です。
まとめ
Laravelを用いてAuth0のCustom Signupを実装してみました。
Custom Signupを利用することで、アプリケーション固有の入力項目にも対応することができ、かつ、Redirectの場合であれば、Social ProviderでSignupしたユーザーにも対応することができるようになりました。
少しの手間で、Custom Signupを実現することができ、Auth0の凄さには驚きを隠せません。
ただ、認証再開時の実装については、あまり文献がなかったので、見事にハマってしまいましたが、良さげな解決法が見つかってよかったです。
しかし、Httpステータスコードの307
に対応していないブラウザ(特にモバイルが心配)もある可能性もあり、そこについてはまだ調査していないので、実際に利用する際にはご注意ください...。
(RFCで定められているから、大丈夫だと思うんだけど...一応)。