Amazon API Gateway で Custom Authorization を使ってクライアントの認可を行う

API Gateway

Custom Authorization とは

2016年2月11日に Amazon API Gateway (以下 API Gateway) の新機能「Custom Authorization *1」がリリースされました。

Custom Authorization を使うと、 API にアクセスしてきたクライアントに対する認可の機能を組み込むことができるようになります。弊社内でも要望の多かった機能なので、このアップデートは喜ばしい限りです!

本ブログでは Custom Authorization を使うとどのような流れで認可が行えるのか、その仕組みを解説します。

認証と認可

Custom Authorization を理解するためには、そもそも「認証」と「認可」の違いを把握しておく必要があります。こちらについては、以前都元のブログで分かりやすく解説しています。

・認証というのは 通信の相手が誰(何)であるかを確認すること です。
・認可というのは とある特定の条件に対して、リソースアクセスの権限を与えること です。

よくわかる認証と認可 | Developers.IO

これが、認証と認可の違いです。 Custom Authorization は「認可」ですので、リソースアクセスの権限を与える話であることがわかります。

Custom Authorization の認可のフロー

次に、 Custom Authorization で認可が行われる一連の流れを見ていきましょう。公式の発表の図をもう少し分かりやすく書いてみました。

custom-auth-image

  1. クライアントから API にリクエストを行う
  2. API Gateway が Auth Function (Lambda) を呼び出す
  3. Auth Function が API Gateway に IAM ポリシーを返す
  4. IAM ポリシーに従って、API Gateway が認可を実施する

Custom Authorization の中で最も重要なのが、「Auth Function」と呼ばれる Lambda Function です。API Gateway で作成した API の設定で特定の Lambda Function を Auth Function に指定することができます(設定方法は後述)。この中で独自のロジックで認可する処理を行います。結果として IAM ポリシーの形式で返却すると、API Gateway がそのポリシーに従って認可を実施するという流れになっています。

Auth Function の中身

公式のドキュメントで紹介されている Auth Function の実装コードを覗いてみましょう(一部、読みやすいように修正しています)。

console.log('Loading function');

exports.handler = function(event, context) {

  // トークンを取得
  var token = event.authorizationToken;

  // ここではシンプルに「allow」「deny」「unauthorized」で判定
  switch (token) {
    case 'allow':
      // API へのリクエストを許可する
      context.succeed(generatePolicy('user', 'Allow', event.methodArn));
      break;
    case 'deny':
      // API へのリクエストを拒否する
      context.succeed(generatePolicy('user', 'Deny', event.methodArn));
      break;
    case 'unauthorized':
      // 評価に値しない場合など
      context.fail("Unauthorized");
      break;
    default:
      // エラー
      context.fail("error");
  }
};

var generatePolicy = function(principalId, effect, resource) {
  // IAM ポリシーを生成する
  return {
    principalId: principalId,
    policyDocument: {
      Version: '2012-10-17',
      Statement: [{
        Action: 'execute-api:Invoke',
        Effect: effect,
        Resource: resource
      }]
    }
  };
}

ハンドラーの第一引数の event には、API Gateway から呼び出された時に渡される、いくつかの情報が含まれます。具体的には次の通りです。

{
    "type":"TOKEN",
    "authorizationToken":"<設定したあるヘッダーの値>",
    "methodArn":"<呼び出されたメソッドを特定する Arn (Amazon Resource Name)>"
}  

もっとも重要な値は authorizationToken です。この値はクライアントからリクエストされた時のヘッダーの中から、自由に設定することができます(設定方法は後述)。この例では、あるヘッダーに allow や  deny などシンプルな文字列が渡される想定で、認可するポリシーを作るか拒否するポリシーを作るか分岐させています。

Custom Authorization を使ってみる

事前知識はこのくらいにしておいて、実際に Custom Authorization を使いながら理解しましょう。

まず、適当な API を作成しておきましょう。ここでは、先日紹介した CloudFormation Template で作成した API を使います。

cfn-api-gateway-02

次に Auth Function となる Lambda Function を作成します。

custom-auth-01

Blue Print は Skip します。次のように Lambda Function を設定します。

項目
Name authFunction
Runtime Node.js 4.3
Lambda function code 上述したコードをそのままペースト
Hander index.handler
Role Basic execution role
Advanced settings 全てデフォルトのままでOK

custom-auth-02

次に API に Custom Authorization を設定します。新たに「Custom Authorization」という項目が追加されているので、クリックします。右側に作成フォームが表示されます。

次のように設定します。

項目
Name CustomAuthorizer
Lambda region ap-northeast-1
Identity token source method.request.header.Authorization

custom-auth-03

「Identity token source」に設定したプロパティの値が Auth Function に渡される authorizationToken で受け取れます。ヘッダーに限らず、この書き方であれば、リクエストパラメータを指定することもできそうですね(試してませんが…)。

「Create」をクリックすると、次の画面が表示されます。「Execution role」を指定しない場合「Lambda function」に設定した Lambda function を Invoke することができる Role が付与されるようです。

custom-auth-04

Custom Authorization の作成が完了すると「Custom Authorizers」として一覧に追加されます。また、この画面で簡易的なテストを行うこともできます。

custom-auth-05

最後に Method の設定を変更します。「Method Request」の「Authorization Settings」の「Authorization」で、先ほど作成した「CustomAuthorizer」が選択できるようになっているはずです。

custom-auth-06

あとはデプロイすれば終わりです。 curl で叩いてみましょう。まずヘッダーを付けずに呼ぶと Unauthorized となります。

$ curl https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/prd/sample 
{"message":"Unauthorized"}

ヘッダーに Authorization: deny を付けると、アクセス拒否されます。

$ curl -H 'Authorization: deny' https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/prd/sample 
{"Message":"User is not authorized to access this resource"}

ヘッダーに Authorization: allow を付けると、成功します(正常系のレスポンス設定してなかったので何も起きませんすいません!)。

$ curl -H 'Authorization: allow' https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/prd/sample 

まとめ

既存の認証・認可システムと組み合わせても良し、もっとライトに使っても良し!拡張性が非常に高い機能という印象を受けました。利用用途はかなり多いはずなので、ぜひ使っていきましょう!

Amazon Cognito の User Pools と組み合わせて、何かできないかなぁと試行錯誤してます。

参考

脚注

  1. 公式の和訳では「カスタム認証」とされていますが、個人的には「カスタム認可」の方が適切だと思うのですが。。。