【初心者向け】Google Cloud API Gatewayを使って、認証付きWeb APIを作成してみた
MAD事業部の佐藤@札幌です。
この記事は クラスメソッド Google Cloud Advent Calendar 2021 22日目の記事です。Google Cloud ほとんど触ったことないのに勢いで社内のGoogle Cloudアドベントカレンダーに申し込んでしまいました。私自身、AWSではよく Amazon API Gateway を使って開発をしていますが、Google Cloudにも API GatewayというフルマネージドなAPIサービスが 2020年の9月にβ版としてリリースされていました。そこで今回は、API Gatewayと Cloud Functionsを使って認証付きWebAPIを作成してみたいと思います。
Google Cloud API Gatewayとは
主にサーバーレスバックエンド(Cloud Functions、Cloud Run、App Engine)向けのWeb APIを統合的に管理することができます。JWT認証やAPI Keyの認証をサポートし、またロギングやモニタリングなどの機能をフルマネージドで提供してくれるサービスです。内部ではEnvoyを基盤に構築されているみたいです。
Cloud Functionsにサンプルアプリケーションをデプロイする
まずはCloud Functionsに、API Gatewayからアクセスされるバックエンドを作成します。クイックスタートベースでデプロイしていきます。Google Cloudのコンソールにアクセスし、CloudShellを起動します。以降はシェルについては、CloudShell上で実行します。
GitHubからサンプルプロジェクトをダウンロードします
Google Cloudが用意しているNode.jsのサンプルプロジェクトをダウンロードして、プロジェクトに移動します。
git clone https://github.com/GoogleCloudPlatform/nodejs-docs-samples.git cd nodejs-docs-samples/functions/helloworld
サンプルコードは、Hello World!とレスポンスするだけのシンプルなアプリケーションです。
exports.helloGET = (req, res) => { res.send('Hello World!'); };
Cloud Functionsにデプロイする
サンプルプロジェクトが用意できたので、Cloud Functionsにデプロイします。ランタイムは nodejs16
とします。
gcloud functions deploy helloGET \ --runtime nodejs16 --trigger-http --allow-unauthenticated
これだけです。簡単ですね。コンソールでも確認してみます。以下のようにデプロイされていることが確認できました。
API GatewayのAPIを作成する
次にAPI GatewayのAPIリソースを作成します。API Gatewayについても CloudShell上で作成していきます。
gcloud api-gateway apis create sample-api
API Gatewayのコンソールに移動して、APIが作成されていることを確認します。
デフォルトのサービスアカウントに必要なロールを紐付ける
APIが作成できたら、次にこれ以降の設定を行うためにサービスアカウントに必要なロールと権限を付与します。
gcloud iam service-accounts add-iam-policy-binding [email protected] \ --member user:xxx \ --role roles/iam.serviceAccountUser
構成を作成する
API が作成できたら次にCloud Functionsにアクセスするための構成を作成していきます。Google Cloud API Gatewayの構成とは、OpenAPI(Swagger)のyamlファイルのことです。これをコンソールからアップロードします。一部Google Cloud用の特殊なアノテーション x-google-backend
というものを付与する必要があります。これに対して、Cloud FunctionsのURLを指定します。このyamlファイルをプロジェクトフォルダに置いておきます。
swagger: '2.0' info: title: sample-api description: Sample API on API Gateway with a Google Cloud Functions backend version: 1.0.0 schemes: - https produces: - application/json paths: /hello: get: summary: Greet a user operationId: hello x-google-backend: address: https://xxx.cloudfunctions.net/helloGET responses: '200': description: A successful response schema: type: string
構成をデプロイします。
gcloud api-gateway api-configs create sample-config \ --api=sample-api --openapi-spec=openapi.yaml
ゲートウェイを作成する
次に今アップロードしたOpenAPIのyamlファイルをゲートウェイにデプロイします。ゲートウェイにデプロイすると外部に公開されるURLが払い出され、OpenAPIの定義にそったパスでルーティングが行われます。
gcloud api-gateway gateways create sample-gateway \ --api=sample-api --api-config=sample-config --location asia-northeast1
構成とゲートウェイが作成できたら、コンソールで確認してみます。
構成タブでは、openapiの定義ファイルが出力されています。
ゲートウェイタブでは、ゲートウェイのURLが払い出されています。これが、外部公開されているURLとなります。
このURLで先程の hello
パスにアクセスしてみます。Cloud Functionsが実行され、 Hello World!
が表示されました。
APIの概要タブを見てみると、リクエストやエラー、レイテンシなどの情報がリアルタイムに表示されるダッシュボードが作られています。
ログについては、詳細タブから飛べるようになっていて、以下のようにアクセスログが表示されています。
API GatewayにAuth0の認証をつけてみる
次に、API Gatewayに対して認証をつけてみたいと思います。API Gatewayでは、JWT(JSON Web Token)による認証がサポートされているので、これを使って認証付きAPIを作成します。今回は、Auth0を使用したJWT認証を行います。ここから、Auth0にアカウントを作成して適当なテナントを作成していることを前提に、Auth0のコンソールにログインして設定していきます。
Auth0でAPIを作成する
まずは、APIを作成します。APIsタブから Create API して、適当なAPIを作成します。この際、Identifierの項目には払い出されたAPI GatewayのURLを指定します。
JWTを取得する
APIが作成できたら、アクセストークンが取得できることを確認します。Testタブに移動しcurlのコマンドをコピーしてターミナルで実行します。
以下のようなJSONがレスポンスされれば成功です。後ほど使うので access_token
をメモっておきます。
{ "access_token": "xxxxx", "expires_in": 86400, "token_type": "Bearer" }
API Gatewayに認証をサポートするように修正する
先程作成したAPI GatewayにJWTでクレームを検証できるように、OpenAPIのyamlを修正して、再度アップロードします。yamlを以下の用に修正します。 securityDefinitions
と security
を追加しています。 x-google-issuer
と x-google-jwks-uri
にはAuth0のテナントのドメインを入力します。 x-google-audiences
には、 ゲートウェイのエンドポイントを入力します。
swagger: '2.0' info: title: sample-api description: Sample API on API Gateway with a Google Cloud Functions backend version: 1.0.0 securityDefinitions: auth0_jwk: authorizationUrl: "" flow: "implicit" type: "oauth2" x-google-issuer: "https://xxx.us.auth0.com/" x-google-jwks_uri: "https://xxx.us.auth0.com/.well-known/jwks.json" x-google-audiences: "https://xxx.an.gateway.dev/" security: - auth0_jwk: [] schemes: - https produces: - application/json paths: /hello: get: summary: Greet a user operationId: hello x-google-backend: address: https://xxx.cloudfunctions.net/helloGET responses: '200': description: A successful response schema: type: string
yamlを修正したら、構成ファイルとして新たに追加します。
gcloud api-gateway api-configs create auth0-oauth2 --api=sample-api --openapi-spec=openapi.yaml
追加できたら、ゲートウェイの構成を追加した構成を使うように更新します。
gcloud api-gateway gateways update sample-gateway --api=sample-api --api-config=gogogle-oauth2 --location asia-northeast1
これで認証付きのAPI Gatewayを作成することができました。
ゲートウェイにリクエストしてみる
ヘッダにトークンを付与しないでリクエスト
認証付きのゲートウェイを作成できたので、リクエストして検証してみます。まずは、ヘッダにトークンを付与しない状態で /hello
パスにアクセスしてみます。
Jwt is missing
というエラーメッセージと401がレスポンスされました。トークンの検証処理が内部で行われていることが確認できます。
Bearerトークンを付与してリクエスト
クエリストリングに access_token
を付与してリクエストしました。Hello World!と表示されました。JWTの検証が行われ CloudFunctions にリクエストが行ってることを確認できました。
まとめ
Google CloudのAPI Gatewayを触ってみました。OpenAPIのyamlを用意して特殊なアノテーションを記述するだけで、フルマネージドなWeb APIを作成することができました。OpenAPIのyamlをアップロードする以外に設定項目がほとんどなく、コンソールからできることはかなり限られていて、かなり割り切ったシンプルな印象を受けました。基本的には、複数のCloudFunctionsのエンドポイントをまとめるためにAPI Gateway + Cloud Functionsという構成で使うのが良さそうと思いました。
参考
https://cloud.google.com/api-gateway/docs/quickstart
https://cloud.google.com/api-gateway/docs/authenticating-users-auth0