ゼスト Tech Blog

ゼストは「護りたい。その想いを護る。」をミッションに、在宅医療・介護業界向けのSaaSを開発しています。

Google Identity Platformのマルチテナンシー化とパスワードポリシー設定

はじめに

サービスを開発していく上で必ずといっていいほど出てくるのが、パスワードポリシーの実装です。そして特にやっかいなのが、サービス導入企業毎に独自のパスワードポリシーで運用したいというものです。

  • A企業では「大文字小文字の英数字を含んだ10桁以上のパスワード」
  • B企業では「英字のみで構成される6桁以上のパスワード」
  • C企業では「特にありません」

素直に設計しようとすると、企業毎にパスワードのルールをDBなどで管理していくことになるでしょう。

「実におもしろい!いやっ、地味にめんどくさい!」

近年、ユーザー管理・認証を提供する、いわゆるIDaaSが増えてきました。自前でこれらを実装するのは大変ですし、手軽に利用できるのであればIDaaSを使ったほうがお得だと感じます。
そこで本記事では、弊社で利用しているGCPにおける、Google Identity Platformが提供するマルチテナンシー機能とパスワードポリシー設定機能を組み合わせて、企業ごとに独自のパスワード設定を実現する方法をご紹介します。

パスワードポリシー設定項目

次のような設定がサポートされています。

enforcementState:パスワードポリシーの使用

意味
ENFORCE パスワードポリシーを使用する
OFF パスワードポリシーを使用しない

forceUpgradeOnSignin:サインイン時にパスワードポリシー遵守を強制

運用途中でパスワードポリシーを変更した場合の振る舞いを設定できます。

意味
true ユーザーがポリシーを遵守したパスワードに更新するまで、ログインの試行は失敗します
false ユーザーは遵守していないパスワードを使用してログイン可能

constraints:設定可能なパスワード要件

