Skip to content

External User Verification with Forms

External User Verification with Forms の翻訳です。

アプリケーションによっては、ユーザーがシステムにログインできるようになる前に、外部サービスで本人(または資格)確認を行う必要があります。例えば、SaaS の無料トライアルで招待コードを要求するケース、不動産掲載サイトで住所確認を行うケース、あるいは医療・金融サービスのようにより厳格な検証が必要なケースなどが考えられます。

Auth0 は最近、サインアップ時にユーザーから追加情報を収集できるようにするため Custom Prompts をリリースしました。これらのフィールドは取得でき、さらに Pre Registration Action Flow で追加の処理を行えます。ただし、情報を取りすぎたり(あるいは条件付きで情報を取得したり)すると、エンドユーザー体験に影響が出る可能性があります。そこで Auth0 は最近、ユーザー情報の収集、ワークフロー実行、イベントデータのオーケストレーションを行うための、新しいドラッグ&ドロップのエディタである Forms もリリースしました。これは検証ワークフローにも適用できます。本記事では「招待コード」ワークフロー、つまりユーザーがアプリケーションにログインできるようになる前に招待コードを検証しなければならないケースを扱います。

注:Custom Prompts と Forms は非常に相性が良いです。サインアップを完了したユーザーは Login Flow をトリガーし、そこで Forms を起動できます。

このブログ記事では Auth0 Forms を使って招待コードを収集し、外部でホストされている API へ HTTP リクエストを送って「コード + メールアドレス」の組み合わせが有効かどうかを検証します。これは限定的なユースケースの一例ですが、ここで説明するソリューションは「アクセス許可を与える前に、ユーザー入力を外部で検証する必要がある」あらゆるシナリオに対応できます。

ユースケース

あなたが、まもなくプライベート Beta をローンチする SaaS アプリケーションを持っているとしましょう。ユーザーがあなたのプラットフォームへサインアップすること自体は制限したくありません(MarTech スタックに取り込めるようにしたい)。しかし、チームメンバーまたはアフィリエイトからアクセスを付与されたユーザー以外は、正式にプラットフォームへログインできないようにしたい、とします。

このユースケースにはさまざまな解き方がありますが、本記事では「ユーザーに “Invite Code(招待コード)” を入力してもらい、バックエンドアプリケーションがそのコードでアクセス付与済みであることを検証する」という方法で解決します。プライベートローンチへのアクセスを承認する前に、ユーザーのメールアドレスとコードを一緒に送信して検証します。

Auth0 Custom Prompts を使えばサインアップ中にこのコードを収集できますが、検証に失敗した場合はアカウント自体が作成されません。あなたの目的は「アカウントは作成したうえで、プライベート Beta へのアクセスだけを招待コードで制御する」ことです。

フォームでユーザーに入力を促す

このフローを起動するために Post-Login Action を利用します。ユーザーはサインアップ後に自動的にこのフローへ入り、以後アプリケーションへ戻ってくるたびにこのフローに入ります。この Action はユーザーのプロフィール上のフラグを確認し、以前に検証済みかどうかを判定します。

exports.onExecutePostLogin = async (event, api) => {
  if (event.user.app_metadata.verified !== true) {
     const FORM_ID = 'ap_eJWCKKPimBy86AyTRsQikQ';
     api.prompt.render(FORM_ID);
  }
};

Auth0 テナントの Forms には ActionsForms からアクセスできます。これを開くと新しいタブでエディタが起動します。以下は、本記事の特定フロー用のビジュアルエディタです。

High-Level Form Flow

High Level Form Flow

ユーザーはテキスト入力で招待コードを入力するよう促され、その後フローが呼び出されます。次の画像では、コード検証の結果にエラーがあるかどうかをチェックするフローダイアグラムが確認できます。エラーがあればエラーメッセージを表示し、そうでなければフローを継続します。

Verification Flow

Verification Flow

ここでは、ユーザーのメールアドレスと収集した招待コードを使って、あなたのエンドポイントを呼び出す HTTP リクエストステップの構成方法が分かります。

HTTP Request Structure

HTTP Request Structure

レスポンスに基づき、結果が true ならユーザーはフローを継続し、そうでなければエラーメッセージが表示され、正しい値を入力するまで先へ進めません。

Conditional Configuration

Conditional Configuration

ユーザーが正しい値を入力すると、フローは終了してメインパスへ戻り、アクセスが付与されたこととアプリケーションを利用できることを通知します。フローを起動した Action には、認証トランザクションを再開するための continue セクションが必要です。また、再びこのフローに入らなくて済むよう、ユーザーのプロフィールにフラグを保存します。

exports.onContinuePostLogin = async (event, api) => {
  api.user.setAppMetadata("verified", true);
};

Success Prompt

Success Prompt

そして、サインアップして「メール + コード」の組み合わせが有効な新規ユーザーが通る、エンドツーエンドのフローがこちらです。

End to End User Journey

以下は、検証フローを今日から構築し始められる完成版 JSON です!Forms の画面で Create Form → Import It! と進むことで、この JSON を直接テナントへインポートできます。

Importing Existing Flow with JSON

Importing Existing Flow with JSON

