DEV Community

Cover image for Serverless User Authentication with AWS Cognito & DynamoDB.
Om Choudhary
Om Choudhary

Posted on

Serverless User Authentication with AWS Cognito & DynamoDB.

Image descriptionπŸš€ Introduction

User authentication is a critical part of web and mobile applications. Instead of managing authentication manually, AWS provides Cognito, which allows seamless user sign-up, login, and token-based authentication.

In this tutorial, we’ll build a fully serverless authentication system using:
βœ… AWS Lambda - Serverless compute for API functions
βœ… Amazon API Gateway - Expose API endpoints
βœ… AWS Cognito - User authentication and authorization
βœ… DynamoDB - Store user data
βœ… Serverless Framework - Automate deployment

By the end of this tutorial, you’ll have a working authentication system with JWT authentication, CRUD operations, and secure API access. πŸ”

🌍 Project Overview

πŸ”Ή Features

βœ” User Sign-up & Login using AWS Cognito
βœ” JWT Authentication for API security
βœ” User Data Management (CRUD) with DynamoDB
βœ” Serverless API Deployment with AWS Lambda & API Gateway
βœ” Secure IAM Roles for fine-grained access control

πŸ— Architecture Diagram
User ➝ API Gateway ➝ AWS Lambda ➝ Cognito & DynamoDB ➝ Response

βš™οΈ Step 1: Setting Up the Serverless Project

First, install the Serverless Framework:

npm install -g serverless
Enter fullscreen mode Exit fullscreen mode

Then, create a new project:

serverless create --template aws-nodejs --path serverless-auth
cd serverless-auth
npm init -y
Enter fullscreen mode Exit fullscreen mode

Install dependencies:

npm install express serverless-http @aws-sdk/client-dynamodb @aws-sdk/lib-dynamodb @aws-sdk/client-cognito-identity-provider jsonwebtoken dotenv
Enter fullscreen mode Exit fullscreen mode

πŸ›  Step 2: Define API in serverless.yml
Create serverless.yml with the following configuration:

service: serverless-auth

plugins:
  - serverless-dotenv-plugin

provider:
  name: aws
  runtime: nodejs18.x
  region: us-east-1
  environment:
    USERS_TABLE: UsersTable
    COGNITO_USER_POOL_ID: your-user-pool-id
    COGNITO_CLIENT_ID: your-client-id
    COGNITO_CLIENT_SECRET: your-client-secret
  iamRoleStatements:
    - Effect: Allow
      Action:
        - dynamodb:PutItem
        - dynamodb:GetItem
        - dynamodb:Scan
        - dynamodb:DeleteItem
      Resource: "arn:aws:dynamodb:us-east-1:*:table/UsersTable"

functions:
  registerUser:
    handler: handler.handler
    events:
      - http:
          path: register
          method: post
          cors: true
      - http:
          path: users/{userId}
          method: get
          cors: true
      - http:
          path: users
          method: get
          cors: true
      - http:
          path: users/{userId}
          method: put
          cors: true
      - http:
          path: users/{userId}
          method: delete
          cors: true

  loginUser:
    handler: handler.handler
    events:
      - http:
          path: login
          method: post
          cors: true
Enter fullscreen mode Exit fullscreen mode

πŸ”‘ Step 3: Implement the Authentication API
Create an app.js file

const { DynamoDBClient } = require("@aws-sdk/client-dynamodb");
const { CognitoIdentityProviderClient, SignUpCommand, InitiateAuthCommand } = require("@aws-sdk/client-cognito-identity-provider");
const { DynamoDBDocumentClient, GetCommand, PutCommand, DeleteCommand, ScanCommand } = require("@aws-sdk/lib-dynamodb");
const express = require("express");
const jwt = require("jsonwebtoken");
const crypto = require("crypto");

const app = express();
const cognito = new CognitoIdentityProviderClient({ region: "us-east-1" });
const USERS_TABLE = process.env.USERS_TABLE;
const client = new DynamoDBClient({ region: process.env.AWS_REGION });
const docClient = DynamoDBDocumentClient.from(client);

app.use(express.json());

// JWT Authentication Middleware
const authenticate = (req, res, next) => {
  const token = req.headers.authorization?.split(" ")[1];
  if (!token) return res.status(401).json({ error: "Unauthorized - No token provided" });

  try {
    const decoded = jwt.verify(token, process.env.COGNITO_PUBLIC_KEY);
    req.user = decoded;
    next();
  } catch (err) {
    return res.status(401).json({ error: "Invalid token", details: err.message });
  }
};

// Login Route
app.post("/login", async (req, res) => {
  const { email, password } = req.body;
  const params = {
    AuthFlow: "USER_PASSWORD_AUTH",
    ClientId: process.env.COGNITO_CLIENT_ID,
    AuthParameters: {
      USERNAME: email,
      PASSWORD: password,
    },
  };

  try {
    const response = await cognito.send(new InitiateAuthCommand(params));
    res.json({ token: response.AuthenticationResult.IdToken });
  } catch (error) {
    res.status(400).json({ error: "Could not authenticate user", details: error.message });
  }
});

module.exports = app;
Enter fullscreen mode Exit fullscreen mode

πŸš€ Step 4: Deploy & Test the API
Run the deployment command:

serverless deploy
Enter fullscreen mode Exit fullscreen mode

Use Postman or cURL to test the API:

curl -X POST https://your-api-url/dev/login -H "Content-Type: application/json" -d '{ "email": "test@example.com", "password": "YourPass123" }'
Enter fullscreen mode Exit fullscreen mode

🎯 Conclusion

In this guide, we built and deployed a serverless authentication system using AWS services. This architecture ensures scalability, cost efficiency, and security. πŸ’‘

πŸ”Ή Want to enhance this further? Add OAuth2 (Google/Facebook Login) or Multi-Factor Authentication (MFA) for stronger security! πŸ”’

Let me know if you found this useful! Drop your thoughts in the comments below. πŸš€

Top comments (0)