Amazon API GatewayのHTTP APIをAuth0と統合して保護してみる

Amazon API GatewayのHTTP APIを構築しつつAuth0を使って保護する機会があったので方法を記載します
2020.06.01

HTTP API は、低レイテンシーでコスト効率が良いAPIを提供するよう設計されていて、 OIDCとOAuth2.0認証をサポートしています。

オーソライザー HTTP API REST API
AWS Lambda
IAM
Amazon Cognito
OpenID Connect/OAuth 2.0

※ HTTP API および REST APIで使用できるオーソライザー

REST APIの場合はLambdaオーソライザーを使用してAuth0との統合が可能です。

HTTP APIの場合は、Lambdaオーソライザーが使用できないですが、OpenID Connect/OAuth 2.0の仕様に基づいて構築されたJWT(JSON Webトークン)オーソライザーを使うことにより認証と承認を処理できます。

以下のような構成です

やってみる

リソース

  • API Gateway
  • Lambda
  • Auth0 API

AWSでAPIを構築

SAM(Serverless Application Model) を用いて構築します。

sam initを実行しプロジェクトを作成します。

  • Quick Start Templatesを使用
  • runtimeはnodejs12.x
  • Project nameは任意のもの(default: sam-app)
  • application templatesはHello World
$ sam init
Which template source would you like to use?
	1 - AWS Quick Start Templates
	2 - Custom Template Location
Choice: 1

Which runtime would you like to use?
	1 - nodejs12.x
	2 - python3.8
	3 - ruby2.7
	4 - go1.x
	5 - java11
	6 - dotnetcore3.1
	7 - nodejs10.x
	8 - python3.7
	9 - python3.6
	10 - python2.7
	11 - ruby2.5
	12 - java8
	13 - dotnetcore2.1
Runtime: 1

Project name [sam-app]:

Cloning app templates from https://github.com/awslabs/aws-sam-cli-app-templates.git

AWS quick start application templates:
	1 - Hello World Example
	2 - Step Functions Sample App (Stock Trader)
	3 - Quick Start: From Scratch
	4 - Quick Start: Scheduled Events
	5 - Quick Start: S3
	6 - Quick Start: SNS
	7 - Quick Start: SQS
	8 - Quick Start: Web Backend
Template selection: 1

-----------------------
Generating application:
-----------------------
Name: sam-app
Runtime: nodejs12.x
Dependency Manager: npm
Application Template: hello-world
Output Directory: .

Next steps can be found in the README file at ./sam-app/README.md

HTTP APIを作成したいのでファイルを修正します。 template.yamlというファイルがあるので、そのファイルの22行目をType: HttpApiに変更します。 33行目の${ServerlessRestApi}${ServerlessHttpApi}に変更します。

$ $ cd sam-app/ ## initを実行してできたディレクトリ
$ vi template.yaml

~~~~~~~~
22           Type: HttpApi

33           Value: !Sub "https://${ServerlessHttpApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/hello/"
~~~~~~~~

sam buildを実行します。

$ sam build
Building function 'HelloWorldFunction'
Running NodejsNpmBuilder:NpmPack
Running NodejsNpmBuilder:CopyNpmrc
Running NodejsNpmBuilder:CopySource
Running NodejsNpmBuilder:NpmInstall
Running NodejsNpmBuilder:CleanUpNpmrc

Build Succeeded

Built Artifacts  : .aws-sam/build
Built Template   : .aws-sam/build/template.yaml

Commands you can use next
=========================
[*] Invoke Function: sam local invoke
[*] Deploy: sam deploy --guided

sam deploy --guidedを実行してデプロイします

  • Stack Name: スタック名
  • AWS Region; AWSリージョン
  • Confirm changes before deploy: Y
  • Allow SAM CLI IAM role creation: Y
  • HelloWorldFunction may not have authorization defined, Is this okay?: y
  • Save arguments to samconfig.toml: Y
  • Deploy this changeset?: y

デプロイが成功すると Successfully ~~~が表示されます。

$ sam deploy --guided
~~~~~
Successfully created/updated stack - sam-app in ap-northeast-1

AWSのコンソールを確認してみます。

API

Lambda Function

リソースが作成されていることが確認できたので、APIにアクセスしてみます。

$ $ curl https://<your endpoint>/hello
{"message":"hello world"}

hello worldというメッセージがjsonで返却されるはずです。 オーソライザーを設定していないため、誰でもアクセスできる状態になっています。

Auth0でAuth0 APIを構築(JWTオーソライザー)

Auth0のダッシュボードにログインし、APIs -> CREATE APIをクリックします

  • Name: AWS JWT Authorizer
  • Identifier: https://aws-jwt-authorizer
  • Signing Algorithm: RS256 (default)

にし、APIを作成します。

HTTP APIにJWTオーソライザーを構成する

/helloには誰でもアクセスできる状態ですが、承認されたユーザーへのリクエストを制限するようにAPIを構成します。

API Gatewayのコンソールにアクセスし、認可->hello->GET->オーソライザーを作成してアタッチをクリックします。

  • 名前(オーソライザーの名前): auth0
  • ID ソース: $request.header.Authorization
  • 発行者 URL: Auth0テナントのURL(https://[YOUR-TENANT-NAME].auth0.com/)
  • 対象: 作成したAuth0 APIの識別子 (https://aws-jwt-authorizer)

上記の内容を入力し、アタッチします。

作成されると以下のようになります。

確認してみる

では、この状態で一度APIにアクセスしてみます。

$ curl https://62c3157bka.execute-api.ap-northeast-1.amazonaws.com/hello
{"message":"Unauthorized"}

承認ができていないので、Unauthorizedというメッセージが返ってきます。 承認されたユーザーにしかアクセスできないようになりました。

オーソライザーの設定に ID ソース:$request.header.Authorization` を設定しましたが、 AuthorizationというHeaderにJWTオーソライザーから取得したトークンを付与しないといけません。

Auth0のダッシュボードにいき、作成したAPIのTestタブをクリックします。 そのページにテスト用のトークンが表示されているのでコピーします。

コピーが完了したら、その値を使ってもう一度APIにアクセスします。

$ export ACCESS_TOKEN="<コピーしたトークン>"
$ curl \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  https://62c3157bka.execute-api.ap-northeast-1.amazonaws.com/hello

トークンの検証が通っていると

{"message":"hello world"}

とメッセージが返ってきます。

これでアクセストークンによる保護ができました。

承認スコープを設定したりしてより細かな保護もできるので是非試してみてください。

まとめ

API GatewayでHTTP APIを構築し、そのAPIに対してAuth0で保護をかけました。 JWTオーソライザーを使用するとエンドポイントへのアクセスを制限できます。

Auth0のようなサービスを使用することにより独自の認証コードを作る必要がなくなるので、よろビジネスロジックに専念できるようになると思います。