Mastering Serverless Framework: A Comprehensive Guide for Developers
Take a deep dive into the Serverless Framework, understanding its core aspects from initialization to deployment and managing complex workflows.
Hello my friends, and welcome to a new story. Today we'll discuss the shortest method to use API key authentication for your JavaScript SDK using AWS Lambda.
So you have a JavaScript SDK (file) that you want to allow only clients with valid key
and valid domain
to actually be able to request and download the SDK file.
Let's say we want https://mydomain.com/sdk.min.js?key=XXXX
to work when requested from client.com
website, and of course when the key is valid, and should send 401
unauthorized if the key is not valid, or the key is valid, but from a different domain.
Here's our checklist from start to finish:
This method installs serverless globally, but you can also install it locally to your project only if you prefer.
npm install -g serverless yarn global add serverless
First thing we need is to run the serverless CLI tool, it'll start an interactive process for creating a new project, it's pretty straightforward.
serverless
This will initialize the interactive CLI of serverless for creating new project
The above command will start the interactive process of creating a new serverless project, I've included the steps below:
$ serverless $ Serverless: No project detected. Do you want to create a new one? (Y/n) $ Serverless: What do you want to make? (Use arrow keys) ❯ AWS Node.js AWS Python $ Serverless: What do you want to call this project? $ Project successfully created in 'lambda-sdk-auth' folder. $ You can monitor, troubleshoot, and test your new service with a free Serverless account. $ Serverless: Would you like to enable this? (Y/n) $ Serverless: Would you like to setup a command line <tab> completion? (Y/n)
The last 2 steps are optional.
Now we have a directory called lambda-sdk-auth
which include our new project.
Files tree
You should now have 3 files in your project:
.gitignore
Which is used to instruct git to exclude some paths like node_modules
& .serverless
.handler.js
We'll write our lambda function here.serverless.yml
This file include all of our serverless configurations.Let's start by updating the default serverless config, replace serverless.yml
's contents with the following:
service: lambda-sdk-auth provider: name: aws runtime: nodejs10.x stage: dev region: us-east-1 functions: sdk: handler: handler.default events: - http: path: sdk.min.js method: get
Let me explain what we've got here:
.default
part of the value means it will look for module.exports.default
in that file.GET
.Before writing our Lambda code, there's 1 last step we need to do, is to install serverless-offline
node module, which will make local development easier by allowing us to invoke our Lambda from the browser, to best simulate AWS environment.
Install serverless-offline:
$ npm install serverless-offline -D
Install serverless offline as a dev dependency via npm
Or via Yarn:
yarn add serverless-offline -D
Install serverless offline as a dev dependency via yarn
Finally update serverless file to include plugins section, with this serverless plugin:
service: lambda-sdk-auth plugins: - serverless-offline provider: name: aws runtime: nodejs10.x stage: dev region: us-east-1 functions: sdk: handler: handler.default events: - http: path: sdk.min.js method: get
Now let's do 2 primary things:
Create a file called sdk.js
in the root of your project, and add anything to it, example:
const test = () => 123;
Our SDK's contents
Now let's move on to writing our function's logic; Let's start by replacing our Lambda file with the below contents, then I'll explain it in details:
const fs = require('fs'); module.exports.default = async (event, context) => { // Get the "key" from request query params const queryParams = event.queryStringParameters; const key = queryParams && queryParams.key; // Return 401 if key is not passed in GET query params if (!key) { return { statusCode: 401, body: JSON.stringify( { error: 'Key is missing' }, null, 2), }; }; // Get requesting domain const clientDomain = event.headers.Origin; // This is a static mock for allowed clients. // You should normally get this from your database const allowedClients = [ { domains: ['devspedia.com'], key: 'example-key' } ]; // Check if client exists using it's unique key const client = allowedClients.find(client => client.key === key); // If client is not found, return 401 as the key is not authrorized if (!client) { return { statusCode: 401, body: JSON.stringify( { error: 'Key not registered' }, null, 2), }; } // Check if the domain exists within the client's allowed // domains const validDomain = clientDomain && client.domains .find(domain => domain === clientDomain.split('://')[1]); // If the request if coming from a domain that was not // allowed for this key, return 401 if (!validDomain) { return { statusCode: 401, body: JSON.stringify( { error: 'Domain not registered for this key' }, null, 2), }; } // Read the SDK javascript file's contents const sdk = fs.readFileSync(`${__dirname}/sdk.js`); // Return the file's contents with correct headers // and transform the sdk buffer array to a string return { statusCode: 200, headers: { 'Content-type': 'text/javascript' }, body: sdk.toString(), }; };
hander.js
Here's what we did in brief points:
fs
module.Now, to try this locally, we'll use cURL from the terminal. Make sure you're inside the project's directory, and then run:
serverless invoke local -f sdk -d "{"headers":{"Origin":"https://devspedia.com"},"queryStringParameters":{"key":"example-key"}}"
The response should be:
{ "statusCode": 200, "headers": { "Content-type": "text/javascript" }, "body": "const test = () => 123;" }
Lambda response
Now, when this is requested from the browser, the browser will only render the body part of this response, and in this case, it's our SDK's content.
To actually simulate this in the browser, let's run serverless offline. Make sure you're inside the project directory, and then run:
serverless offline
Then in the browser, you can open devspedia.com and open up browser's developer tools/console, then make a request via fetch:
fetch( 'http://localhost:3000/sdk.min.js?key=example-key' ).then( res => res.text() ).then( res => console.log(res) );
fetch request the SDK
You should see const test = () => 123;
in the logs, which is the file's contents.
Deployment is easy, few steps you'll need to make:
aws configure
in your terminal, and add your authentication info.serverless deploy
.That's it, thanks for reading devspedia, I love you, and I'll see you the next time :)