

# API Gateway Lambda オーソライザーを使用する
<a name="apigateway-use-lambda-authorizer"></a>

Lambda オーソライザー (以前はカスタムオーソライザーと呼ばれていました) は、API へのアクセスを制御するために使用します。****クライアントが API のメソッドへのリクエストを行うと、API Gateway は Lambda オーソライザーを呼び出します。Lambda オーソライザーは、発信者の ID を入力として受け取り、IAM ポリシーを出力として返します。

Lambda オーソライザーを使用して、カスタム認可スキームを実装します。スキームでは、リクエストパラメータを使用して、発信者のアイデンティティを判断したり、OAuth や SAML などのベアラートークン認証戦略を使用したりできます。Lambda オーソライザーは、API Gateway REST API コンソール、AWS CLI、または AWS SDK を使用して作成します。

## Lambda オーソライザーの認可ワークフロー
<a name="api-gateway-lambda-authorizer-flow"></a>

次の図は、Lambda オーソライザーの認可ワークフローを示しています。

![API Gateway Lambda 認可ワークフロー](http://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/images/custom-auth-workflow.png)


**API Gateway Lambda 認可ワークフロー**

1. クライアントは、API Gateway API でメソッドを呼び出し、ベアラートークンまたはリクエストパラメータを渡します。

1. API Gateway は、メソッドリクエストが Lambda オーソライザーで設定されているかどうかを確認します。存在する場合、API Gateway は Lambda 関数を呼び出します。

1. Lambda 関数は発信者を認証します。関数は次の方法で認証できます。
   + OAuth プロバイダーを呼び出して OAuth アクセストークンを取得します。
   + SAML プロバイダーを呼び出して SAML アサーションを取得します。
   + リクエストパラメータ値に基づいて IAM ポリシーを生成します。
   + データベースから認証情報を取得します。

1. Lambda 関数は、IAM ポリシーとプリンシパル識別子を返します。Lambda 関数がこの情報を返さない場合、呼び出しは失敗します。

1. API Gateway は IAM ポリシーを評価します。
   + アクセスが拒否された場合、API Gateway は `403 ACCESS_DENIED` などの適切な HTTP ステータスコードを返します。
   + アクセスが許可されている場合、API Gateway はメソッドを呼び出します。

     認可のキャッシュを有効にすると、API Gateway でポリシーがキャッシュされるため、Lambda オーソライザー関数は再度呼び出されません。ポリシーが API 全体のすべてのリソースとメソッドに適用されていることを確認します。

`403 ACCESS_DENIED` または `401 UNAUTHORIZED` ゲートウェイレスポンスはカスタマイズできます。詳細については[API Gateway での REST API のゲートウェイレスポンス](api-gateway-gatewayResponse-definition.md)を参照してください。

## Lambda オーソライザーのタイプの選択
<a name="api-gateway-lambda-authorizer-choose"></a>

Lambda オーソライザーには 2 種類あります。

**リクエストパラメータベースの Lambda オーソライザー (`REQUEST` オーソライザー)**  
`REQUEST` オーソライザーは、発信者 ID をヘッダー、クエリ文字列パラメータ、[`stageVariables`](api-gateway-mapping-template-reference.md#stagevariables-template-reference)、[`$context`](api-gateway-mapping-template-reference.md#context-variable-reference) 変数の組み合わせとして受け取ります。`REQUEST` オーソライザーを使用して、`$context.path` や `$context.httpMethod` コンテキスト変数など、複数の ID ソースからの情報に基づいてきめ細かなポリシーを作成できます。  
`REQUEST` オーソライザーの認可のキャッシュを有効にすると、API Gateway は、指定されたすべての ID ソースがリクエスト内に存在することを確認します。指定された ID ソースが欠落しているか、null または空である場合、API Gateway は、Lambda オーソライザー関数を呼び出すことなく `401 Unauthorized` HTTP レスポンスを返します。複数の ID ソースが定義されている場合は、すべての ID ソースがオーソライザーのキャッシュキーを取得するために使用されます (順序は保持されます)。複数の ID ソースを使用して、きめ細かなキャッシュキーを定義できます。  
キャッシュキーのいずれかの部分を変更して API を再デプロイすると、オーソライザーは、キャッシュされたポリシードキュメントを破棄して新しいドキュメントを作成します。  
`REQUEST` オーソライザーの認可のキャッシュを無効にすると、API Gateway はリクエストを Lambda 関数に直接渡します。

**トークンベースの Lambda オーソライザー (`TOKEN` オーソライザー)**  
`TOKEN` オーソライザーは、JSON ウェブトークン (JWT) や OAuth トークンなどのベアラートークンで発信者 ID を受け取ります。  
`TOKEN` オーソライザーの認可のキャッシュを有効にすると、トークンソースに指定されているヘッダー名がキャッシュキーになります。  
さらに、トークン検証を使用して RegEx ステートメントを入力できます。API Gateway は、この式に対して入力トークンの初期検証を実行し、検証が成功すると Lambda オーソライザー関数を呼び出します。これにより API への呼び出しを減らすことができます。  
`IdentityValidationExpression` プロパティは `TOKEN` オーソライザーでのみサポートされています。詳細については、「[x-amazon-apigateway-authorizer オブジェクト](api-gateway-swagger-extensions-authorizer.md)」を参照してください。

**注記**  
`REQUEST` オーソライザーを使用して API へのアクセスを制御することをお勧めします。`REQUEST` オーソライザーを使用すると、(`TOKEN` オーソライザーを使用した場合の単一の ID ソースと比べて) 複数の ID ソースに基づく API へのアクセスを制御できます。さらに、`REQUEST` オーソライザーでは複数の ID ソースを使用してキャッシュキーを分離できます。

## `REQUEST` オーソライザー Lambda 関数の例
<a name="api-gateway-lambda-authorizer-request-lambda-function-create"></a>

次のコード例で作成する Lambda オーソライザー関数では、クライアントが提供した `HeaderAuth1` ヘッダー、`QueryString1` クエリパラメータ、ステージ変数 `StageVar1` のすべてが、それぞれ `headerValue1`、`queryValue1`、`stageValue1` の指定された値と一致する場合に、リクエストを許可します。

------
#### [ Node.js ]

```
// A simple request-based authorizer example to demonstrate how to use request 
// parameters to allow or deny a request. In this example, a request is  
// authorized if the client-supplied HeaderAuth1 header, QueryString1
// query parameter, and stage variable of StageVar1 all match
// specified values of 'headerValue1', 'queryValue1', and 'stageValue1',
// respectively.
    
export const handler = function(event, context, callback) {
    console.log('Received event:', JSON.stringify(event, null, 2));
    
    // Retrieve request parameters from the Lambda function input:
    var headers = event.headers;
    var queryStringParameters = event.queryStringParameters;
    var pathParameters = event.pathParameters;
    var stageVariables = event.stageVariables;
        
    // Parse the input for the parameter values
    var tmp = event.methodArn.split(':');
    var apiGatewayArnTmp = tmp[5].split('/');
    var awsAccountId = tmp[4];
    var region = tmp[3];
    var restApiId = apiGatewayArnTmp[0];
    var stage = apiGatewayArnTmp[1];
    var method = apiGatewayArnTmp[2];
    var resource = '/'; // root resource
    if (apiGatewayArnTmp[3]) {
        resource += apiGatewayArnTmp[3];
    }
        
    // Perform authorization to return the Allow policy for correct parameters and 
    // the 'Unauthorized' error, otherwise.

     
    if (headers.HeaderAuth1 === "headerValue1"
        && queryStringParameters.QueryString1 === "queryValue1"
        && stageVariables.StageVar1 === "stageValue1") {
        callback(null, generateAllow('me', event.methodArn));
    }  else {
        callback(null, generateDeny('me', event.methodArn));
    }
}
     
// Help function to generate an IAM policy
var generatePolicy = function(principalId, effect, resource) {
    // Required output:
    var authResponse = {};
    authResponse.principalId = principalId;
    if (effect && resource) {
        var policyDocument = {};
        policyDocument.Version = '2012-10-17'; // default version
        policyDocument.Statement = [];
        var statementOne = {};
        statementOne.Action = 'execute-api:Invoke'; // default action
        statementOne.Effect = effect;
        statementOne.Resource = resource;
        policyDocument.Statement[0] = statementOne;
        authResponse.policyDocument = policyDocument;
    }
    // Optional output with custom properties of the String, Number or Boolean type.
    authResponse.context = {
        "stringKey": "stringval",
        "numberKey": 123,
        "booleanKey": true
    };
    return authResponse;
}
     
var generateAllow = function(principalId, resource) {
    return generatePolicy(principalId, 'Allow', resource);
}
     
var generateDeny = function(principalId, resource) {
    return generatePolicy(principalId, 'Deny', resource);
}
```

------
#### [ Python ]

```
# A simple request-based authorizer example to demonstrate how to use request
# parameters to allow or deny a request. In this example, a request is
# authorized if the client-supplied headerauth1 header, QueryString1
# query parameter, and stage variable of StageVar1 all match
# specified values of 'headerValue1', 'queryValue1', and 'stageValue1',
# respectively.

def lambda_handler(event, context):
    print(event)

    # Retrieve request parameters from the Lambda function input:
    headers = event['headers']
    queryStringParameters = event['queryStringParameters']
    pathParameters = event['pathParameters']
    stageVariables = event['stageVariables']

    # Parse the input for the parameter values
    tmp = event['methodArn'].split(':')
    apiGatewayArnTmp = tmp[5].split('/')
    awsAccountId = tmp[4]
    region = tmp[3]
    restApiId = apiGatewayArnTmp[0]
    stage = apiGatewayArnTmp[1]
    method = apiGatewayArnTmp[2]
    resource = '/'

    if (apiGatewayArnTmp[3]):
        resource += apiGatewayArnTmp[3]

    # Perform authorization to return the Allow policy for correct parameters
    # and the 'Unauthorized' error, otherwise.

    if (headers['HeaderAuth1'] == "headerValue1" and queryStringParameters['QueryString1'] == "queryValue1" and stageVariables['StageVar1'] == "stageValue1"):
        response = generateAllow('me', event['methodArn'])
        print('authorized')
        return response
    else:
        print('unauthorized')
        response = generateDeny('me', event['methodArn'])
        return response
    # Help function to generate IAM policy


def generatePolicy(principalId, effect, resource):
    authResponse = {}
    authResponse['principalId'] = principalId
    if (effect and resource):
        policyDocument = {}
        policyDocument['Version'] = '2012-10-17'
        policyDocument['Statement'] = []
        statementOne = {}
        statementOne['Action'] = 'execute-api:Invoke'
        statementOne['Effect'] = effect
        statementOne['Resource'] = resource
        policyDocument['Statement'] = [statementOne]
        authResponse['policyDocument'] = policyDocument

    authResponse['context'] = {
        "stringKey": "stringval",
        "numberKey": 123,
        "booleanKey": True
    }

    return authResponse


def generateAllow(principalId, resource):
    return generatePolicy(principalId, 'Allow', resource)


def generateDeny(principalId, resource):
    return generatePolicy(principalId, 'Deny', resource)
```

------

この例では、Lambda オーソライザー関数は入力パラメータをチェックして次のように動作します。
+ 必要なすべてのパラメータ値が予想値と一致する場合、オーソライザー関数は `200 OK` HTTP レスポンスと次のような IAM ポリシーを返し、メソッドリクエストは成功します。

------
#### [ JSON ]

****  

  ```
  {
    "Version":"2012-10-17",		 	 	 
    "Statement": [
      {
        "Action": "execute-api:Invoke",
        "Effect": "Allow",
        "Resource": "arn:aws:execute-api:us-east-1:123456789012:ivdtdhp7b5/ESTestInvoke-stage/GET/"
      }
    ]
  }
  ```

------
+ 一致しない場合、オーソライザー関数は `401 Unauthorized` HTTP レスポンスを返し、メソッドリクエストは失敗します。

Lambda オーソライザー関数は、IAM ポリシーだけでなく、発信者のプリンシパル ID を返す必要があります。オプションで、`context` オブジェクトを返すこともできます。このオブジェクトには、統合バックエンドに渡すことができる追加情報が含まれています。詳細については、「[API Gateway Lambda オーソライザーからの出力](api-gateway-lambda-authorizer-output.md)」を参照してください。

本番稼働コードでは、認可を付与する前にユーザーの認証が必要になる場合があります。この場合、認証プロバイダーを (関連ドキュメントの指示に従って) 呼び出すことで、Lambda 関数に認証ロジックを追加できます。

## `TOKEN` オーソライザー Lambda 関数の例
<a name="api-gateway-lambda-authorizer-token-lambda-function-create"></a>

次のコード例で作成する `TOKEN` Lambda オーソライザー関数では、クライアントが提供したトークン値が `allow` の場合に、メソッドを呼び出すことを発信者に許可します。トークン値が `deny` の場合、発信者はリクエストを呼び出すことを許可されません。トークン値が `unauthorized` または空の文字列の場合、オーソライザー関数は `401 UNAUTHORIZED` レスポンスを返します。

------
#### [ Node.js ]

```
// A simple token-based authorizer example to demonstrate how to use an authorization token 
// to allow or deny a request. In this example, the caller named 'user' is allowed to invoke 
// a request if the client-supplied token value is 'allow'. The caller is not allowed to invoke 
// the request if the token value is 'deny'. If the token value is 'unauthorized' or an empty
// string, the authorizer function returns an HTTP 401 status code. For any other token value, 
// the authorizer returns an HTTP 500 status code. 
// Note that token values are case-sensitive.

export const handler =  function(event, context, callback) {
    var token = event.authorizationToken;
    switch (token) {
        case 'allow':
            callback(null, generatePolicy('user', 'Allow', event.methodArn));
            break;
        case 'deny':
            callback(null, generatePolicy('user', 'Deny', event.methodArn));
            break;
        case 'unauthorized':
            callback("Unauthorized");   // Return a 401 Unauthorized response
            break;
        default:
            callback("Error: Invalid token"); // Return a 500 Invalid token response
    }
};

// Help function to generate an IAM policy
var generatePolicy = function(principalId, effect, resource) {
    var authResponse = {};
    
    authResponse.principalId = principalId;
    if (effect && resource) {
        var policyDocument = {};
        policyDocument.Version = '2012-10-17'; 
        policyDocument.Statement = [];
        var statementOne = {};
        statementOne.Action = 'execute-api:Invoke'; 
        statementOne.Effect = effect;
        statementOne.Resource = resource;
        policyDocument.Statement[0] = statementOne;
        authResponse.policyDocument = policyDocument;
    }
    
    // Optional output with custom properties of the String, Number or Boolean type.
    authResponse.context = {
        "stringKey": "stringval",
        "numberKey": 123,
        "booleanKey": true
    };
    return authResponse;
}
```

------
#### [ Python ]

```
# A simple token-based authorizer example to demonstrate how to use an authorization token
# to allow or deny a request. In this example, the caller named 'user' is allowed to invoke
# a request if the client-supplied token value is 'allow'. The caller is not allowed to invoke
# the request if the token value is 'deny'. If the token value is 'unauthorized' or an empty
# string, the authorizer function returns an HTTP 401 status code. For any other token value,
# the authorizer returns an HTTP 500 status code.
# Note that token values are case-sensitive.

import json


def lambda_handler(event, context):
    token = event['authorizationToken']
    if token == 'allow':
        print('authorized')
        response = generatePolicy('user', 'Allow', event['methodArn'])
    elif token == 'deny':
        print('unauthorized')
        response = generatePolicy('user', 'Deny', event['methodArn'])
    elif token == 'unauthorized':
        print('unauthorized')
        raise Exception('Unauthorized')  # Return a 401 Unauthorized response
        return 'unauthorized'
    try:
        return json.loads(response)
    except BaseException:
        print('unauthorized')
        return 'unauthorized'  # Return a 500 error


def generatePolicy(principalId, effect, resource):
    authResponse = {}
    authResponse['principalId'] = principalId
    if (effect and resource):
        policyDocument = {}
        policyDocument['Version'] = '2012-10-17'
        policyDocument['Statement'] = []
        statementOne = {}
        statementOne['Action'] = 'execute-api:Invoke'
        statementOne['Effect'] = effect
        statementOne['Resource'] = resource
        policyDocument['Statement'] = [statementOne]
        authResponse['policyDocument'] = policyDocument
    authResponse['context'] = {
        "stringKey": "stringval",
        "numberKey": 123,
        "booleanKey": True
    }
    authResponse_JSON = json.dumps(authResponse)
    return authResponse_JSON
```

------

この例では、API がメソッドリクエストを受信すると、API Gateway は `event.authorizationToken` 属性でこの Lambda オーソライザー関数にソーストークンを渡します。Lambda オーソライザー関数はトークンを読み取り、次のように動作します。
+ トークン値が `allow` の場合、オーソライザー関数は `200 OK` HTTP レスポンスと次のような IAM ポリシーを返し、メソッドリクエストは成功します。

------
#### [ JSON ]

****  

  ```
  {
    "Version":"2012-10-17",		 	 	 
    "Statement": [
      {
        "Action": "execute-api:Invoke",
        "Effect": "Allow",
        "Resource": "arn:aws:execute-api:us-east-1:123456789012:ivdtdhp7b5/ESTestInvoke-stage/GET/"
      }
    ]
  }
  ```

------
+ トークン値が `deny` の場合、オーソライザー関数は `200 OK` HTTP レスポンスと次のような `Deny` IAM ポリシーを返し、メソッドリクエストは失敗します。

------
#### [ JSON ]

****  

  ```
  {
    "Version":"2012-10-17",		 	 	 
    "Statement": [
      {
        "Action": "execute-api:Invoke",
        "Effect": "Deny",
        "Resource": "arn:aws:execute-api:us-east-1:123456789012:ivdtdhp7b5/ESTestInvoke-stage/GET/"
      }
    ]
  }
  ```

------
**注記**  
テスト環境以外では、API Gateway は `403 Forbidden` HTTP レスポンスを返し、メソッドリクエストは失敗します。
+ トークン値が `unauthorized` または空の文字列の場合、オーソライザー関数は `401 Unauthorized` HTTP レスポンスを返し、メソッド呼び出しは失敗します。
+ それ以外のトークンの場合、クライアントは `500 Invalid token` レスポンスを受け取り、メソッド呼び出しは失敗します。

Lambda オーソライザー関数は、IAM ポリシーだけでなく、発信者のプリンシパル ID を返す必要があります。オプションで、`context` オブジェクトを返すこともできます。このオブジェクトには、統合バックエンドに渡すことができる追加情報が含まれています。詳細については、「[API Gateway Lambda オーソライザーからの出力](api-gateway-lambda-authorizer-output.md)」を参照してください。

本番稼働コードでは、認可を付与する前にユーザーの認証が必要になる場合があります。この場合、認証プロバイダーを (関連ドキュメントの指示に従って) 呼び出すことで、Lambda 関数に認証ロジックを追加できます。

## その他の Lambda オーソライザー関数の例
<a name="api-gateway-lambda-authorizer-lambda-function-create"></a>

次のリストは、その他の Lambda オーソライザー関数の例を示しています。Lambda 関数は、API を作成したのと同じアカウントまたは別のアカウントで作成できます。

前の例に示した Lambda 関数の場合は、他の AWS のサービスを呼び出さないため、組み込みの [AWSLambdaBasicExecutionRole](https://docs.aws.amazon.com/lambda/latest/dg/lambda-intro-execution-role.html) を使用できます。Lambda 関数から他の AWS のサービスを呼び出す場合は、Lambda 関数に IAM 実行ロールを割り当てる必要があります。ロールを作成するには、「[AWS Lambda 実行ロール](https://docs.aws.amazon.com/lambda/latest/dg/lambda-intro-execution-role.html)」の手順に従ってください。

**その他の Lambda オーソライザー関数の例**
+  サンプルアプリケーションについては、GitHub の「[Open Banking Brazil - Authorization Samples](https://github.com/aws-samples/openbanking-brazilian-auth-samples)」を参照してください。
+  その他のサンプルの Lambda 関数については、GitHub の「[aws-apigateway-lambda-authorizer-blueprints](https://github.com/awslabs/aws-apigateway-lambda-authorizer-blueprints)」を参照してください。
+ Amazon Cognito ユーザープールを使用してユーザーを認証するとともに Verified Permissions を使用してポリシーストアに基づいて発信者を認証する Lambda オーソライザーを作成できます。詳細については、「[Verified Permissions を使用して ID の属性に基づいてアクセスを制御する](apigateway-lambda-authorizer-verified-permissions.md)」を参照してください。
+ Lambda コンソールには Python の設計図が用意されています。[**(設計図を使用する**] を選択して [**api-gateway-authorizer-python**] 設計図を選択すると使用できます。