👨‍💻

サービス管理者用の認証にはIAPとIdentity Platformの組み合わせが便利かもしれない

2023/08/15に公開

皆さんはサービスの管理者が使うための認証をどのように実装していますか?
FirebaseAuth0などのサービスを使ってログイン機能を実装している方も多いと思いますが、
今回はGCPIAPIdentity Platformを使って管理画面のログイン機能を実装することのメリットと具体的な実装方法について紹介します。

IAP(Identity-Aware Proxy)とは

IAPとはIdentity-Aware Proxyの略で、GCPのサービスの一つです。
IAPを使うことで、サービスに対するリクエストの前段に認証を挟むことができます。

例えば、GAECloud Runなどのサービスに対してIAPを設定すると、Cloud Runにリクエストが到達する時にはすでに認証が完了している状態となりリクエストヘッダーにはIAPが発行したJWTが含まれています。

アプリケーション側でこのJWTからユーザーのメールアドレスなどを取得することができます。

以下はJWTからユーザーのメールアドレスを取得するコードの例です。

具体的なコードを確認
auth.ts
import { headers } from 'next/headers';
import prisma from "#/prisma"
import { OAuth2Client } from "google-auth-library"

export const getUserByJwt = async () => {
  const headersList = headers();
  const authorization = headersList.get('x-goog-iap-jwt-assertion');
  // 開発環境時にデバッグ認証用 Header での認証を許可
  if (
    process.env.NODE_ENV === 'development' &&
    process.env.DEBUG_EMAIL
  ) {
    const email = process.env.DEBUG_EMAIL
    if (typeof email !== 'string') {
      throw new Error('Custom Header が正しくありません')
    }

    const user = await prisma.user.findUnique({
      where: {
        email,
      },
    })

    return {
      email,
      role: user?.role,
      id: user?.id,
      sub: '',
      hd: '',
    }
  }
  // Authorization ヘッダーの存在チェック
  if (!authorization) {
    throw new Error('Authorization header は必須です')
  }

  const oAuth2Client = new OAuth2Client()
  const response = await oAuth2Client.getIapPublicKeys()
  const ticket = await oAuth2Client.verifySignedJwtWithCertsAsync(
    authorization,
    response.pubkeys,
    process.env.GCP_IAP_AUDIENCE, 
    ['https://cloud.google.com/iap']
  )

  const payload = ticket.getPayload()
  if (!payload) {
    throw new Error(
      'トークンに payload 情報が含まれていません'
    )
  }


  const { email, hd, sub } = payload

  if (!email) {
    throw new Error('トークンに email 情報が含まれていません')
  }

  const mail = email.split(':')[1]

  const user = await prisma.user.findUnique({
    where: {
      email: mail
    },
  })

  return {
    email: mail,
    role: user?.role,
    id: user?.id,
    sub,
    hd,
  }
}

ただし、IAPだけを使うことの欠点として、ユーザーの管理がIAMでロールを付与する形でしか管理できないのでプログラム的にアプリケーション側でユーザーを追加したりすることがやりづらいという点があります。
自分だけが使う管理画面だったらいいのですが、急にログインさせたいユーザーが増えた場合に少し面倒です。

Identity Platformとは

そこで、Identity Platformの出番です。
Identity PlatformとはGCPのアクセス管理プラットフォームで、ちょうどAuth0Firebase Authのようなサービスです。
以下の画像のように、Identity Platformを使うことで、ユーザーの管理を行うことができます。

また認証方法もGoogleやX(旧Twitter)、メール認証など色々と選べることが魅力的です。

このIdentity PlatformFirebaseのAPIが使用できるので、FirebaseのSDKを使ってユーザーを追加したり削除したりすることができます。

以下はFirebaseのSDKを使ってユーザーを操作するコードの例です。

ユーザーが存在するかどうかのチェック

const isUserExists = await app.auth().getUserByEmail(body?.email).catch(() => false)

ユーザーの作成

await app.auth().createUser({
  email: body?.email,
  emailVerified: false,
  password: randomUUID(),
  displayName: body?.name,
})

ユーザーの削除

