【資料公開】Auth0ハンズオンウェビナー – 基本編

認証認可プラットフォーム「Auth0」のハンズオンウェビナーの解説付きの資料です。基本編と拡張編の2パート・全10ステップに分けて、段階的にAuth0を学んでいくことができます。ぜひ手を動かしながらお読みいただけると幸いです。本記事は基本編です。
2020.03.11

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

はじめに

2020年3月に、認証認可プラットフォーム「Auth0」のハンズオンウェビナーを社内向けと社外向け、それぞれ開催しました。特に社内向けではクラスメソッドではAuth0について興味を持つ社員が多く、ウェビナー参加者は2日あわせて70人前後となりました。

社外向けのAuth0ハンズオンセミナーは毎月開催していますが、今回はウェビナー用に新規に作り直しました。以前よりも少しハードルを上げた構成としましたが、ほとんどの方が付いてこれた様子で、結果としてはいい感じの難易度設定となりました。

オンサイトでのイベントが規制されている昨今ですので Auth0に興味のある方々が、本資料を元にウェビナーを開催できたらいいな と言う想いで、ウェビナー資料を解説付きで公開したいと思います。ぜひ参考にしてみてください。

ウェビナーは 基本編拡張編 の2部構成(各1時間)にしました。ちょっとずつやるも良し、まとめて一気にやってみるのも良しです。段階的に学んでいける内容になっていますので、ぜひ手を動かしながらお読みいただけると幸いです。

本記事では、まず基本編を解説していきます。拡張編は以下をご覧ください。

ウェビナー資料

ウェビナー資料はSpeaker Deckにアップロードしました。あわせてご覧ください。

1. テナントの作成

Auth0を利用する上で初めて行う作業が、アカウントの作成とテナントの作成です。アカウントの作成は以下のURLにアクセスして行います。

アカウントを作成する流れの中で、新しいテナントを1つ作成します。1つのアカウントにつきテナントは何個でも作成できるので、1つ目のテナントはまず試す用のテナントとすると良いでしょう。

テナント名はランダムで自動入力されていますが、自分で好きな文字列にして構いません。ただし、全世界でユニークでなければいけません。

また、リージョンは以下の3つの中から選択します。特にこだわりが無ければ US (アメリカ) で良いでしょう。

表示名 リージョン
AU オーストラリア
EU ヨーロッパ
US アメリカ

テナントとアカウントの関係

以下はアカウントとテナントの関係性を表現した、簡単な図です。

テナントは、認証基盤そのものを表します。さらに、そのテナントの中に認証基盤にログインするアプリケーション(Webアプリ、モバイルアプリなど)の情報を追加していきます。このテナントを、アカウントを使って管理します。

1つのアカウントは、複数のテナントを管理することができます。AWSを知っている人はSwitch Roleのような感覚と思っていただけるとしっくり来ると思います。

テナント設定を行う「Auth0 Dashboard」

アカウントとテナントの作成が完了すると、Dashboardが表示されます。テナントの各設定は、このDashboardから行います。

2. SPA (Single Page Application) の認証

Webアプリ開発で最近耳にする機会が多い SPA (Single Page Application) に、ログイン機能を埋め込んでみましょう。

SPA (Single Page Application) は単一のWebページでアプリのような振る舞いを作るための技術です。一度Webページを読み込んだ後は、JavaScript内で完結されるので軽快に動作するなどといった特徴があります。現代のWebアプリの主流です。

Auth0では、このようなSPAに対してログイン機能をかんたんに導入できるようになっています。

auth0-spa-js による認証の仕組み

Auth0のログイン機能をSPAに導入するには auth0-spa-js というSDKを利用する方法がオススメです。このSDKにはAuth0が考える、SPAの認証のベストプラクティスが詰め込められています。

採用されているフローは Authorization Code Flow with Proof Key for Code Exchange と言います。長い名前ですが、ここでは要点だけを解説します。下記の図を見ながら、順々に追っていきましょう。

① ログインボタンを押す

まずWebアプリのどこかにログインボタンを仕込みます。Webアプリ側にはいわゆるログインフォームは置きません。Webアプリにはログインをするためのボタンだけを置き、ログインページも含むログイン機構はほぼAuth0にお任せする形にします。

