11
5

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 3 years have passed since last update.

Auth0のCustom SignUpをLaravelで実装してみた

Last updated at Posted at 2020-01-19

はじめに

最近Auth0を使い始めまして、久しぶりに素晴らしいサービスに出会えたと感謝している毎日です。
その中で、Auth0を使って少々詰まったユースケースがあったのでご紹介しようと思います。

実装例にはLaravelを用いていますが、ポイントはどの言語でも変わらないと思われます(たぶん)。

概要

Auth0は認証を中心としたエコシステムを提供しているマネージドサービスになります。
ユーザーの登録、ログイン、ログアウト、メール検証、二段階認証など様々なユースケースに対応しています。

僕がAuth0を使って実現したかったのは、SignUp時の追加情報入力フローです。
というのも、Auth0のデフォルトのSignUpでは、Emailとパスワードか、Social Providerに登録済みの情報しか取得することができません。
アプリケーションによっては足りない情報の入力を促したり、SignUp時に必須で取得したい情報が存在すると思います。
そのようなSignUp時の追加情報入力フローをAuth0 Rulesを使って実装してみました。

Custom SignUpのフロー

Auth0でCustom SignUpを実現するにはいくつか方法が存在します。
公式ドキュメントに記載されてはいる方法だと以下の3つです。

  1. Auth0のSignUpページをカスタマイズする
  2. 自分でログインページを作成しホスティングする
  3. 別ページにRedirectさせる
  4. 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のフローを図にすると以下のような感じです。
image.png

図の7.でAuth0 Rulesを使って、Custom SignUpページにRedirectされています。
この時、認証は一度中断されるため、ここでユーザーが離脱した場合には、認証状態はなりません。
(実際には6.の時点で、Auth0上にユーザーが作成されるようですが)

認証フローを再開する場合には、https://{YOUR_AUTH0_DOMAIN}/continue?state={STATE} にリクエストを送る必要があります。
(stateパラメーターについては、7.でRedirectされる際に、URLのクエリパラメーターにくっついてきます。)

認証フローが再開し、その後の処理が無事終了すると、認証済みになり、トークンが発行され、ログイン状態になります。

Rulesの作成

実際にRulesを作成してみます。
既にAuth0のアカウントは作成済みという前提で進めます。

  1. Auth0のDashboardのサイドメニューにあるRulesをクリックします。
  2. CREATE RULE をクリックし、Empty ruleを選択します
  3. 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.protocolredirect-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で定められているから、大丈夫だと思うんだけど...一応)。

11
5
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
11
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?