API Authorizer - Serverless

Gulmohar
Nggawe Nirman Tech Blog
4 min readSep 2, 2020

Today I want to talk about security. This is my first blog post following the Serverless series This will be a quite complex blog post, as there are many moving parts involved, but to make it simple I used the simple JWT plugin to generate and verify the token in getting the example done. But let’s start by the beginning…

What is the ultimate goal ??

I need to do secure calls to my backend (API gateway). I want only authorized requests will be served by my application and others will not able to access my application APIs.

Tokens are created by either own token generator application either using JWT which we are going to do in this example OR by using any third-party token provides like Auth0 or oAuth or by AWS Cognito and they are valid for a certain period of time. Later we can validate the tokens against the token generator when we do a call to an API Gateway.

How do we achieve this ??

To achieve this, we need to integrate many different providers and systems into the picture, and here is where serverless shines.

We will use our own application to take care of the authentication. We can also use Auth0 as a third party for authentication purposes (AWS has a similar service called Cognito). Auth0 is a company that provides the token generation, creates and validates the tokens (and many other features related to authentication).

API Authorizer

Then we will have an API Gateway with 2 lambdas attached. One lambda will be working with the API Gateway as normal and we will have another one that will be the authorizer.

The authorizer Lambda, will call our application(or Auth0) and verify that the token is valid, and if it’s valid will create a valid policy, that can be evaluated and then the normal Lamdba can execute.

Steps to achieve this ??

  1. If we wanted to use auth0 then, the first step will be to register an account in Auth0 and create a new client. Auth0 is free for developers, so you can try it with no problems. You can pick what kind of client you want it is a native app or a web/rest app. When you create the client, you will get an id, a secret, and a domain that you will use in your calls to Auth0.
  2. Create a rest application that generates and verifies the token (or integrate with Auth0). We will use our application to generate the token in the process of login and logout.
  3. Now let’s go to the server(less) world. Let’s create a function that does something. I am lazy so I just created a new project with serverless and used the “Hello world” function that comes as an example. Deploy it and test it.
  4. Let’s create the authorizer….

a. serverless.yml

provider:
name: aws
runtime: nodejs12.x
profile: default
region: eu-west-1
apiKeys:
- myVerySecretAPIKeyFoThisApplication
functions:
test:
handler: handler.test
events:
- http:
path: test
method: get
authorizer: authorize
generateToken:
handler: handler.generateToken
events:
- http:
path: generateToken
method: post
private: true
authorize:
handler: handler.authorize

b. handler.js

'use strict';const authorizer = require('./authorizer');module.exports.test = (event, context, callback) => {
const response = {
statusCode: 200,
body: JSON.stringify({
message: 'You are Authorized user!'
})
};
callback(null, response);
};
module.exports.generateToken = (event, context, callback) => {
const token = authorizer.generateToken(event.body);
console.log(token);
const response = {
statusCode: 200,
body: JSON.stringify({
token
})
};
callback(null, response);
};
module.exports.authorize = (event, context, callback) => {
try {
console.log(event.authorizationToken);
console.log(event.methodArn);
const policy = authorizer.generatePolicy(event.authorizationToken, event.methodArn);
callback(null, policy);
} catch (error) {
console.log(error.message);
callback(error.message);
}
};

c.authorizer.js

'use strict';const jwt = require('jsonwebtoken');const SECRET_KEY = 'myVerySecretKey';module.exports.generatePolicy = (token, methodArn) => {
if (this.decodeToken(token) != null) {
console.log('token successfully decoded');
return generatePolicy('user', 'Allow', methodArn);
} else {
console.log('invalid token!! Not decoded properly');
const error = new Error('Unauthorized');
throw error;
}
};
module.exports.generateToken = jsonToSign => {
var token = jwt.sign(jsonToSign, SECRET_KEY);
console.log(token);
return token;
};
module.exports.decodeToken = token => {
try {
var decoded = jwt.verify(token, SECRET_KEY);
console.log(decoded);
return decoded;
} catch (error) {
console.log(error);
return null;
}
};
var generatePolicy = function(principalId, effect, resource) {
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;
}
return authResponse;
};

Now deploy your serverless application as

sls deploy

To test:

  1. Generate the application by URL “/generate token” with below details
Header:
x-api-key: <generated-apikey-from-sls-deply>
paylaod:
{
"name": "gen-token",
"appname": "testapp",
"appId": "myPersonalAppId"
}

2. This will provide you the JWT token

3. Hit another API as “/test” with an above token with below details

Header:
Authorization: <token-from-above-api>

4. If the token is valid you will get success response.

--

--