Auth0で普段と違う地域からのログインを抑制したい

2022.01.12

普段と違う地域からのログインがあった場合、アラートを出したり、ログインを拒否するケースを考えた場合、Auth0ではアダプティブMFA という機能を使うことによってMFAをトリガーしたり、信頼性が低い(リスクが高い)ログインフローを抑制できると説明されています。

アダプティブMFAの評価の中に、 ImpossibleTravel というものがありますが、これが普段と違う地域からのログインを判定するスコアとなっています。

ただし、アダプティブMFAを有効にするには、エンタープライズプランを契約している かつ アダプティブMFAアドオンを別料金で購入していることが条件となります。

このアドオンを使う以外の方法で、なんとかユーザーの所在地を取得する方法がないか調べていたのですが、

Auth0はユーザーがログインした時のIPアドレスをユーザープロファイルに持っていますが、所在地は持っていませんでした。

ただし、ログイン時のIPアドレスをAuth0のActionsで取得はできるので、これを使ってIPアドレスの所在地を外部APIから取得することはできるだろうと考えたので実際に試してみようと思います。

ログイン時のIPアドレスを使用する

IP GeolocationのAPI

さまざま存在していますが、今回はipgeolocationというものを使おうと思います。

IP Geolocation APIがあるので、このAPIを使ってIPの所在地を取得することにします。

APIアクセス例)

# Get geolocation for an IPv4 IP Address = 8.8.8.8
$ curl 'https://api.ipgeolocation.io/ipgeo?apiKey=API_KEY&ip=8.8.8.8'

# Get geolocation for an IPv6 IP Address = 2001:4860:4860::1
$ curl 'https://api.ipgeolocation.io/ipgeo?apiKey=API_KEY&ip=2001:4860:4860::1'

Actionsを作成

Auth0のActionsは、Auth0のサインアップやログインのフローを独自のコードでカスタムできます。

今回はログインフローをカスタマイズするActionsを作ってみます。

Actions->LibraryのページのCustom Buildをクリックすると新規作成できます。

Custom Actionsを記述

onExecutePostLogin の箇所がログインの時に実行される部分なので、ここに記述していきます。

以下の2つのフローを考えてみました。

所在地の自動登録時のフロー(所在地がまだuser_metadataに登録されていない)

  1. ユーザーのリクエストIPをパラメーターにしてIP Geolocation APIを実行します。
  2. APIの返却結果から、country_codeやcountry_nameなど、必要な情報をAuth0のuser_metadataに保存します

ログイン時の所在地チェックのフロー(所在地がuser_metadataに登録されている)

  1. ユーザーのリクエストIPをパラメーターにしてIP Geolocation APIを実行します。
  2. user_metadataに保存されている所在地情報とAPIの結果を比較して判断する

サンプルコード)

const axios = require('axios');

/**
* Handler that will be called during the execution of a PostLogin flow.
*
* @param {Event} event - Details about the user and the context in which they are logging in.
* @param {PostLoginAPI} api - Interface whose methods can be used to change the behavior of the login.
*/
exports.onExecutePostLogin = async (event, api) => {
  if (event.client.client_id === "<<アプリケーションのクライアントIP>>") {
    const user_location = event.user.user_metadata.user_location;

    const url = 'https://api.ipgeolocation.io/ipgeo?apiKey=' + event.secrets.APIKEY + '&ip=' + event.request.ip
    const ip_geolocation_response = await axios.get(url);

    if(user_location) {
      if(user_location !== ip_geolocation_response.data.country_code2) {
        console.log("metadata: " + user_location)
        console.log("api_res: " + ip_geolocation_response.data.country_code2)
        return api.access.deny(`Access to ${event.request.ip} is not allowed. IP Address access ImpossibleTravel.`);
      }

      console.log("Login successful")
    }else{
      api.user.setUserMetadata('user_location', ip_geolocation_response.data.country_code2);
    }
  }
};

Custom Actionsをデプロイ後、Login Flowに組み込みます。

動作検証

カスタマイズしたLogin Flowを実行したところ、user_metadataにuser_locationが存在しない場合,

user_metadataに書き込み後、ログインされていました。

(ログイン後のuser_metadata)

(追加したCustom Actionsのログ)

console.log()でIP Geolocation APIの結果が出力されるはずです。

JP以外のIPに変更してログインを試みると

コードに書いた通りのメッセージを出力してエラー終了されました。

metadataにはJPとはいっているが、アクセス元のIPがAUのものだったから失敗とみなしたことになります。

(追記) event.request.geoip を使う

リクエストIPを使って外部サービスから所在地情報を取得する方法をとっていましたが、Auth0のActionsでevent.request.geoipから所在地情報を取得できるよと教えてもらったので、その方法も記載します。

Event Object

  • cityName
  • continentCode
  • countryCode
  • countryCode3
  • countryName
  • latitude
  • longitude
  • timeZone

上記の情報が取得できますので、

以下のようなサンプルコードでもIP GeolocationのAPIを使ったケースと同じような挙動になるかと思います。

/**
* Handler that will be called during the execution of a PostLogin flow.
*
* @param {Event} event - Details about the user and the context in which they are logging in.
* @param {PostLoginAPI} api - Interface whose methods can be used to change the behavior of the login.
*/
exports.onExecutePostLogin = async (event, api) => {
  if (event.client.client_id === "<<アプリケーションのクライアントIP>>") {
    const user_location = event.user.user_metadata.user_location;
    const ip_geolocation_response = event.request.geoip;

    if(user_location) {
      if(user_location !== ip_geolocation_response.countryCode) {
        console.log("metadata: " + user_location)
        console.log("api_res: " + ip_geolocation_response.countryCode)
        return api.access.deny(`Access to ${event.request.ip} is not allowed. IP Address access ImpossibleTravel.`);
      }

      console.log("Login successful")
    }else{
      api.user.setUserMetadata('user_location', ip_geolocation_response.countryCode);
    }
  }
};

event.request.geoip の出力結果は以下

{  cityName: 'Shiroi',  continentCode: 'AS',  countryCode3: 'JPN',  countryCode: 'JP',  countryName: 'Japan',  latitude: 35,  longitude: 140,  timeZone: 'Asia/Tokyo'\n}

最後に

Auth0で普段と違う地域からのログインがあった場合、アラートを出したり、ログインを拒否するには、

アダプティブMFAを有効にして対応する方法がありますが、Actionsを使って取得、判定する方法も使えるかと思います。 若干の作り込みは必要ですが、Actionsでも所在地情報は取れて、ログインの判定に使えると覚えておいて損はないでしょう。