Using Custom Authorization - Request based for AWS Lambda
Tags: awslambda, apisecurity, customauthorization, aws
Published: 2024-03-23 08:44:44

Many a time we face a challenge for our Lambda function endpoints where a customer autorization is required. 
Below is a post which shows how to implement a Request based Custom Authorization for AWS Lambda.

A **Lambda authorizer**, formerly known as custom authorizer, controls API access using a Lambda function. It's beneficial for custom authorization with bearer token authentication like OAuth or SAML OR request parameter based authentications. It processes the caller's identity and returns an IAM policy. There are two types: **TOKEN authorizer** for bearer tokens and **REQUEST authorizer** for parameters. WebSocket APIs only support REQUEST authorizers. You can use Lambda functions across AWS accounts for authorizers.

In this part, I will show you how to create a request based token.

It will cover 3 high level steps
1. Create a sample Lambda REST API
2. Create a trigger on API Gateway
3. Create the customer Authorizer and attach it to API and test it.

**Step 1 **- Go to AWS Console, select Lambda and click on '_Create Function_'

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1oy2tl3inlaxgs1b1nv7.JPG)

**Step 2** - Author from scratch and API, Give it any name and create a new role for Lambda execution

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/231tzss6svxv9i1gub9o.JPG)

**Step 3** - Customize the output message with your favourite Hello world message. My sample as below

```
export const handler = async (event) => {
  // TODO implement
  const response = {
    statusCode: 200,
    body: JSON.stringify('You have called My Rest API!'),
  };
  return response;
};
```

**Step 4** - Once the Lambda is created we will create an API Gateway trigger which will basically call this Lambda on the endpoint invocation

a. Click on add trigger
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qj0kc6ycl3bkun0iyz9z.JPG)

b. Select the source as API Gateway
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tew15mo5vytadqcffge7.JPG)

c. Fill in the required Trigger parameters to expose it as a REST API and click on Create. Note the security parameter is kept as **OPEN**. This is specifically to test our scenarios
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2hgbmpz1pnyezuwo80s0.JPG)

**Step 5** - On the API, click on Configuration tab and click on Triggers, Note the API Endpoint and click on it to open it.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2fvucla4r8apsmp8edr3.JPG)

**Step 6** - You will be able to view the result from the endpoint.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kwxzha55zk43ptnh9it9.JPG)

**Step 7** - Now we will create the authorizer function, Go back to Lambda function, and click on '_Create Function_'. Fill in the details as below

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fifyoq1fe1xtyhj5xfkw.JPG)

**Step 8** - Now under the code section, fill in the below code.
This is a sample code from AWS blogs. Importantly look for the Header, QueryParam and StageVariable validation conditions.
This will be the one which will be request-based authorizer.

```
   // 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.
    var authResponse = {};
    var condition = {};
    condition.IpAddress = {};
     
    if (headers.headerauth1 === "H1"
        || queryStringParameters.QueryString1 === "Q1"
        || stageVariables.StageVar1 === "S1") {
        callback(null, generateAllow('me', event.methodArn));
    }  else {
        callback("Unauthorized");
    }
}
     
// 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);
}
```

**Step 9** - Add the code and now click '_Deploy_'

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rcvmwuumu9d7mkptxihr.JPG)

**Step 10** - Now, go to **API Gateway** and click on the API Name. NOTE:- you need to go to API Gateway not AWS Lambda.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p2zbadw44opsjqopfnp8.JPG)

**Step 11** - Now go to _Authorizers _and click on _Create authorizer_

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h488wbprumv0y87bwu67.JPG)

**Step 12** - Next up, we need to create the autorizer with the right Identify source type and the key values.
One which we are doing here are Header, Query String and StageVariable. There are few others also based on which you can perform the authorization

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8ch70w71haqzqv2hezw7.JPG)

**Step 13** - Now go back to the authorizer that is created and click on its details

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3dz3rn6fyy9822eki0yg.JPG)

**Step 14** - Next screen will give you the option to test the authorization, 

a. if you give any values that are acceptable then it will return a 200

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ht4a3w4nvly9g8j6a3og.JPG)

b. if you give any values that are not acceptable then it will return a 401

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/04vw8ixchuydp1ct23uh.JPG)


So in this way you can achieve custom authorization for your code very quickly.

Hope this was useful to you. And in the next blog I will show creation of a token based authorizer.