const user = await app.auth().getUserByEmail(organizationUser.email)
await app.auth().deleteUser(user.uid)

IAPとIdentity Platformの組み合わせ認証のメリット

このIAPIdentity Platformを組み合わせることで、Identity Platformでユーザーを管理しつつ、IAPで認証を行うことができます。
これによって、認証方法が限られているIAPの欠点を補うことができます。

また、アプリケーション側にアクセスできるのはすでに認証が完了しているユーザーのみということになるので以下のメリットが得られます。

  • アプリケーション側でログインなどの実装処理を書く必要がない
  • アプリケーションにアクセスできる人はすでに認証済みということが前提なのでセキュリティ的に安心
  • 二つを組み合わせることで色々な認証方法を提供できる

今回は私(管理者)がログインして確認するための画面なので、私以外の人間がこの画面にアクセスできないIAPの仕組みがあると、
アプリケーション側の実装でセキュリティの心配をあまりする必要がなくなります。

IAPとIdentity Platformの組み合わせ認証のデメリット

アプリケーションにアクセスする際に初めに必ずIdentity Platformのログイン画面が表示されるので一般のユーザーが使うサービスには向いていません。

実装方法

メリットについて説明しましたが、次に実際にどのように実装するのかを説明します。

HTTP(S)ロードバランシングの作成

まず、IAPを使うためにはHTTP(S)ロードバランシングを作成する必要があります。

フロントエンド構成の作成

HTTP(S)ロードバランシングを作成するにはまず、フロントエンド構成を作成する必要があります。
フロントエンド構成ではIPアドレスやポート、プロトコル、証明書の設定などを行います。

バックエンド構成の作成

次に、バックエンド構成を作成します。

バックエンドタイプとしてサーバーレスネットワークエンドポイントグループを今回は選択します。

ルーティングルールの作成

ルーティングルールを作成します。今回は「単純なホストとパスのルール」を選択します。

IAPの有効化

次に、IAPを有効化します。

https://console.cloud.google.com/security/iap

上記のページにて先ほど作成したバックエンドに対してIAPを有効化します。

IAPとIdentity Platformの連携

IAPとIdentity Platformを組み合わせるにはIAPの画面で認証にIAMではなく、Identity Platformを選択します。
IAPの画面にて該当のバックエンドサービスにチェックを入れ選択します。

そして、Identity Platformの設定画面で認証方法を選択します。
認証方法には複数選択が可能です。

Google認証を使う場合は必要に応じて、OAuth同意画面などの設定をしましょう。

Cloud Runにてトリガーを選択

私は管理者用のサービスにCloud Runを使っているので、最後に該当の Cloud Run のアプリケーションに対してトリガーを設定します。
今回は作成したCloud Load Balancingからのリクエストのみ許可したいので以下のように選択します。

これで、IAPIdentity Platformを組み合わせた認証が完了しました。
Cloud Runにアクセスすると、Identity Platformのログイン画面が表示されるようになります。

ログイン画面のカスタマイズ

また、IAPの画面でIdentity Platformを認証として設定すると自動で認証用のCloud Runのサービスが作成されます。
IAPの該当のバックエンドサービスにチェックを入れて選択することでそのログインURLとカスタマイズ用のURLが表示されるので、アクセスして必要に応じてカスタマイズを行いましょう。

ログイン画面のカスタマイズ用のCSSのURLも指定できるので、こちらも必要に応じてCSSでカスタマイズしましょう。

詳しい、設定の方法は以下のドキュメントを参照してみてください。

https://cloud.google.com/iap/docs/cloud-run-sign-in?hl=ja

まとめ

今回はIAPIdentity Platformを組み合わせて管理画面のログイン機能を実装することのメリットと具体的な実装方法について紹介しました。
管理者だけがログインして使えるサービスには、アプリケーション側でログインの実装を気にせずセキュアにログイン機能が使えるこのIAPの仕組みはとても便利だと思います。
さらにIdentity Platformを使うことでログインできるユーザーの管理も簡単に行うことができます。

管理画面などを作る機会がある場合には皆さんもぜひ試してみてください。

参考

https://zenn.dev/ww24/articles/19099c85febe0d

Discussion