🌐
第四回:GCPでログイン機能を作ってみた
はじめに
現在、趣味でWebサービスを作っていますが、そのWebサービスに対して、ログイン認証機能を構築することで、特定のユーザーだけがアクセスできるようにしたいと思いました。
※現在使っているサービスの機能は過去の下記記事に記載しております。
ログイン認証をどうやって実現したか
ログイン認証はいくつもの実現方法があると思いますが、今回はサービスをGCP環境に構築しているのもあるため、GCP上のサービスを使って、ログイン認証を実現することを考えました。
そこで、Identity PlatformとIdentity Aware Proxyを利用して、ログイン認証を実現することを考えました。
構成イメージとしては、下記が限りなく近いです。要因としては、下記が挙げられます。
現状はフロントはWebアプリのみだが、将来的にモバイルアプリを作る可能性があるため、バックエンドAPIに対してLBを配置したい
モバイルアプリやフロント側アプリは誰でもアクセス可能だが、バックエンドAPIは認証トークンをつけて、アクセス制限をしたい
Identity Platformとは?
端的に言うと、GCPで提供している認証サービスです。Firebase Authenticationの上位互換なイメージです。詳細は下記の記事にまとまっておりますので、ご覧いただけると幸いです。
OIDC、SAMLなどの認証方式をサポートしていますが、今回はシンプルにEメールアドレス・パスワード認証としています。
※この場合、GCPコンソールで、Identity Platformの画面からユーザーを追加することも可能です。
Identity Aware Proxyとは?
端的に言うと、APIサーバーなどのリソースを保護するためのサービスになります。シンプルに認証されたユーザーのみをアクセス可能にするだけではなく、ユーザーごとにアクセス制御を行うことも可能です。詳細は下記の記事やドキュメントをご覧ください。
今回は、バックエンドのAPIサーバーとしてCloud Runを利用しているため、IAPを用いて、Runへのアクセス制御を行います。
認証関連について構築した構成図
下記のような構成図となります。処理フローとしては、下記です。
ログイン認証:フロント側でEメール・パスワードを求め、その情報を用いて、Identity Platformで認証処理を実施(フロント側では、Firebase Auth SDKを用いると楽に実装が可能となる)
IDトークンの保持:認証OKの場合、ユーザーごとのIDトークン(JWT)情報が払い出されるため、それをフロント側では保持する
バックエンドへのリクエスト:フロント側からバックエンドのAPI(LB + Identity Aware Proxy + Cloud Run)に対してリクエストを送る際に、Authorization: BearerヘッダーにIDトークンを付与して、送信する(詳細はこちらを参照)

