AWS Lambda REQUEST オーソライザーを使用して WebSocket API へのアクセスを制御する
WebSocket API の Lambda オーソライザー関数は、REST API と似ていますが、次のような例外があります。
-
Lambda オーソライザー関数は、$connect
ルートにのみ使用できます。
-
パス変数 (event.pathParameters
) を使用することはできません。これは、パスが固定されているためです。
-
event.methodArn
は、HTTP メソッドがないため、REST API の同等のメソッドとは異なります。$connect
の場合、methodArn
は "$connect"
で終了します。
arn:aws:execute-api:region
:account-id
:api-id
/stage-name
/$connect
-
event.requestContext
のコンテキスト変数は、REST API のコンテキスト変数とは異なります。
次の例では、WebSocket API の REQUEST
オーソライザーへの入力を示しています。
{
"type": "REQUEST",
"methodArn": "arn:aws:execute-api:us-east-1:123456789012:abcdef123/default/$connect",
"headers": {
"Connection": "upgrade",
"content-length": "0",
"HeaderAuth1": "headerValue1",
"Host": "abcdef123.execute-api.us-east-1.amazonaws.com",
"Sec-WebSocket-Extensions": "permessage-deflate; client_max_window_bits",
"Sec-WebSocket-Key": "...",
"Sec-WebSocket-Version": "13",
"Upgrade": "websocket",
"X-Amzn-Trace-Id": "...",
"X-Forwarded-For": "...",
"X-Forwarded-Port": "443",
"X-Forwarded-Proto": "https"
},
"multiValueHeaders": {
"Connection": [
"upgrade"
],
"content-length": [
"0"
],
"HeaderAuth1": [
"headerValue1"
],
"Host": [
"abcdef123.execute-api.us-east-1.amazonaws.com"
],
"Sec-WebSocket-Extensions": [
"permessage-deflate; client_max_window_bits"
],
"Sec-WebSocket-Key": [
"..."
],
"Sec-WebSocket-Version": [
"13"
],
"Upgrade": [
"websocket"
],
"X-Amzn-Trace-Id": [
"..."
],
"X-Forwarded-For": [
"..."
],
"X-Forwarded-Port": [
"443"
],
"X-Forwarded-Proto": [
"https"
]
},
"queryStringParameters": {
"QueryString1": "queryValue1"
},
"multiValueQueryStringParameters": {
"QueryString1": [
"queryValue1"
]
},
"stageVariables": {},
"requestContext": {
"routeKey": "$connect",
"eventType": "CONNECT",
"extendedRequestId": "...",
"requestTime": "19/Jan/2023:21:13:26 +0000",
"messageDirection": "IN",
"stage": "default",
"connectedAt": 1674162806344,
"requestTimeEpoch": 1674162806345,
"identity": {
"sourceIp": "..."
},
"requestId": "...",
"domainName": "abcdef123.execute-api.us-east-1.amazonaws.com",
"connectionId": "...",
"apiId": "abcdef123"
}
}
次の例では、Lambda オーソライザー関数は、その他の Lambda オーソライザー関数の例 の REST API の Lambda オーソライザー関数の WebSocket バージョンです。
- Node.js
// A simple REQUEST 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 and QueryString1 query parameter
// in the request context match the specified values of
// of 'headerValue1' and 'queryValue1' 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 stageVariables = event.stageVariables;
var requestContext = event.requestContext;
// 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 ApiId = apiGatewayArnTmp[0];
var stage = apiGatewayArnTmp[1];
var route = apiGatewayArnTmp[2];
// Perform authorization to return the Allow policy for correct parameters and
// the 'Unauthorized' error, otherwise.
var authResponse = {};
var condition = {};
condition.IpAddress = {};
if (headers.HeaderAuth1 === "headerValue1"
&& queryStringParameters.QueryString1 === "queryValue1") {
callback(null, generateAllow('me', event.methodArn));
} else {
callback("Unauthorized");
}
}
// Helper 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 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 and QueryString1 query parameter
# in the request context match the specified values of
# of 'headerValue1' and 'queryValue1' respectively.
import json
def lambda_handler(event, context):
print(event)
# Retrieve request parameters from the Lambda function input:
headers = event['headers']
queryStringParameters = event['queryStringParameters']
stageVariables = event['stageVariables']
requestContext = event['requestContext']
# Parse the input for the parameter values
tmp = event['methodArn'].split(':')
apiGatewayArnTmp = tmp[5].split('/')
awsAccountId = tmp[4]
region = tmp[3]
ApiId = apiGatewayArnTmp[0]
stage = apiGatewayArnTmp[1]
route = apiGatewayArnTmp[2]
# Perform authorization to return the Allow policy for correct parameters
# and the 'Unauthorized' error, otherwise.
authResponse = {}
condition = {}
condition['IpAddress'] = {}
if (headers['HeaderAuth1'] ==
"headerValue1" and queryStringParameters["QueryString1"] == "queryValue1"):
response = generateAllow('me', event['methodArn'])
print('authorized')
return json.loads(response)
else:
print('unauthorized')
return 'unauthorized'
# 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
}
authResponse_JSON = json.dumps(authResponse)
return authResponse_JSON
def generateAllow(principalId, resource):
return generatePolicy(principalId, 'Allow', resource)
def generateDeny(principalId, resource):
return generatePolicy(principalId, 'Deny', resource)
前述の Lambda 関数を WebSocket API の REQUEST
オーソライザー関数として設定する場合、REST API と同じ手順に従います。
コンソールでこの Lambda オーソライザーを使用するよう $connect
ルートを設定するには、$connect
ルートを選択または作成します。[ルートリクエスト設定] セクションで、[編集] を選択します。[承認] ドロップダウンメニューで承認者を選択し、[変更を保存] を選択します。
オーソライザーをテストするには、新しい接続を作成する必要があります。$connect
でオーソライザーを変更しても、接続済みのクライアントに影響はありません。WebSocket API に接続するときは、設定済みの ID ソースの値を指定する必要があります。たとえば、次の例のように wscat
を使用し、有効なクエリ文字列およびヘッダーを送信して接続できます。
wscat -c 'wss://myapi.execute-api.us-east-1.amazonaws.com/beta?QueryString1=queryValue1' -H HeaderAuth1:headerValue1
有効な ID 値なしに接続しようとすると、401
レスポンスが送信されます。
wscat -c wss://myapi.execute-api.us-east-1.amazonaws.com/beta
error: Unexpected server response: 401