Node
// index.js file
const registerService = require(“./services/register”);
const loginService = require(“./services/login”);
const verifyService = require(“./services/verify”);
const logoutService = require(“./services/logout”);
const builder = require(“./utils/builder”);
exports.handler = async (event) => {
if (event.path === “/register” && event.httpMethod === “POST”) { // handling user’s sign up process
const response = await registerService.register(
event.name,
event.username,
event.password
);
return builder.buildResponse(200, response);
} else if (event.path === “/login” && event.httpMethod === “POST”) { // handling user’s sign in process
const response = await loginService.login(event.username, event.password);
return builder.buildResponse(200, response);
} else if (event.path === “/verify” && event.httpMethod === “POST”) { // handling user’s auth-verification process
const response = await verifyService.verify(event.username, event.token);
return builder.buildResponse(200, response);
} else if (event.path === “/logout” && event.httpMethod === “POST”) { // handling user’s signout process
const response = await logoutService.logout(event.username, event.token);
return builder.buildResponse(200, response);
} else {
return builder.buildResponse(400, {
message: `${event.httpMethod} is not allowed in ${event.path} route`,
});
}
};
Node
// /services/register.js file
const builder = require(“../utils/builder”);
const bcrypt = require(“bcryptjs”);
const AWS = require(“aws-sdk”);
const dynamoDB = new AWS.DynamoDB.DocumentClient({
region: ‘us-east-1’,
apiVersion: ‘2012-08-10’,
});
const SALT_ROUND = 8;
// register user using name, username, password
async function register(name, username, password){
if(!name || !username || !password){
return builder.buildResponse(400, {message: “Missing required fields”});
}
const foundUser = await getUser(username);
if(foundUser && foundUser.username){
return builder.buildResponse(400, {message: “User already exists”});
}
const hashedPass = bcrypt.hashSync(password.trim(), SALT_ROUND);
const newUser = {
name,
username,
password: hashedPass
};
const saveUserResponse = await saveUser(newUser);
if(!saveUserResponse) return builder.buildResponse(400, {message: “Server Error: Please try again later”});
return builder.buildResponse(200, {message: “User registered successfully”});
}
// retrieve user data via username from DynamoDB
const getUser = async (username)=>{
const params = {
Key: {
username: username
},
TableName: “login-database”
}
return await dynamoDB.get(params).promise().then((response)=>{
return response.Item;
}).catch((err)=>{
return err;
})
}
// save user in DynamoDB
const saveUser = async (user)=>{
const params = {
Item: user,
TableName: “login-database”
}
return await dynamoDB.put(params).promise().then(()=>{
return true;
}).catch((err)=>{
return err;
})
}
module.exports.register = register;
Node
// /services/login.js file
const builder = require(“../utils/builder”);
const bcrypt = require(“bcryptjs”);
const auth = require(“../utils/auth”);
const AWS = require(“aws-sdk”);
const dynamoDB = new AWS.DynamoDB.DocumentClient({
region: ‘us-east-1’,
apiVersion: ‘2012-08-10’,
});
const SALT_ROUND = 8;
async function login(username, password){
if(!username || !password){
return builder.buildResponse(400, {message: “Missing required fields”});
}
const foundUser = await getUser(username);
if(!foundUser || !foundUser.username){ // user doesn’t exist in database
return builder.buildResponse(400, {message: “User doesn’t exist”});
}
if(!bcrypt.compareSync(password, foundUser.password)){ // password doesn’t match with the existing password
return builder.buildResponse(403, {message: “Wrong Password”});
}
const token = auth.generateToken(foundUser.username); // generate token encapsulating username
const tokenArray = foundUser.tokens || [];
tokenArray.push(token); // store the generated token in the database
const params = {
Key: {
username: username
},
UpdateExpression: `set tokens = :value`,
ExpressionAttributeValues: {
“:value”: tokenArray
},
TableName: “login-database”,
ReturnValues: “UPDATED_NEW”
};
const response = {
username: foundUser.username,
name: foundUser.name,
token: token
};
return await dynamoDB.update(params).promise().then(()=>{
return builder.buildResponse(200, {message: “User logged in successfully”, response});
}).catch((err)=>{
return builder.buildResponse(400, {message: err});
})
}
// retrieve user data via username from DynamoDB
const getUser = async (username)=>{
const params = {
Key: {
username: username
},
TableName: “login-database”
}
return await dynamoDB.get(params).promise().then((response)=>{
return response.Item;
}).catch((err)=>{
return err;
})
}
module.exports.login = login;
Node
// /services/verify.js file
const auth = require(“../utils/auth”);
const builder = require(“../utils/builder”);
const AWS = require(“aws-sdk”);
const dynamoDB = new AWS.DynamoDB.DocumentClient({
region: ‘us-east-1’,
apiVersion: ‘2012-08-10’,
});
async function verify(username, token){
if(!username || !token) return builder.buildResponse(400, {
verified: false,
message: “Missing required fields”
});
// verify the validation of the current token
const verificationResponse = auth.verifyToken(username, token);
if(!verificationResponse.verified) return builder.buildResponse(400, verificationResponse);
const foundUser = await getUser(username);
if(!foundUser || !foundUser.username || !foundUser.tokens) return builder.buildResponse(400, {verified: false, message: “Missing Fields detected”});
if(!foundUser.tokens.includes(token)) return builder.buildResponse(400, {verified: false, message: “User is not authenticated”});
return builder.buildResponse(200, {
verified: true,
message: “success”,
username,
token
});
}
// retrieve user data via username from DynamoDB
const getUser = async (username)=>{
const params = {
Key: {
username: username
},
TableName: “login-database”
}
return await dynamoDB.get(params).promise().then((response)=>{
return response.Item;
}).catch((err)=>{
return err;
})
}
module.exports.verify = verify;
Node
// /services/logout.js file
const builder = require(“../utils/builder”);
const bcrypt = require(“bcryptjs”);
const auth = require(“../utils/auth”);
const AWS = require(“aws-sdk”);
const dynamoDB = new AWS.DynamoDB.DocumentClient({
region: ‘us-east-1’,
apiVersion: ‘2012-08-10’,
});
async function logout(username, token){
if(!username || !token){
return builder.buildResponse(400, {message: “Missing required fields”});
}
const foundUser = await getUser(username);
if(!foundUser || !foundUser.username){ // user doesn’t exist in database
return builder.buildResponse(400, {message: “User doesn’t exist”});
}
// user is already logged out
if(!foundUser.tokens || !foundUser.tokens.includes(token)){
return builder.buildResponse(400, {message: “User is not authenticated”});
}
// remove the current token from the database
const tokenArray = foundUser.tokens.filter(currToken => currToken !== token);
const params = {
Key: {
username: username
},
UpdateExpression: `set tokens = :value`,
ExpressionAttributeValues: {
“:value”: tokenArray
},
TableName: “login-database”,
ReturnValues: “UPDATED_NEW”
};
return await dynamoDB.update(params).promise().then(()=>{
return builder.buildResponse(200, {message: “Logged out successfully”});
}).catch((err)=>{
return builder.buildResponse(400, {message: err});
})
}
// retrieve user data via username from DynamoDB
const getUser = async (username)=>{
const params = {
Key: {
username: username
},
TableName: “login-database”
}
return await dynamoDB.get(params).promise().then((response)=>{
return response.Item;
}).catch((err)=>{
return err;
})
}
module.exports.logout = logout;
Node
// /utils/auth.js file
const jwt = require(“jsonwebtoken”);
// generate a token encapsulating the username (expires after 1 hour)
const generateToken = (username)=>{
if(!username) return null;
return jwt.sign({username}, process.env.JWT_SECRET, {
expiresIn: ‘1h’
})
}
// verify the validation of the current token
const verifyToken = (username, token)=>{
return jwt.verify(token, process.env.JWT_SECRET, (error, response)=>{
if(error) return {verified: false, message: “Invalid Token”};
if(response.username !== username) return {verified: false, message: “Invalid User”};
return {verified: true, message: “User is verified”};
})
}
module.exports.generateToken = generateToken;
module.exports.verifyToken = verifyToken;
Node
// /utils/builder.js file
// generate response
const buildResponse = (statusCode, data)=>{
return {
statusCode: statusCode,
headers: { “Content-Type”: “application/json” },
body: data,
};
}
module.exports.buildResponse = buildResponse;