意味
requireUppercase 大文字必須
requireLowercase 小文字必須
requireNumeric 数字必須
requireNonAlphanumeric 記号必須(^ $ * . [ ] { } ( ) ? " ! @ # % & / \ , > < ' : ; | _ ~)
minLength 最小文字数(6~30 文字。デフォルトは 6)
maxLength 最大文字数(最大 4,096 文字)

参考:

cloud.google.com

マルチテナンシー

テナントと呼ばれる単位で、上記のパスワードポリシー設定とユーザーの管理ができるようになります。 よって、冒頭に述べた企業ごとのパスワードポリシーを実現するには、企業ごとにテナント登録とパスワードポリシー設定をすれば良いわけです。

まずは以下の公式ドキュメントにそって、マルチテナンシーの有効化をします。

マルチテナンシーの設定  |  Identity Platform のドキュメント  |  Google Cloud

いざ実践

まず、テナントを作成するにあたりテナント名(displayName)が必要となります。 テナント名は以下のルールに準拠しなければ登録できません。

英字、数字、ハイフンを使用し、英字で始まる 4~20 文字

テナントを作成すると、GCP側でテナントID({テナント名}-{X桁の文字列})で払い出されます。 このテナントIDを使ってユーザーを登録していきます。

まずはパスワードポリシーを設定したテナント作成から見ていきましょう。
※ 2023年10月時点では管理コンソール上で設定する方法は提供されていないようです。

import * as sdk from "firebase-admin";

const app = sdk.initializeApp({
    credential: sdk.credential.cert("秘密鍵")
});

async function createTenant() {
  // tenantManagerなるものを生成
  const tenantManager = app.auth().tenantManager();
  // テナントを作成 with パスワードポリシー
  const tenant = await tenantManager.createTenant({
    // displayNameがテナント名
    displayName: "tenantname",
    // IDプロバイダはEmail/Password
    emailSignInConfig: {
      enabled: true,
      passwordRequired: true,
    },
    passwordPolicyConfig: {
      enforcementState: "ENFORCE",
      forceUpgradeOnSignin: true,
      constraints: {
        requireUppercase: true,
        requireLowercase: true,
        requireNonAlphanumeric: true,
        requireNumeric: true,
        minLength: 10,
      }
    },
  });

  console.log(tenant);

// 出力結果
// Tenant {
//   tenantId: 'tenantname-xxxxx', // 実際はハイフンより後ろはランダムな文字列で生成される
//   displayName: 'tenantname',
//   emailSignInConfig_: EmailSignInConfig { enabled: true, passwordRequired: true },
//   anonymousSignInEnabled: false,
//   passwordPolicyConfig: PasswordPolicyAuthConfig {
//     enforcementState: 'ENFORCE',
//     constraints: {
//       requireLowercase: true,
//       requireUppercase: true,
//       requireNonAlphanumeric: true,
//       requireNumeric: true,
//       minLength: 10,
//       maxLength: 4096
//     },
//     forceUpgradeOnSignin: true
//   }
// }

}

createTenant();

コンソール画面で確認すると、作成できていますね!

これでこのテナント配下に登録されるユーザーのパスワードポリシーは「10文字以上で大文字小文字を含む英数字と記号が必須」となります。

それでは続いて、作成したテナントにユーザーを登録していきましょう。

import * as sdk from "firebase-admin";

const app = sdk.initializeApp({
    credential: sdk.credential.cert("秘密鍵")
});

async function createUser() {
  const tenantManager = app.auth().tenantManager();
  // テナントIDを指定し、tenantAwareAuthを使ってユーザーを作成する
  const tenantAwareAuth = tenantManager.authForTenant("tenantname-xxxxx");
  // ユーザー作成
  const user = await tenantAwareAuth.createUser({
    email: "test@example.com", 
    password: "Password2023!", 
    emailVerified: true 
  });

  console.log(user);

// 出力結果
// UserRecord {
//   uid: 'Vw5PEokErSSlc1oy7Sujvbs7Hj52',
//   email: 'test@example.com',
//   emailVerified: true,
//   displayName: undefined,
//   photoURL: undefined,
//   phoneNumber: undefined,
//   disabled: false,
//   metadata: UserMetadata {
//     creationTime: 'Fri, 20 Oct 2023 18:15:48 GMT',
//     lastSignInTime: null,
//     lastRefreshTime: null
//   },
//   providerData: [
//     UserInfo {
//       uid: 'test@example.com',
//       displayName: undefined,
//       email: 'test@example.com',
//       photoURL: undefined,
//       providerId: 'password',
//       phoneNumber: undefined
//     }
//   ],
//   passwordHash: undefined,
//   passwordSalt: undefined,
//   tokensValidAfterTime: 'Fri, 20 Oct 2023 18:15:48 GMT',
//   tenantId: 'tenantname-xxxxx'
// }

}

createUser();

コンソール画面で確認

実に簡単ですね!

もし、パスワードポリシーに準拠していないパスワードを指定すると、以下エラーが返ってきます。

errorInfo: {
  code: 'auth/internal-error',
  message: 'Missing password requirements: [Password must contain at least 10 characters, Password must contain an upper case character, Password must contain a numeric character, Password must contain a non-alphanumeric character] Raw server response: "{"error":{"code":400,"message":"PASSWORD_DOES_NOT_MEET_REQUIREMENTS : Missing password requirements: [Password must contain at least 10 characters, Password must contain an upper case character, Password must contain a numeric character, Password must contain a non-alphanumeric character]","errors":[{"message":"PASSWORD_DOES_NOT_MEET_REQUIREMENTS : Missing password requirements: [Password must contain at least 10 characters, Password must contain an upper case character, Password must contain a numeric character, Password must contain a non-alphanumeric character]","domain":"global","reason":"invalid"}]}}"'
},

ログインする際はtenantId, email, passwordが必要になります。

curl -s 'https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword?key=${API_KEY}' -H 'Content-Type: application/json' --data-binary '{"tenantId":"tenantname-xxxxx","email":"test@example.com","password":"Password2023!"}'
// 結果
{
  "kind": "identitytoolkit#VerifyPasswordResponse",
  "localId": "Vw5PEokErSSlc1oy7Sujvbs7Hj52",
  "email": "test@example.com",
  "displayName": "",
  "idToken": "JWT形式の値",
  "registered": true
}

無事ログインまでできました。

ちなみにログイン試行の失敗には上限があり、ドキュメントには以下のように記載されてました。

ユーザーのログイン試行の失敗には上限があります。短時間でログイン試行に複数回失敗すると、ユーザーは一時的にアカウントにログインできなくなります。

実際に試したところ、「X分間の間に5回失敗すると、Y分間アカウントロックがかかる」といった感じで、これらの値の設定変更はできそうにありませんでした。

ユーザーのログイン手順をご参照ください。

cloud.google.com

価格

基本として、MAU数での課金となります。 49,999MAU数までは無料で使うことができ、ボリューム数に応じて単価が下がるのも嬉しいところです。

詳しくはこちらをご参照ください。

cloud.google.com

最後に

実際に触ってみた感想としては、実装工数は少なく実装できたと思います。
マルチテナンシーを有効化していると、ログイン時にemail, passwordに加えてtenantIdを指定する必要がありますが、企業ごとにパスワードポリシーをお手軽に設定できるというのは魅力的ではないでしょうか。
本記事では紹介できませんでしたが、Identity Platformでは二要素認証を設定したり、SAML対応ができたりとまだまだできることはたくさんあります。気になった方は公式ドキュメントを読んでみてほしいと思います。
その他、IDaaS系のクラウドサービスはいくつかあるので複数試した上で検討するのが良いと思いますが、Identity Platformはその中でも機能面と料金面の両面においてコストパフォーマンスに優れていると言えるのではないでしょうか。
自前で作ったほうがカスタマイズ性は高く、後々問題が発生したときに融通が利くので、その辺のメリデメを踏まえ選定するのが良いかと思います。