上記それぞれのステップにおいての実装周りの話は後述します。その前に、上記インフラを構築する際の手順について触れていきます。
インフラの構成手順
前述した認証に関する構成を組むために、下記に箇条書きとなりますが、手順を記載します。
※多くの手順はterraformで組もうと思って実装中ですが、一部はterraformで自動的な構築ができない部分もあります。OAuth認証画面の構築などが該当します。
大まかに下記のステップとなります。それぞれのステップについて、詳細を見ていきます。
Cloud Runの構築(バックエンド側)
Identity Platformの設定(初期化)
Identity Aware Proxyの設定(初期化)
ドメインの取得
SSL証明書の取得
Load Balancingの構築
DNSの設定
IAPの設定
Cloud Runの構築(フロントエンド側)
1. Cloud Runの構築(バックエンド側)
以下の手順が必要となります。ただし、下記は、今回のログイン認証周りとはあまり関連しないため、私の作ったサービスでは必要という理解で大丈夫です。
artifact registry apiを有効化し、新規でレポジトリを作成
firestore apiを有効化し、新規のデータベースを構築
GCSのバケットを構築
vertex ai apiを有効化
バックエンドAPIのセットアップを行い、デプロイする(こちらを参照)
2. Identity Platformの設定(初期化)
以下の手順が必要となります。まずは、Identity Platformを利用可能な状態にします。
下記は全て、google cloud console上で実施が可能です。
identity platformを有効化
(メール/パスワードの)プロバイダを追加する
(後追いの実施でもOK)任意のユーザーを追加する
3. Identity Aware Proxyの設定(初期化)
以下の手順で、Identity Aware Proxyが利用可能となります。
下記も全て、google cloud console上で実施が可能です。
Identity Aware Proxyを有効にする
★OAuth認証画面を構成
※★マークがついた部分は、terraformでの自動構築ができない部分となります。
4. ドメインの取得
HTTPS通信とするため、SSL証明書が必要となりますが、その際にドメインも必要となります。そこで、ドメインの取得も行います。
今回は、GCPのCloud Domainsを使って、ドメインを取得します。下記の手順となります。同様にgoogle cloud console上で実施が可能となります。
cloud domains apiを有効にする
cloud dns apiを有効にする
cloud domains経由で、ドメインを作成する
生成したドメインに対して、activation処理を行う(メールで対応する。設定したEメールアドレス宛に、verifyのメールが飛んでくるので、メールに記載されたリンク先をクリックすれば良い)
5. SSL証明書の取得
SSL証明書を構築します。
具体的な手順は、こちらのドキュメントから確認が出来ます。
ドメインは前述で作成したものを利用すれば良いです。このタイミングでは、SSL証明書のステータスは、provisioningのままとなります。
6. Load Balancingの構築
Load Balancingを作成します。先ほどのSSL証明書の構築手順と関連して、こちらのドキュメントが参考になります。手順の概要は下記となります。
Load Balancingの設定
アプリケーションロードバランサーを設定
インターネット接続(外部)を設定
グローバルワークロード
グローバル外部アプリケーションロードバランサーを設定
フロントエンドの構成
静的IPの新規作成と設定
先に構築したSSL証明書を設定
バックエンドの構成
バックエンドサービスの新規作成
サーバーレスネットワークエンドポイントグループを選択し、新規作成
CDNは無効
ロギングは有効化
セキュリティポリシーの設定
サーバーレスネットワークエンドポイントグループの作成
リージョンを指定(東京に設定)
バックエンド側のCloudRunを指定
ルーティングルールの設定
パスごとにバックエンドのルーティング先を変更可能(今回は全パスで同一のバックエンドにルーティングするように設定)
7. DNSの設定
取得したドメインとLBの紐付けを行います。
今回はDNSプロバイダとして、Cloud DNSを利用します。Cloud DNSの画面から、LBのIPをwwwなどのAレコードに対して、設定します。詳細な手順はこちらをご覧ください。
8. IAPの設定
Identity Aware Proxyの設定を行い、バックエンド側のRunへのアクセス時にガードをかけるようにします。下記手順となりますが、全てgoogle cloud console上(IAPの設定画面)で実施が可能です。
IAPの有効化
対象となるバックエンドサービス(LB設定時に構築)のIAPを有効化する
CLIEND_ID, CLIENT_SECRETの確認
対象となるバックエンドサービスに対して、「OAuth構成に移動」から、確認が可能
ただし、IAPが有効の状態だと、上記メニューが確認不可(のように見える)
Identity Platformのアカウントを使うように設定
対象となるバックエンドサービスを指定し、「承認には外部 ID を使用します」をクリック
準備したIDPのEmail/Passwordを選択
ログインページを作成、を選択(自動でrun上にログインページが作成される。今回は利用しない)
設定より、HTTPオプションを有効にする
フロント側がstreamlitなどのpython clientの場合は不要だが、javascript経由のアクセスの場合は、CORS対策として必要となる
9. Cloud Runの構築(フロントエンド側)
最後にフロントエンド側のRunの設定を行い、デプロイします。
基本的な手順はこちらを参照してください。
注意点としては、secret/config.jsonのapiKeyやauthDomainは、Identity Platformの「アプリケーションの構成」画面から確認が可能です。
各フローの実装周りの話
最後に各フロー(前述した3ステップ)の実装周りの話について触れていきます。
ログイン認証 + IDトークンの保持
こちらの実装が参考になります。
class LoginHelper:
def __init__(self) -> None:
# Identity Platformの認証処理を行うためのインスタンスを取得
with open(os.path.join(os.path.dirname(__file__), "secret", "config.json")) as f:
firebase_config = json.load(f)
app = pyrebase.pyrebase.initialize_app(config=firebase_config)
self.__auth = app.auth()
def login(self, email: str, password: str) -> User:
try:
user = self.__auth.sign_in_with_email_and_password(email, password)
return User(email=email, id_token=user["idToken"])
except Exception as e:
raise IdentityPlatformException(f"Sign in Error is occured! Exception detail is {e}")
secret/config.jsonに格納した情報から、接続先のプロバイダー情報を判別して、接続をしてくれます。Firebase APIのラッパーライブラリであるpyrebaseを利用しています。
sign in処理で帰ってきたレスポンスからIDトークンを取得しています。(このIDトークンをバックエンドのリクエスト時に利用します)
バックエンドへのリクエスト
こちらの実装が参考になります。
def request_api(self, token: str, request_name: str, data: dict) -> Response:
url = f"{self.__backend_url}/{request_name}"
resp = requests.request(
"POST",
url,
data=json.dumps(data),
headers={"Content-Type": "application/json", "Authorization": "Bearer {}".format(token)},
)
if resp.status_code != 200:
raise Exception(
"Bad response from application: {!r} / {!r} / {!r}".format(resp.status_code, resp.headers, resp.text)
)
else:
return resp
シンプルにrestfulなAPIに対するリクエストではあるんですが、Authorization: Bearerヘッダに対して、先ほど認証処理時に取得したIDトークンを付与していることに注意です。
※このトークンがないと、バックエンドのRunの前段にあるIdentity Aware Proxyに弾かれて、リクエストがRunまで届きません。
まとめ
GCPのIdentity PlatformとIdentity Aware Proxyを利用して、特定のユーザーのみがサービスを使えるような環境を構築してみました。
GCP上で公開するサービスを作る際の参考になれば幸いです。ただし、考慮が不足している点(例えば、IDトークンの有効期限が切れたら、リフレッシュさせるなど)もあったりするので、サービス構築に必要な要件を整理して、適切にセキュアなサービスを構築するのが望ましいと思います。
Read next
Loading recommendations...