{
  "version": "3.0.0",
  "form": {
    "name": "Invite Code",
    "description": null,
    "messages": {
      "custom": {},
      "errors": {}
    },
    "languages": {
      "default": null,
      "primary": "en"
    },
    "translations": {},
    "start": {
      "nextNode": "step_Y3AO",
      "coordinates": {
        "x": 22,
        "y": 167
      },
      "hiddenFields": []
    },
    "nodes": [
      {
        "id": "step_Y3AO",
        "type": "STEP",
        "alias": "Collect Code",
        "config": {
          "nextNode": "flow_Cilk",
          "components": [
            {
              "id": "rich_text_sME2",
              "type": "RICH_TEXT",
              "config": {
                "content": "<h3><strong>Please enter your invite code.</strong></h3>"
              },
              "category": "BLOCK"
            },
            {
              "id": "invite_code",
              "hint": null,
              "type": "TEXT",
              "label": "Invite Code",
              "config": {
                "maxLength": null,
                "minLength": null,
                "multiline": false,
                "placeholder": "XXX-XXX",
                "defaultValue": null
              },
              "category": "FIELD",
              "required": true,
              "transient": false
            },
            {
              "id": "next_button_xlCe",
              "type": "NEXT_BUTTON",
              "config": {
                "text": "Continue"
              },
              "category": "BLOCK"
            }
          ]
        },
        "coordinates": {
          "x": 323,
          "y": -36
        }
      },
      {
        "id": "flow_Cilk",
        "type": "FLOW",
        "alias": "Verify Code",
        "config": {
          "flowId": "#FLOW-1#",
          "nextNode": "step_IifF"
        },
        "coordinates": {
          "x": 1051,
          "y": 139
        }
      },
      {
        "id": "step_IifF",
        "type": "STEP",
        "alias": "Verification Complete",
        "config": {
          "nextNode": "$ending",
          "components": [
            {
              "id": "rich_text_pMye",
              "type": "RICH_TEXT",
              "config": {
                "content": "<h3><strong>Welcome to Demo0, </strong></h3><h3><strong>Click Continue to get Started!</strong></h3>"
              },
              "category": "BLOCK"
            },
            {
              "id": "next_button_Z6Md",
              "type": "NEXT_BUTTON",
              "config": {
                "text": "Continue"
              },
              "category": "BLOCK"
            }
          ]
        },
        "coordinates": {
          "x": 1510,
          "y": -2
        }
      }
    ],
    "ending": {
      "content": null,
      "redirection": null,
      "callback": null,
      "afterSubmit": {
        "email": null,
        "flowId": null
      },
      "coordinates": {
        "x": 2194,
        "y": 110
      },
      "resumeFlow": true
    },
    "social": [],
    "style": {
      "css": null,
      "theme": "ROUND",
      "version": "MODERN"
    },
    "tags": []
  },
  "flows": {
    "#FLOW-1#": {
      "name": "Verify Invite Code",
      "description": null,
      "actions": [
        {
          "id": "http_request_verify_code",
          "type": "HTTP",
          "alias": "Verify Invite Code",
          "notes": null,
          "action": "SEND_REQUEST",
          "params": {
            "url": "https://edge.samyap.dev/api/verify-code",
            "body": {
              "email": "{{context.user.email}}",
              "invite_code": "{{fields.invite_code}}"
            },
            "type": "JSON",
            "basic": null,
            "method": "POST",
            "params": {},
            "headers": {},
            "acceptNOK": false,
            "connectionId": null
          },
          "allowFailure": false
        },
        {
          "id": "if_then_condition_q0Ym",
          "type": "FLOW",
          "alias": "Verify Code Result",
          "notes": null,
          "action": "BOOLEAN_CONDITION",
          "params": {
            "then": [],
            "evaluate": {
              "operands": [
                {
                  "operands": [
                    "{{ http_request_verify_code.body.result }}",
                    "true"
                  ],
                  "operator": "EQ"
                }
              ],
              "operator": "AND"
            },
            "otherwise": [
              {
                "id": "show_error_message_4rkM",
                "type": "FLOW",
                "alias": null,
                "notes": null,
                "action": "ERROR_MESSAGE",
                "params": {
                  "message": "Your code is invalid.  Please contact support for help"
                },
                "allowFailure": false
              }
            ]
          },
          "allowFailure": false
        }
      ],
      "triggers": {
        "webhook": {
          "secret": null,
          "enabled": false
        }
      },
      "synchronous": true,
      "security": {
        "rateLimits": []
      }
    }
  },
  "connections": {}
}

まとめ

今日は、Auth0 Forms を活用してユーザーから入力を収集し、ノーコードの Forms / Flows エディタを使って外部システムでユーザーを検証する方法を学びました。この能力は、Progressive Profiling(段階的プロファイリング)や同意取得(Consent Collection)など、さまざまなアプリケーションへ拡張できます。

Auth0 では、お客様に高品質のサービスと開発者体験を提供することを目指しています。お客様とユーザーの最初の接点はログイン画面であることが多いため、ユーザー体験が最高水準であることの重要性を理解しています。ホスト型の Universal Login により、こうした高セキュリティなワークフローをデプロイ・保守する負担を取り除けます。本記事で示した Forms の機能により、チームはより速く改善を回し、ブランドに合った “粘り強い(リテンションにつながる)” 体験へカスタマイズし、最終的にビジネス成長を後押しできます。