APIGateway Authorizerの良い点・惜しい点

AWSでAPIGatewayを使うシーンはよくあると思いますが、最近Authorizerを使ってAPIGatewayで認証を行う仕組みを使う機会があり、これがなかなか便利だったので簡単に使い方などをまとめてみたいと思います。

Authorizerとは

Authorizerとは、APIGatewayでリクエストの認証を行う仕組みです。デフォルトでは、APIGatewayはリクエストを全部BackendのLambdaなりEC2なりに流しますが、Authorizerを定義しておくことで「認証されたリクエストのみBackendに流し、認証されていないものは401エラーを返す」という挙動をしてくれるようになります。

Authorizerの種類

Authorizerには大きく以下の2種類があります。
① Cognito
② Lambda

① Cognitoは、その名の通りCognitoを使った認証方式です。設定はシンプルで、Cognito User Poolの情報と、Token Sourceを入力するだけです。Cognitoではユーザ認証が完了するとJWT形式のID Tokenが発行されるのですが、そのID TokenをToken Sourceで指定したhttpヘッダに入れてリクエストすれば、認証が通るという仕組みです。

画像1

② Lambdaは、こちらもその名の通りLambdaを使った認証方式です。リクエストを受けた際にAuthorizerで指定されたLambda Functionを実行し、そこで実装された認証ロジックをパスすれば認証が通ります。Lambdaで独自ロジックを定義できるので、複雑な認証を行いたい場合でも対応できます。

画像2

Authorizerの良い点

・Backendのアプリで認証ロジックを持つ必要がない
当然と言えば当然のメリットですが、APIGatewayのレイヤで認証を通しておくことで、Backend側での認証ロジックが不要となります。

・Lambdaで複雑な認証処理も可能
Cognito認証だけでも便利なのですが、Lambdaを呼び出して独自認証ロジックを持たせられるのがとても便利です。Managed Serviceでよくある「痒いところに手が届かない」みたいな事態を確実に避けることができます。

・メソッド単位での指定が可能
Authorizerはメソッド単位で指定ができるので、「このメソッドは認証無し、このメソッドはCognito認証」のように細かい使い分けが可能です。APIごとに認証レベルを変えたい場合でも対応できるので、地味に便利なポイントです。

Authorizerの惜しい点

・Cognito認証でCookieデータが使えない
Cognito認証は所定のhttpヘッダにID Tokenを入れて認証するという仕組みですが、できればこういったTokenデータはCookieに持たせたいです。しかしCognito認証ではCookieのデータをうまく拾ってくれる機能は無いようで、現状ではカスタムのhttpヘッダを追加してそこにID Tokenを入れるしかありません。そうするとCookieの方のTokenもhttponlyを外してjavascriptで読めるようにしておかないといけなくなり、セキュリティ的な不安が出てきてしまいます。
Cookie情報はLambdaには渡せるので、Lambda認証にすればhttpヘッダにTokenを入れる必要もないのですが、そのためだけにLambda認証にするのももったいない感じです。

・ID Tokenとユーザ名の検証くらいはCognito認証でやりたい
Cognito認証では、「送られてきたID Tokenが有効かどうか」だけを検証しています。しかし本当はこれだけでは認証としては不十分で、「ID Tokenをデコードして得られるユーザ名と、リクエストに含まれるユーザ名(もしくはユーザを一意に識別する情報)が一致している」ことをもって認証するのがより安全です。そうでないと、「別のユーザが他人のTokenを使って認証する」といういわゆるなりすまし的なことができてしまいます。
これもLambdaを使えば実現できるのですが、ID Token + ユーザ名検証くらいはわざわざ独自のLambdaを作らなくてもできてほしいところです。認証の機能としては最低限必要なところだと思うので。。

終わりに

Cognito認証では上記2点の惜しい点があることから、実際にAuthorizerを使う際は今のところはLambda認証を使うのが現実的な手段かなと思います。逆にLambda認証を使うデメリットは「独自のLambda Functionを作って運用しないといけない」くらいなので、それが苦にならない場合はぜひ使ってみてください。

参考:Authorizer作成のCloudFormationコード例

# Cognito type
APIGatewayAuthorizerCognito:
  Type: AWS::ApiGateway::Authorizer
  Properties:
    IdentitySource: 'method.request.header.Authorization'
    Name: !Join ['-', [!Ref AWS::StackName, 'cognito']]
    ProviderARNs:
      - !GetAtt CognitoUserPoolAdminTool.Arn
    RestApiId: !Ref APIGateway
    Type: COGNITO_USER_POOLS
# Lambda type
APIGatewayAuthorizerLambda:
  Type: AWS::ApiGateway::Authorizer
  Properties:
    AuthorizerResultTtlInSeconds: 0
    AuthorizerUri: !Join ['', ['arn:aws:apigateway:', !Ref AWS::Region, ':lambda:path/2015-03-31/functions/arn:aws:lambda:', !Ref AWS::Region, ':', !Ref AWS::AccountId, ':function:', !Ref LambdaFunctionAuthorizer, '/invocations']]
    Name: !Join ['-', [!Ref AWS::StackName, 'lambda']]
    RestApiId: !Ref APIGateway
    Type: REQUEST

この記事が気に入ったらサポートをしてみませんか?