② Auth0のログインページにリダイレクトする

Auth0がホスティングするログインページは「Universal Login」と言います。ログインボタンをクリックした後はUniversal Loginのページにリダイレクトします。なお、実際に遷移する処理はSDKに内包されているので、Webアプリ側ではメソッドを呼ぶだけで済みます。

③ ログイン処理

Universal Loginに遷移したあとは、Auth0の中で完結する世界になります。ログインする手段としてID/パスワード認証であればフォームに入力、Googleでログインしたい場合は「Sign in with Google」のボタンを押すだけです。

どのようなログイン手段を提供するかは、Auth0のテナントに設定します。その設定値によってログイン画面の表示内容が変わってきます。例えばGoogleでログインする機能をオフにすれば、ログイン画面に「Sign in with Google」のボタンは表示されなくなります。

④ 元のWebアプリにリダイレクト

③でログインが完了したら、元のWebアプリにリダイレクトされます。リダイレクト時には Authorization Code と呼ばれる一時的なコードが渡されます。ログイン完了後はIDトークン(またはアクセストークン)を入手したいわけですが、Authorization Codeはこれらのトークンの引換券のようなものです。リダイレクト後、Authorization Codeを受け取ったWebアプリ内でIDトークン(またはアクセストークン)を入手する処理を行います。

PKCE (Proof Key for Code Exchange) とは

以上が認証フローの要約です。このフローがいわゆるAuthorization Code Flowですが、このフローを攻撃者から守るために生まれたのが Authorization Code Flow with Proof Key for Code Exchange です。Proof Key for Code Exchangeは、略してPKCE (ピクシー)とも呼ばれます。

Authorization Code Flowには、④の元のWebアプリにリダイレクトする際に なりすましてAuthorization Codeを横取りする ことが技術的に可能である、という問題があります。例えばモバイルアプリの場合はリダイレクト先はURL Schemeを指定する形となりますが、このURL Schemeは野良アプリにも設定することができます。そのためユーザーのスマホに悪意のあるアプリがインストールされている場合、正常なアプリでログインしようとしているはずなのに、途中から悪意のあるアプリに変わってしまいます(しかもログインもできてしまっている状態)。

そこでAuthorization Code FlowにPKCEという考え方が加わります。PKCEは 認証リクエスト元とログイン後のリダイレクト先が同じであるかどうかを確認する ための仕組みになります。まず認証リクエストを実行する前に Code Verifier というランダム値を生成します。この値をAuth0に対して認証リクエストを行う際に渡しておきつつ、リダイレクト後のIDトークン(またはアクセストークン)を引き換える際に再度渡します。このようにすることで、認証リクエスト時とトークン引き換え時が同じアプリから呼び出されたものである証明ができるようになります。

Authorization Code Flow with Proof Key for Code Exchangeセキュリティリスクを軽減するため少し複雑になっていますが、このフローに準拠する処理が auth0-spa-js には内包されています。端的に言うと「auth0-spa-js を使えばいいだけ」な訳ですが、どういったフローで動いているのかざっくりでも知っておくと、なぜ推奨されるのか・なぜ使うべきなのかが理解できてくると思います。

サンプルアプリケーションでログインしてみる

サンプルアプリケーションでのログイン手順については、別途手順を解説するブログを公開済みです。こちらを参考にサンプルアプリケーションを動かしてみてください。

この記事に一点だけ補足します。Applicationの設定で Allowed Callback URLs を指定する必要があります。こちらは Authorization Code Flowの中で認証完了後にリダイレクトすることができるURLのホワイトリスト になります。このホワイトリストに入っていないURLを設定した場合、そもそも認証リクエスト時にエラーになります。設定しないとログインできませんので、理解した上で設定を行いましょう。

3. コネクションとは?

コネクションとは、ログインするための手段そのものを指します。具体的には「IDとパスワードでログインする」「Googleアカウントでログインする」「LINEアカウントでログインする」などといったような形です。

コネクションはいくつかの種類に分けられています。

種類 説明
Database Connection Auth0が管理するデータベース、または独自のデータベースに接続する方式。
Social Login ソーシャルプロバイダとシングルサインオンにて接続する方式。
Enterprise Directory Enterprise向け製品と連携して接続する方式。
Passwordless System SMSやEメールを使って、パスワードレスで接続する方式。

