Securing Microservices: Implementing Cognito, JWT, and CloudWatch Alarms
Project Context: Justifai
The justifai project is a microservice-based application that initially launched with an open API. As the project evolved, it became crucial to implement robust security measures and comprehensive monitoring to ensure data integrity, user authentication, and system reliability. This post details the implementation of AWS Cognito for authentication, JWT authorization on API Gateway, and CloudWatch alarms for operational oversight.
The Problem
Initially, the justifai API was publicly accessible without any authentication layer. This presented significant security risks, allowing unauthorized access to resources and potential data manipulation. Furthermore, there was a lack of automated monitoring for critical application components, particularly our AWS Lambda functions and Dead-Letter Queues (DLQs). This made detecting and responding to errors reactive rather than proactive.
The Solution: A Multi-Layered Security and Monitoring Strategy
To address these challenges, we adopted a multi-layered approach encompassing authentication, authorization, monitoring, and frontend integration.
API Authentication with AWS Cognito and JWT
We integrated AWS Cognito for user authentication. A Cognito User Pool was set up, coupled with a client configured for Single Page Applications (SPA). All API requests are now authorized using a JWT authorizer on API Gateway. This ensures that only authenticated users with valid tokens can access protected API endpoints.
Robust Monitoring with CloudWatch Alarms
To enhance operational visibility, we configured CloudWatch alarms for key metrics:
- Lambda Errors: Alarms trigger when any of our three Lambda functions report errors.
- DLQ Depth: An alarm monitors the message count in our Dead-Letter Queue. A growing DLQ indicates processing failures.
These alarms are configured to publish notifications to a dedicated SNS topic, with an optional email subscription for critical alerts, enabling prompt responses to issues.
Frontend Integration for a Seamless User Experience
The frontend application was updated to seamlessly integrate with Cognito:
- Login Screen: A dedicated login screen for user authentication via Cognito.
- Session Management: Handles user sessions and token refreshing.
- Token Transmission: The
idTokenreceived from Cognito is sent with every API request in theAuthorization: Bearerheader. - 401 Handling: Robust error handling for
401 Unauthorizedresponses, redirecting users to the login page or prompting re-authentication.
Enhanced Security Policies
- CORS Restriction: The API's Cross-Origin Resource Sharing (CORS) policy was tightened, replacing a broad
*origin with a specific list ofallowed_originsto prevent unauthorized cross-domain requests. - CI Integration: A new job was added to our Continuous Integration pipeline to build the frontend assets, ensuring that security and feature updates are properly deployed.
Here’s a simplified JavaScript example illustrating how a client might attach a JWT token to an API request:
import axios from 'axios';
const getAuthToken = () => {
// In a real app, this would fetch from local storage or context
const idToken = localStorage.getItem('idToken');
return idToken;
};
const securedApiCall = async (endpoint, data) => {
const token = getAuthToken();
if (!token) {
throw new Error('No authentication token found.');
}
try {
const response = await axios.post(`https://api.example.com/${endpoint}`, data, {
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
}
});
return response.data;
} catch (error) {
if (error.response && error.response.status === 401) {
// Handle unauthorized: e.g., redirect to login
console.error('Unauthorized, redirecting to login...');
window.location.href = '/login';
}
throw error;
}
};
// Example usage:
// securedApiCall('secure-resource', { param: 'value' })
// .then(response => console.log('Success:', response))
// .catch(error => console.error('API Error:', error));
This code snippet demonstrates how to retrieve an authentication token and include it in the Authorization header for requests to a secured API endpoint, also showing basic error handling for 401 Unauthorized responses.
Results After Implementation
The implementation of these measures has significantly enhanced the security posture of the justifai project. The API is no longer open, with all access now protected by robust authentication and authorization. The CloudWatch alarms provide critical real-time insights into system health, enabling our team to proactively address potential issues with Lambdas and DLQs. The integrated frontend ensures a secure and smooth user experience from login to data interaction.
Key Takeaways
Securing microservices requires a holistic approach that covers authentication, authorization, and monitoring. Leveraging cloud-native services like AWS Cognito and CloudWatch provides powerful tools to build secure and reliable applications. Always integrate security at every layer, from the API Gateway to the frontend, and ensure you have effective alerting in place to maintain operational excellence.
Generated with Gitvlg.com