顔認証のクラウドサービスMercury Cloudでさらに遊んでみる – 2要素認証に1対N検索を用いる –

2021.11.12

Mercury Cloudの顔検索APIを2要素認証として使ってみる記事です。 顔検索APIは特徴データベースに登録されているすべての顔特徴の中からアップロードした画像から検出された顔を検索し、最も近い結果を返す というAPIです。

以前の記事でカメラで取得した画像データを特徴データベースに登録しました。

このデータベースを使って顔検索をし、本人特定のフローをシミュレーションしてみます。 Auth0を使った認証の2要素目として動作するようにしていきます。

やってみる

顔比較APIを2要素目として試した時に使ったActionsとアプリを流用します。

顔比較のアプリにダイレクトするAuth0 Actions

Auth0での認証途中でリダイレクトするようなActionsを作ります。

例)

payloadにユーザーを特定できる情報としてemailとexternalUserIdというデータを送ります。

externalUserIdはapp_metadataに保存しているfeature_id(顔特徴データベースに登録されているユーザー固有の特徴ID)を使用します。

のちのセクションでも出てきますが、app_metadata.feature_id は初回の顔特徴を登録したときのものを保存します。

externalUserIdが取得できた場合は顔特徴を登録せずに検索の処理を行う  といった処理ができるのではないでしょうか。

exports.onExecutePostLogin = async (event, api) => {
  const token = api.redirect.encodeToken({
    secret: event.secrets.REDIRECT_SECRET,
    expiresInSeconds: 600, 
    payload: {
      // Custom claims to be added to the token
      email: event.user.email,
      externalUserId: event.user.app_metadata.feature_id,
    },
  });

  // Send the user to https://my-app.exampleco.com along
  // with a `session_token` query string param including
  // the email.
  api.redirect.sendUserTo("https://<<顔認証のアプリURL>>", {
    query: { session_token: token }
  });

  // provider
  //   any: Use any of the configured challenges.
  //   duo: Use the Duo mutlifactor provider.
  //   google-authenticator: Use the Google Authenticator provider.
  //   guardian: Use the Guardian provider.
  //api.multifactor.enable("any", {"allowRememberBrowser": false});
};

顔特徴データベースから検索

※ Auth0からexternalUserIdというデータが送られてきたケースで行なっていきます。

カメラの映像から取得した画像をデータベース内で検索します。

カメラで静止画を取得する, base64でデータを取得 だったりは前回の記事で行ったので割愛します。

顔認証のクラウドサービスMercury Cloudでさらに遊んでみる – 2要素認証に1対1比較を用いる –

例)

データベースから上位1件のデータを取得する

Auth0での認証途中の処理なので、最終的にAuth0の/continueエンドポイントにデータと一緒にリダイレクトします

var data = JSON.stringify({
        "image": {
          "data": img_data, // カメラで取得した画像(base64のデータ)
        },
        "db_ids":["<<特徴データベースのID>>"],
        "top_k":1, // db_idsで指定された各特徴データベースの上位K個の類似検索結果を、[1、1024]の範囲で指定
        "min_score": 0.9 // 各特徴データベースで顔検索するときの最小スコアを、[0、1]の範囲で指定
});

var config = {
    method: 'post',
    url: 'https://mercury-ap-northeast-1.japancv.co.jp/openapi/face/v1/<<app_id>>/databases/search',
    headers: { 
      'x-date': x_date_time_string(), // 処理の中身は省略
      'Authorization': authorization(), // 処理の中身は省略 
      'Content-Type': 'application/json'
    },
    data : data
};

// Auth0に返す情報を作成する
const is_compare = false
const returnToken = jwt.sign(
  {
              sub: "<<Auth0ユーザーのID>>",
              exp: 1646444617, //expiresInSecondsパラメータで指定された有効期限(秒単位
              iss: "<<Auth0ユーザーのID>>",
              state: "<<Auth0から発行されたstateパラメーター>>",
              is_compare: is_compare, // 比較結果
              score: 0, // 比較スコア  
              feature_id: qrwefrgw // 顔特徴ID
  },
  "<<シークレット文字列>>",
  { algorithm: "HS256" }
);

axios(config)
  .then(response => {
      // ここにAPIの実行結果を使った処理を書く
            〜〜〜〜〜〜〜
            〜〜〜〜〜〜〜
            〜〜〜〜〜〜〜
  }).catch(error => {
    console.log("error")
              // ここにAPIの実行結果を使った処理を書く
            〜〜〜〜〜〜〜
            〜〜〜〜〜〜〜
            〜〜〜〜〜〜〜
  }).finally(function() { 
     // Auth0のcontinueエンドポイントにリダイレクト
      // face_compareというパラメータを使って情報を渡している
     reply.redirect(auth0_continue + "?state=" + request.body.state + "&face_compare=" + returnToken)
     return
   });

Auth0に戻ってきた時のActions

Auth0の/continueエンドポイントに来ると、ActionsのonContinuePostLoginが動くので、その中に処理を書きます。

例)

顔検索の結果と、スコア、顔特徴IDを受け取り、最終的にスコア値をid-tokenに格納

顔特徴IDをapp_metadataに保存

した例です。

exports.onContinuePostLogin = async (event, api) => {
  const payload = api.redirect.validateToken({
    secret: event.secrets.REDIRECT_SECRET,
    tokenParameterName: 'face_compare',
  });

  if(payload.is_compare == false) {
    api.access.deny("顔認証に失敗しました");
  }
  // use the data encoded in the token, such as: 
  const namespace = 'https://study-archives';
  api.idToken.setCustomClaim(`${namespace}/score`, payload.score);
  api.idToken.setCustomClaim(`${namespace}/feature_id`, payload.feature_id);
  api.user.setAppMetadata("feature_id", payload.feature_id);
};

一連の流れを確認

ログインから顔認証をし、id-tokenにスコア値を格納したところまでを録画しました

最後に

1対Nの顔検索をAuth0の認証に組み込んでみました。 顔データを送信するだけで本人確認ができるので複雑なアプリケーションを作る必要はなさそうですね。

注意しなければいけない点として、 Auth0の機能としての多要素認証ではないので、顔検証に失敗したりカメラがなくて顔のデータを取得できない場合は Auth0の多要素認証を実行するようにしたほうが良いかと思います。