コネクションとユーザー

Auth0を使ってログインを行うと、テナント内にユーザー情報が作成されます。このユーザー情報はコネクション単位で作成されます。例えばLINEでログインした場合はLINEユーザー、Facebookでログインした場合はFacebookユーザーとして作成される、といった具合です。

1人が異なるコネクションでログインしてしまった場合は別々のユーザー情報が作成されてしまいますが、Auth0にはアカウントリンクという機能により名寄せすることができます。こちらは後述している手順により、非常に簡単に実現できるようになっています。

コネクションとアプリケーション

Auth0テナントにログイン可能なアプリケーション(Webアプリやモバイルアプリなど)は複数作成できますが コネクションのON/OFFはアプリケーションごとに設定できます。 例えば「LINEのアプリ内ブラウザからアクセスされる場合はLINEでログインできるようにしたいが、Google Chromeなどのようなブラウザからアクセスされる場合はさせたくない」といったようなユースケースがあり得ます。あるいは「管理画面へのログインのみAD連携したい」などといったユースケースもあるかも知れません。各コネクションはアプリケーションごとにON/OFFを決められるので、そのようなユースケースにも柔軟に対応できます。

作成済みのSPA向けのアプリケーション設定を変更し、コネクションのON/OFFを試してみましょう。Dashboardから Applications を開き、先ほど作成したアプリケーションを選択し、 Connections タブを開きます。

アプリケーション作成時は、Auth0上でIDとパスワードを保管する Username-Password-Authentication とGoogleでログインする google-oauth2 がONになっています。このうち google-oauth2 のフリップをクリックしてOFFにしてみます。

サンプルアプリケーションで再ログインすると「Sign In with Google」ボタンがログインフォームから消えていることが確認できます。

コネクションと料金

Auth0はユーザー数によって費用が決まりますが、1ユーザーあたりの金額はコネクションの種類によって変わってきます(また、利用用途との組み合わせ)。そのため利用開始時には「どのような用途で」「どのコネクションで」「どのくらいの人数」で利用するか把握しておく必要があります。

また下記の記事では、コネクションの種類と利用用途の組み合わせを解説しています。あわせてお読みいただけるとより理解できてくると思います。

4. ユニバーサルログイン

ユニバーサルログインは、Auth0が提供するログインページのことです。

Auth0がホスティングしてくれるので、ログインページを自前で実装する必要が無くなります。また、前述の通りAuth0のテナント設定を動的に反映するので、コネクションを増やしたくなった際も、Webアプリなどの改修を行う必要がなく、Auth0のテナントの設定を更新だけで済みます。

ClassicとNew Experience

Universal Loginには2つの種類のUIが提供されています。名前を ClassicNew Experience と呼びます。

Classicは古くから使われているUIで、かなり柔軟にUIをカスタマイズできることが特徴です。一方、New Experienceは2019年にリリースされたばかりの新しいUIです。New Experienceでは「ログインの体験」がより良くなるように様々な改善点がありますが、Classicほど柔軟にカスタマイズできない点があります。

カスタマイズ性にどのくらいの違いがあるかを説明します。まずClassicはHTMLファイルを修正することができるので、例えば「スタイルをガラッと変えたい」「ヘッダーを追加したい」「リンクを増やしたい」などの要件にも対応できます。New ExperienceはAuth0 Dashboardで行える設定、またはAuth0 Management APIを使ったロゴや色、文言の修正などが行えるようになっています。New Experienceをカスタマイズするための機能は今後増えていく予定ですが、現状だと要件によってはClassicでないと実現できない可能性があります。

Auth0 Lock

ユニバーサルログインを理解する上で欠かせないのが Auth0 Lock です。

Auth0 LockはログインウィジェットをWebページの好きな箇所に埋め込むことができるUIライブラリです。どうしてもWebアプリの中にログインフォームを埋め込みたい場合は、Auth0 Lockを使うことで実現することができます。Auth0 Lockも、内部ではテナント情報を読み込んでUIを切り替えてくれるので、Webアプリへの埋め込みと動的なUI変更が実現できるようになっています。また、設定情報を加えていくことでUIを変更できます。

なお、ClassicのログインページはAuth0 Lockを用いて実装されています。

ユニバーサルログインのカスタマイズ

ユニバーサルログインはClassicをベースとしたHTMLファイルをコードでカスタマイズすることができます。Dashboardの左メニューより Universal Login を選び、Login のタブを開きます。Customize Login Page のフリップをクリックし、ログインページのカスタマイズをONにします。

ソースコードは次のようになっています。ソースコードを読んでみると、Auth0から取得した設定値(config)を使ってAuth0 Lock(lock)の挙動を制御していることが分かります。下記のコードはソースコードのJavaScriptの部分になります。

// Decode utf8 characters properly
var config = JSON.parse(decodeURIComponent(escape(window.atob('@@config@@'))));
config.extraParams = config.extraParams || {};
var connection = config.connection;
var prompt = config.prompt;
var languageDictionary;
var language;

if (config.dict && config.dict.signin && config.dict.signin.title) {
  languageDictionary = { title: config.dict.signin.title };
} else if (typeof config.dict === 'string') {
  language = config.dict;
}
var loginHint = config.extraParams.login_hint;
var colors = config.colors || {};

// Available Lock configuration options: https://auth0.com/docs/libraries/lock/v11/configuration
var lock = new Auth0Lock(config.clientID, config.auth0Domain, {
  auth: {
    redirectUrl: config.callbackURL,
    responseType: (config.internalOptions || {}).response_type ||
      (config.callbackOnLocationHash ? 'token' : 'code'),
    params: config.internalOptions
  },
  /* additional configuration needed for custom domains
  configurationBaseUrl: config.clientConfigurationBaseUrl,
  overrides: {
    __tenant: config.auth0Tenant,
    __token_issuer: 'YOUR_CUSTOM_DOMAIN'
  }, */
  assetsUrl:  config.assetsUrl,
  allowedConnections: connection ? [connection] : null,
  rememberLastLogin: !prompt,
  language: language,
  languageDictionary: languageDictionary,
  theme: {
    //logo:            'YOUR LOGO HERE',
    primaryColor:    colors.primary ? colors.primary : 'green'
  },
  prefill: loginHint ? { email: loginHint, username: loginHint } : null,
  closable: false,
  defaultADUsernameFromEmailPrefix: false,
  // uncomment if you want small buttons for social providers
  // socialButtonStyle: 'small'
});

if(colors.page_background) {
  var css = '.auth0-lock.auth0-lock .auth0-lock-overlay { background: ' +
              colors.page_background +
            ' }';
  var style = document.createElement('style');

  style.appendChild(document.createTextNode(css));

  document.body.appendChild(style);
}

lock.show();

Auth0 Lockに設定可能な値は下記にまとまっていますので、こちらを参考に表示を制御してみてください。

ユニバーサルログインを選ぶか、Auth0 Lockを選ぶか

ログインフォーム、あるいはログインページを作る上で様々な選択肢があって魅力的ですが、実際に組み込む際には 自分たちのシステムの場合、どれが合うんだろう? と悩んでしまいます。ここでは選定ロジックを1つ紹介してみたいと思います。

まずはじめに検討する事項は Webアプリに埋め込む必要があるか、ないか についてです。Webアプリに埋め込む場合は、Webアプリを利用する一連の体験の中に組み込みやすいのです。しかしながら複数のWebアプリが存在する場合には、それぞれに埋め込む必要が出てきます。

ユニバーサルログインを使った場合はWebアプリにはログインボタンを置くだけになるので、複数のWebアプリへのログイン機能の組み込みが非常にかんたんになります。ログインの機能一式、より手離れが良くなるというわけです。

ということで、埋め込む必要が本当にあるかどうかを良く考えた上で、埋め込む必然性がない場合はユニバーサルログインを使う方が良いでしょう。

次にユニバーサルログインの種類ですが、前述の通りClassicまたはNew Experienceになります。New Experience のカスタマイズ性で要件が実現できるかどうかをまず調査しましょう。その上で、New Experienceで十分満たせるのであればNew Experienceを使い、よりカスタマイズが必要な場合はClassicを使うようにしましょう。

ClassicまたはNew Experienceのどちらを使う場合でも、あまりカスタマイズしすぎないことがポイントです。カスタマイズを少なくすることで、Auth0のサービスにアップデートが合った際に最小限のコストで追従することができるようになります。

Auth0の公式ドキュメントにも「Lock vs. Custom UI」というページがありますので、あわせてご覧ください。

5. Rules

Rulesは Auth0のコア機能の一つ です。Auth0は拡張性が高いと良く言われますが、その拡張性はRulesによって実現されています。

Rulesとは、ユーザーがログインするとき(正確にはトークンがリフレッシュされるとき)に発動させることができる関数(Function)です。テナント内に複数設定することができ、Dashboardから実行される順番の変更やRuleごとのON/OFFの切り替えなどが行えるようになっています。

ポイントは 100以上のテンプレートがすぐに使える ところです。テンプレートですので、テンプレートをベースに独自のコードを書き加えることができます。認証・認可で拡張したい箇所は自ずと似てくる ところがありますので、「テンプレートをそのまま使う」あるいは「テンプレートを少し書き換える」だけで要件が満たせることが多いです。もちろん、1から独自のRuleを作ることもできます。

Auth0エンジンの中でのRules

Auth0での認証のフローがどのように行われているのか、こちらのブログに「Inside the Auth0 Engine」としてまとまっています。

Rulesの箇所を見てみましょう。IDトークンとアクセストークンがクライアントに渡る前に発動し、設定されたRuleがパイプのように動作し、変更されたIDトークンとアクセストークンを渡していることが分かります。Rulesを使って何らかの機能を拡張する時には、この図ようにRulesの機能をパイプとして見立てながら考えていくと良いと思います。

また、Rulesの図の上部に記載されている「OPTIONAL REDIRECT」も便利な機能です。Ruleの中でリダイレクトオプションを設定することで、ログイン後に外部のWebページに飛ばすことができます。これは例えばパスワードの強制的な変更、カスタムのMFA、カスタムのコンセントの同意などをユーザーに強制させることができます。

テンプレートを使ってみる

テンプレートを使って、簡単なRuleを作成してみましょう。「特定のドメインのメールアドレスのみログインを許可する」という機能を拡張してみます。

Dashboardからの左メニューから Rules を選び CREATE RULE をクリックします。テンプレートが表示されるので Access Control セクションにある Email domain whitelist を選択します。

コードが表示されます。8行目の whitelist の配列に、ログインを可能にするドメインを設定します。SAVE CHANGES をクリックして完了です。

function (user, context, callback) {

  // Access should only be granted to verified users.
  if (!user.email || !user.email_verified) {
    return callback(new UnauthorizedError('Access denied.'));
  }

  const whitelist = ['example.com', 'example.org']; //authorized domains
  const userHasAccess = whitelist.some(
      function (domain) {
        const emailSplit = user.email.split('@');
        return emailSplit[emailSplit.length - 1].toLowerCase() === domain;
      });

  if (!userHasAccess) {
    return callback(new UnauthorizedError('Access denied.'));
  }

  return callback(null, user, context);
}

サンプルアプリケーションで再ログインを行います。再ログインを行う際の注意点として、上記のRuleはメールアドレスの確認が取れているユーザーのみが許可されています(4行目)。メールアドレス/パスワードで登録したあと、メールアドレス宛にメールアドレス確認メールが届いているので、メール本文内のリンクをクリックしてから試してください。リンクを踏んで確認が取れた状態になれば email_verifiedtrue になります。

ホワイトリストに含まれるドメインのメールアドレスではログインに成功し、ホワイトリストに含まれていないドメインのメールアドレスはログインに失敗するはずです。ログインに失敗した際、サンプルアプリケーションの表示上は少し分かりづらくなっています(エラーメッセージは表示されず、ログインができていない状態になる)。

Dashboardの Logs を確認すると、ログインエラーが発生し、ユーザーのログインを制限できていることが確認できます。

まとめ

基本編でAuth0のどの機能を扱うか悩みました。Auth0の考え方を機能に触れながら理解する ことに配慮しながら、取り上げる機能をピックアップして解説してみました。ほかにも触れていない便利な機能がありますので、ぜひ調べて試して使っていただければと思います。

本記事はウェビナーが開催できるような資料の公開が目的ですが、ウェビナーならずともぜひ試していただけると幸いです。