Drone and IoT Security: The Next Cyber Battlefield
GPT-4 Integration in Legacy Systems: Overcoming Enterprise Technical Challenges
Secure GitHub Automation with MCP Server: Step-by-Step Implementation Guide
CQRS Pattern for Microservices: Handling High Workloads.
Introduction
While developing APIs, I always strive to ensure that responses are returned within 200ms. But guess what? Sticking to that principle is one of the toughest tasks I have faced. And then, the hell broke loose when I was working on an API for an eCommerce platform to fetch order summaries.
The API had to fetch data from six tables spread across five microservices, each handling millions of records. Even with extensive optimizations, keeping the response time under 200ms was impossible due to the architecture.
And that’s when I discovered the CQRS (Command Query Responsibility Segregation) pattern a game-changer that helped me achieve my performance goals. I fell in love with it, and today, I am sharing my experience so you can also harness its power.
Let’s start with the definition before diving into the magic of CQRS pattern.
What is CQRS (Command Query Responsibility Segregation)?
CQRS (Command Query Responsibility Segregation) is a software architecture pattern that separates read operations (queries) from write operations (commands). Instead of having a single model to handle both, CQRS introduces separate models:
- Command Model: Handles writes (Create, Update, Delete operations) and enforces business rules.
- Query Model: Handles reads (Fetch operations) and is optimized for performance.
- Event Bus: Ensures that updates in the command model are eventually reflected in the query model asynchronously.
By decoupling reads and writes, CQRS allows APIs to be significantly faster because queries no longer depend on complex joins or transactional integrity constraints.
CQRS Query Models & Event Bus
Here’s how the CQRS pattern works in a distributed system:
- Write operations update the Command Model (SQL Database mircoservices).
- An Event Bus publishes events whenever data changes (e.g., Order Placed, Payment Processed, etc.).
- The Query Model (No SQL Database microservice) listens to these events and updates a denormalized, optimized version of the data.
- Read operations now use this optimized Query Model, allowing for fast retrieval without expensive joins



Now, let’s see CQRS in action with an eCommerce order summary API.
CQRS Example in an eCommerce Platform
The Problem
In a traditional approach, fetching an order summary required joining data from six different tables across multiple microservices:
- Users (Customer Details)
- Orders (Order Metadata)
- OrderItems (Products in the Order)
- Payments (Payment Status)
- Shipping (Delivery Information)
- Products (Product Details)
Since each microservice had its own database, the API had to make multiple requests and perform heavy joins, leading to slow performance.
How CQRS Helped
With CQRS, we built a denormalized Order Summary Table in the Read Model. This table is updated asynchronously whenever an order is created or updated. Now, instead of fetching data from multiple microservices, the API can fetch everything from one optimized table.
Write Model (Command Side)
Table for Writes (Normalized Data Structure)



Read Model (Query Side)
Denormalized Order Summary Table



API Response After CQRS Pattern
{
"orderId": "1234",
"user": {
"name": "John Doe",
"email": "john@example.com"
},
"totalAmount": 899.99,
"status": "Shipped",
"products": [
{ "productId": "p1a2b3c4", "name": "Smartphone", "quantity": 1, "price": 699.99 },
{ "productId": "p9x8y7z6", "name": "Wireless Earbuds", "quantity": 1, "price": 199.99 }
],
"paymentStatus": "Completed",
"shippingStatus": "In Transit",
"trackingNumber": "TRK123456789"
} How CQRS Pattern Makes the API Faster
- No more heavy joins → API fetches everything from a precomputed read table.
- No dependency on multiple microservices → One optimized query instead of multiple API calls.
- Asynchronous updates → Event Bus ensures data stays eventually consistent without blocking API performance.
Key Benefits of CQRS Pattern
Performance Boost
- No complex joins → Faster response times (~<200ms response achieved).
- Read models are optimized for fast retrieval.
Scalability
- Reads and writes scale independently, allowing microservices to handle high traffic efficiently.
- Command-side databases remain normalized, ensuring data integrity.
Flexibility
- Read models can be designed for specific use cases (e.g., Order Summary API).
- Different databases (SQL, NoSQL, etc.) can be used for command and query models.
Challenges & Considerations
Eventual Consistency
- Since reads are updated asynchronously, there may be a lag before changes reflect.
- If real-time consistency is needed, CQRS alone may not be enough.
Increased Complexity
- Maintaining two models (Command & Query) adds extra code & infrastructure overhead.
- Requires event-driven architecture (Kafka, RabbitMQ, or AWS SNS/SQS).
Data Synchronization
- Event-driven updates must be reliable to prevent stale read models.
- Monitoring & logging are crucial for tracking CQRS events.
Conclusion
CQRS completely transformed my eCommerce API performance, making it blazing fast . If you’re struggling with slow APIs due to heavy joins & microservice calls, give CQRS a try—it might be your performance savior too!
GitHub Actions: How to Secure Secrets and Credentials in CI/CD
Introduction
CI/CD pipelines streamline software development and accelerate releases. However, they also introduce security vulnerabilities. Secrets and credentials are essential for secure automation and must be safeguarded. GitHub Actions provides a structured approach to managing these risks. This blog explores effective strategies for securing secrets in GitHub Actions.
The Risk of Exposed Secrets
Secrets are sensitive data. Think API keys, database passwords, and SSH keys. CI/CD pipelines often need them. Storing them directly in code is a huge mistake/risk. Anyone with access to the repository can see them. This is a major security vulnerability.
The Impact of Leaked Secrets
- Leading Cause of Data Breaches: Exposed secrets are one of the leading causes of security incidents. Attackers can exploit leaked credentials to gain unauthorized access to systems, manipulate data, or deploy malicious code.
- Stolen Credentials are Common: Over 80% of security breaches involve compromised credentials. Hardcoded secrets, improperly stored access tokens, and mismanaged access control increase this risk. Cybercriminals actively scan public repositories for exposed secrets, and once compromised, these credentials grant attackers unrestricted access to systems, enabling data exfiltration, service disruption, and financial fraud. Additionally, leaked credentials facilitate lateral movement within an organization’s infrastructure, allowing attackers to escalate privileges and persist undetected. Organizations that fail to secure their credentials face not only security breaches but also financial losses, legal consequences, and reputational damage.
- Compliance Violations: Many industries are governed by strict regulations that require robust secret management practices. Failure to protect sensitive information can lead to legal penalties and reputational damage.
Why Securing Secrets in GitHub Actions Matters
- Exposed credentials lead to unauthorized access.
- Hardcoded secrets can be leaked in repositories.
- Secrets in logs or artifacts can be extracted by attackers.
- Compliance requirements mandate proper secret management.
GitHub Actions Secrets: The Solution
GitHub Actions provides a secure way to store secrets. These are environment variables. They are available only to your workflows. They are encrypted at rest. They are not stored in your repository.
How to Use GitHub Actions Secrets



Using Secrets in Workflows
Secrets are accessed in workflows using the ${{ secrets.YOUR_SECRET_NAME }} syntax. Here’s an example:
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Deploy
uses: some-action@v1
with:
api_key: ${{ secrets.API_KEY }}
database_password: ${{ secrets.DATABASE_PASSWORD }}
This workflow uses two secrets: API_KEY and DATABASE_PASSWORD. These are passed to the some-action action. The action can then use them.
Best Practices for Securing Secrets



1. Use GitHub Secrets
- Store sensitive values in GitHub’s built-in secrets management.
- Access them in workflows using secrets.NAME.
- Avoid exposing secrets in logs by using environment variables securely.
2. Restrict Repository and Environment Access
- Limit who can read and modify secrets.
- Use branch protection rules to prevent unauthorized changes.
- Leverage environment-specific secrets to restrict access.
3. Use OpenID Connect (OIDC) for Federated Identity
- Avoid long-lived credentials by using short-term access tokens.
- Configure cloud providers to trust GitHub’s identity.
- Reduce the risk of secret exposure through ephemeral authentication.
4. Rotate Secrets Regularly
- Implement automated secret rotation where possible.
- Use scheduled jobs or external tools to refresh tokens.
- Revoke outdated credentials to minimize risks.
5. Scan for Hardcoded Secrets
- Use GitHub Advanced Security or tools like TruffleHog.
- Prevent accidental commits of sensitive data.
- Set up pre-commit hooks to detect secrets before pushing.
6. Mask Secrets in Logs
- GitHub automatically masks secrets, but verify log outputs.
- Use ::add-mask:: to hide additional sensitive data.
- Ensure custom scripts do not print secrets inadvertently.
7. Store Secrets in Secure Vaults
- Use external secret management tools (e.g., HashiCorp Vault, AWS Secrets Manager, Azure Key Vault).
- Fetch secrets dynamically during workflow execution.
- Minimize direct exposure of credentials within workflows.
Example: Deploying to AWS
Let’s say you want to deploy to AWS. You need AWS credentials. Store these as secrets. Use the AWS CLI action.
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: us-east-1
- name: Deploy to S3
run: aws s3 cp ./build s3://your-bucket/
This workflow configures AWS credentials using secrets. It then deploys a build to an S3 bucket.
Advanced Secret Management
For complex projects, consider more advanced tools. HashiCorp Vault and AWS Secrets Manager are good options. These provide centralized secret management. They offer features like secret rotation and auditing.
Implementation Steps
1. Set Up GitHub Secrets
- Navigate to repository settings → Secrets.
- Add a new secret and reference it in workflows.
2. Configure OIDC for Secure Authentication
- Enable OIDC in GitHub Actions settings.
- Configure cloud IAM policies to trust GitHub’s OIDC identity.
3. Use Secret Scanning and Detection Tools
- Enable GitHub Secret Scanning.
- Integrate scanning tools in CI/CD pipelines.
4. Regularly Rotate and Revoke Credentials
- Automate secret rotation using cloud provider tools.
- Remove unused or outdated secrets.
Key Takeaways
- Protecting secrets is vital for CI/CD security.
- GitHub Actions secrets provide a secure way to store and use sensitive data.
- Follow best practices for secret management. This minimizes security risks.
- Consider advanced tools for complex projects.
Conclusion
Secrets management in GitHub Actions is crucial for security. Use GitHub secrets, OIDC, and external vaults. Automate secret rotation and scanning to minimize exposure. Secure CI/CD automation requires strict access control and proactive monitoring.
Go Concurrency with Generics: Speed Up Data Pipelines
Introduction
Efficient data processing is crucial for modern applications, especially when dealing with large datasets. Go’s concurrency model makes it an excellent choice for building high-performance data pipelines. With the introduction of generics in Go 1.18, we can now write type-safe concurrent functions that improve code reusability and maintainability.
In this blog, we will explore how Go concurrency and generics can be combined to process data pipelines efficiently.
Why Use Generics in Concurrency?
Prior to generics, go developers relied on interfaces (interface{}) to handle different data types. However, this led to type assertion overhead and reduced type safety. With generics, we can define functions that work with multiple data types while maintaining type safety, improving performance, and reducing boilerplate code.
Advantages of Using Generics in Concurrency
- Type Safety: Generics ensure that the correct data type is used, reducing runtime errors.
- Code Reusability: The same function can work with different types without requiring duplicate code.
- Performance Optimization: Eliminates type assertions, reducing execution time and memory overhead.
- Maintainability: Code becomes cleaner and easier to read, reducing debugging effort.
Drawbacks of Using Generics in Concurrency
- Learning Curve: Developers familiar with traditional Go may take time to adapt to generics.
- Increased Complexity: Generics can sometimes make the code more complex than necessary.
- Compile Time Overhead: Compilation may take longer due to type inference and checking.
How It Impacts Runtime Performance
Using generics in concurrency improves performance by reducing type assertions and ensuring direct usage of the correct types. However, improper use of concurrency (such as excessive goroutines or unbuffered channels) can lead to performance bottlenecks.
Implementing a Concurrent Data Pipeline with Generics
Let’s build a concurrent data pipeline using Go’s goroutines, channels, and generics.
Step 1: Define a Generic Worker Function
A worker function processes incoming data items concurrently. We use a generic type to handle different data types efficiently.
package main
import (
"fmt"
"sync"
"strings"
)
type ProcessFunc[T any] func(T) T
func worker[T any](data <-chan T, results chan<- T, process ProcessFunc[T], wg *sync.WaitGroup) {
defer wg.Done()
for item := range data {
results <- process(item)
}
}
Here, worker receives data through a channel, processes it using a generic function, and sends results to an output channel.
Step 2: Implement the Concurrent Pipeline
We now create a function to process a slice of data concurrently using multiple workers.
func processPipeline[T any](input []T, process ProcessFunc[T], numWorkers int) []T {
data := make(chan T, len(input))
results := make(chan T, len(input))
var wg sync.WaitGroup
// Start workers
for i := 0; i < numWorkers; i++ {
wg.Add(1)
go worker(data, results, process, &wg)
}
// Send input data to workers
for _, item := range input {
data <- item
}
close(data)
// Wait for all workers to finish
wg.Wait()
close(results)
// Collect results
output := make([]T, 0, len(input))
for result := range results {
output = append(output, result)
}
return output
} This function:
- Distributes work across multiple goroutines.
- Uses channels for efficient communication.
- Ensures proper synchronization with sync.WaitGroup.
Step 3: Using the Generic Pipeline
Now, we can use this pipeline with different data types. Below is an example where we process integers and strings.
func main() {
// Example: Processing Integers
numbers := []int{1, 2, 3, 4, 5}
squaredNumbers := processPipeline(numbers, func(n int) int { return n * n }, 3)
fmt.Println("Squared Numbers:", squaredNumbers)
// Example: Processing Strings
words := []string{"go", "concurrency", "generics"}
upperWords := processPipeline(words, func(s string) string { return strings.ToUpper(s) }, 2)
fmt.Println("Uppercase Words:", upperWords)
} Performance Benefits
- Increased Throughput: Multiple goroutines process data simultaneously, reducing latency.
- Type Safety: Generics eliminate the need for type assertions and improve readability.
- Reusability: The same pipeline can handle various data types without duplication.
Conclusion
With Go generics and concurrency, we can build scalable, type-safe, and efficient data pipelines. By leveraging goroutines, channels, and generics, we can process large datasets with improved performance while keeping the code clean and reusable.
Try this approach in your next Go project and experience the power of modern concurrency!
OAuth2 & OpenID Connect Authentication for Cloud Run
Cloud Run is a serverless computing platform that allows you to deploy containerized applications with automatic scaling. However, securing these applications is crucial, especially when exposing them to the internet. OAuth2 and OpenID Connect (OIDC) provide robust authentication and authorization mechanisms to protect Cloud Run services. This blog post walks through implementing OAuth2 and OIDC authentication for a Cloud Run service.
What is OAuth2 and OpenID Connect?
- OAuth2: It is a standard authorization framework, giving third-party applications limited access to a web service without revealing the user’s credentials.
- OpenID Connect (OIDC): This is an identity layer on top of OAuth2 that offers authentication in addition to authorization.
You can use OIDC to authenticate your users to gain identity tokens proving the identity of users and is thus ideal for securing Cloud Run services.
Advantages of using OAuth2 and OpenID Connect (OIDC):
1. Enhanced Security
- Normalized Authentication: OAuth2 and OIDC standard industry mechanisms for authenticating and authorizing securely.
- Token-Based Security: They use access and ID tokens, reducing the need for storing sensitive user credentials.
- Single Sign-On (SSO): OIDC enables SSO, allowing users to log in once and access multiple services.
2. Seamless Integration with Identity Providers
- Works with Google Identity, Okta, Auth0, Azure AD, and other OAuth2/OIDC providers.
- Supports federated identity, allowing users to authenticate with social logins (Google, Facebook, GitHub, etc.).
3. Simplified Authentication for Cloud Run Services
- Identity-Aware Proxy (IAP): Google Cloud’s IAP can handle OAuth2/OIDC authentication without modifying your app.
- Automatic Token Verification: Cloud Run services can easily verify Google-issued ID tokens without additional libraries.
4. Scalability and Performance
- Stateless Authentication: No need to maintain session storage since OAuth2 tokens handle authentication.
- Efficient API Access: Using OAuth2, Cloud Run can securely call APIs (e.g., Google APIs) without requiring user credentials.
5. Role-Based Access Control (RBAC)
- OIDC allows you to extract user roles and permissions from identity providers, enabling fine-grained access control.
6. Reduced Development Overhead
- Google Cloud offers built-in support for OAuth2/OIDC, reducing the need for manual authentication handling.
- Managed services like Firebase Authentication or Cloud Identity Platform can be easily integrated.
7. Improved User Experience
- Enables secure and password-less authentication with providers like Google.
- Users can authenticate across multiple applications without re-entering credentials.
8. Compatibility with Service-to-Service Authentication
- OAuth2 client credentials flow allows Cloud Run services to securely communicate with other APIs or services.
- Google Cloud Service Accounts support workload identity federation for secure access.
Authenticating with Cloud Run
1. Select an Identity Provider (IdP)
Some well-known identity providers that support OAuth2 and OIDC are as follows:
- Google Identity Platform
- Auth0
- Okta
- Microsoft Azure AD
- Keycloak
For the purpose of this tutorial, we will use Google Identity Platform as the IdP.
2. Enable Identity-Aware Proxy (IAP)
Google’s Identity-Aware Proxy (IAP) helps you limit access to your Cloud Run service using Google authentication.
Steps to Enable IAP:
1. Enable IAP in your Google Cloud project:
gcloud services enable iap.googleapis.com
What Does It Do?
- This command activates the IAP API (iap.googleapis.com) for your Google Cloud project.
2. Deploy your Cloud Run service with authentication enabled:
gcloud run deploy my-service \
--add-cloudsql-instances=my-instance \
--service-account=my-service-account@my-project.iam.gserviceaccount.com
What Does It Do?
- gcloud run deploy my-service
- Deploys a new Cloud Run service named my-service.
- If the service already exists, this command updates it.
- –add-cloudsql-instances=my-instance
- Connects the Cloud Run service to a Cloud SQL instance named my-instance.
- This is required if your application needs to access a database hosted in Cloud SQL.
- –service-account=my-service-account@my-project.iam.gserviceaccount.com
- Assigns a specific service account to the Cloud Run service.
- This service account must have appropriate IAM roles to access Cloud SQL, such as roles/cloudsql.client.
3. Configure IAP by restricting access to authorized users:
gcloud projects add-iam-policy-binding my-project \
--member=user:example@gmail.com \
--role=roles/iap.httpsResourceAccessor
What Does It Do?
- gcloud projects add-iam-policy-binding my-project
- Adds an IAM policy binding (permission) to the Google Cloud project named my-project.
- Replace my-project with your actual Google Cloud project ID.
- –member=user:example@gmail.com
- Specifies the user who will receive the permission.
- –role=roles/iap.httpsResourceAccessor
- Grants the “IAP-secured Web App User” role (roles/iap.httpsResourceAccessor).
- This allows the specified user to access web applications protected by Identity-Aware Proxy (IAP).
- Without this role, the user will be blocked by IAP when trying to access a Cloud Run, App Engine, or Compute Engine backend.
3. Implement OAuth2 in Your Application
If you are not using IAP and want to implement OAuth2 authentication manually, follow these steps:
a. Register Your Application with Google OAuth
- Go to the Google Cloud Console.
- Navigate to APIs & Services > Credentials.
- Create a new OAuth 2.0 Client ID.
- Configure the Authorized Redirect URIs (e.g., https://your-service-url/callback).
- Note down the Client ID and Client Secret.
b. Implement OAuth2 Flow in Your Application
Your application should handle the OAuth2 authorization flow:
1. Redirect Users to the Authorization URL
from flask import Flask, redirect, request
import requests
import os
app = Flask(__name__)
CLIENT_ID = os.getenv("GOOGLE_CLIENT_ID")
REDIRECT_URI = https://your-service-url/callback
AUTH_URL = https://accounts.google.com/o/oauth2/auth
@app.route("/login")
def login():
auth_url = (f"{AUTH_URL}?client_id={CLIENT_ID}&response_type=code"
f"&redirect_uri={REDIRECT_URI}&scope=openid email profile")
return redirect(auth_url)
What Does It Do?
1. Import Required Modules
from flask import Flask, redirect, request
import requests
import os
- Import all the necessary libraries.
2. Initialize Flask App
app = Flask(__name__)
- Creates an instance of a Flask web application.
3. Define OAuth2 Configuration Variables
CLIENT_ID = os.getenv("GOOGLE_CLIENT_ID")
REDIRECT_URI = https://your-service-url/callback
AUTH_URL = "https://accounts.google.com/o/oauth2/auth" - CLIENT_ID: Retrieved from environment variables (GOOGLE_CLIENT_ID). This is the unique identifier for your app registered with Google.
- REDIRECT_URI: The callback URL where Google will redirect after authentication. This must be pre-configured in the Google OAuth settings.
- AUTH_URL: The Google OAuth2 authorization endpoint, used to initiate the authentication process.
4. Define the /login Route
@app.route("/login")
def login():
auth_url = (f"{AUTH_URL}?client_id={CLIENT_ID}&response_type=code"
f"&redirect_uri={REDIRECT_URI}&scope=openid email profile")
return redirect(auth_url) - Defines a /login route that users visit to start the authentication process.
- Builds the authorization URL dynamically:
- client_id={CLIENT_ID} → Specifies the registered application.
- response_type=code → Requests an authorization code (needed for exchanging tokens).
- redirect_uri={REDIRECT_URI} → Specifies where Google should send the user after authentication.
- scope=openid email profile → Requests access to the user’s OpenID, email, and profile information.
- Redirects the user to Google’s authentication page, where they can log in and approve access.
2. Handle the Callback and Exchange Code for Tokens
TOKEN_URL = https://oauth2.googleapis.com/token
CLIENT_SECRET = os.getenv("GOOGLE_CLIENT_SECRET")
@app.route("/callback")
def callback():
code = request.args.get("code")
data = {
"code": code,
"client_id": CLIENT_ID,
"client_secret": CLIENT_SECRET,
"redirect_uri": REDIRECT_URI,
"grant_type": "authorization_code"
}
response = requests.post(TOKEN_URL, data=data)
tokens = response.json()
return tokens
What Does It Do?
1. Define the Token Endpoint and Client Secret
TOKEN_URL = https://oauth2.googleapis.com/token
CLIENT_SECRET = os.getenv("GOOGLE_CLIENT_SECRET")
- TOKEN_URL: This is the endpoint provided by Google to exchange the authorization code for access and ID tokens.
- CLIENT_SECRET: Retrieved from environment variables (GOOGLE_CLIENT_SECRET), it is used to authenticate the application during the token request.
2. Define the /callback Route
@app.route("/callback")
def callback():
code = request.args.get("code") - callback route: This is the URL where Google redirects the user after they authenticate.
- request.args.get(“code”): Extracts the code query parameter from the URL, which was sent by Google after successful login.
3. Prepare Data for Token Exchange
data = {
"code": code,
"client_id": CLIENT_ID,
"client_secret": CLIENT_SECRET,
"redirect_uri": REDIRECT_URI,
"grant_type": "authorization_code"
} - This dictionary contains the required fields for exchanging the authorization code for tokens.
4. Send a Request to Exchange the Code for Tokens
response = requests.post(TOKEN_URL, data=data)
tokens = response.json()
- requests.post(TOKEN_URL, data=data): Sends a POST request to Google’s token endpoint with the required parameters.
- response.json(): Converts the response from Google into a JSON object containing authentication tokens.
5. Return the Tokens
return tokens
- Returns the tokens received from Google, which typically include:
- access_token → Used for API access.
- id_token → Contains user identity information (JWT format).
- expires_in → Token expiration time.
- refresh_token (if offline access is enabled) → Used to get a new access token without re-authentication.
4. Validate the ID Token
Once the access and ID tokens are obtained, you should validate the ID token to ensure its authenticity:
import jwt
from google.auth.transport import requests
from google.oauth2 import id_token
@app.route(“/profile”)
def profile():
token = request.headers.get(“Authorization”).split(“Bearer “)[1]
try:
id_info = id_token.verify_oauth2_token(token, requests.Request(), CLIENT_ID)
return id_info
except Exception as e:
return {“error”: “Invalid token”}, 401
What Does It Do?
1. Import Required Modules
import jwt
from google.auth.transport import requests
from google.oauth2 import id_token
- Import all the necessary libraries.
2. Define the /profile Route
@app.route("/profile")
def profile(): - This route handles authenticated user requests to fetch profile information.
3. Extract the Token from the Request
token = request.headers.get("Authorization").split("Bearer ")[1] - Retrieves the Authorization header from the HTTP request.
The token is expected in the format:
Authorization: Bearer <id_token>
- .split(“Bearer “)[1] extracts the actual token by removing the “Bearer ” prefix.
4. Verify the ID Token
try:
id_info = id_token.verify_oauth2_token(token, requests.Request(), CLIENT_ID)
- id_token.verify_oauth2_token(token, requests.Request(), CLIENT_ID):
- Validates the token’s signature using Google’s public keys.
- Ensures the token was issued for the correct CLIENT_ID and is not expired.
- Parses the token into a dictionary containing user identity data.
5. Return the User’s Information if Verified
return id_info
- If verification is successful, the decoded ID token (JSON format) is returned, typically containing:
{
"sub": "1234567890",
"name": "John Doe",
"email": "johndoe@example.com",
"picture": https://lh3.googleusercontent.com/a-/AOh14...
} 6. Handle Invalid or Expired Tokens
except Exception as e:
return {"error": "Invalid token"}, 401
- If verification fails due to an invalid or expired token, it returns a 401 Unauthorized error.
5. Deploy Your Application to Cloud Run
After implementing authentication, deploy the application to Cloud Run:
gcloud run deploy my-auth-service --source .
Conclusion
Implementing OAuth2 and OpenID Connect authentication in Cloud Run enhances security by ensuring only authenticated users can access your services. You can use Google Identity Platform and IAP for seamless authentication or implement OAuth2 manually using an external IdP. By following these steps, you can effectively secure your Cloud Run applications while providing a smooth authentication experience for users.
Containerize Legacy Application for Cloud Run – Steps & Benefits
Ever feel like you’re stuck with old technologies? Today, businesses are shifting their apps to the cloud. They do this for better performance, more scalability, and cost savings. But what about legacy applications? Many businesses depend on ‘legacy-applications’ – Those are reliable, monolithic systems were built long before cloud technologies were popular. You can modernize these apps and get the benefits of the cloud. One of the best ways to do this is by using containers and Google cloud Run.
In this blog, we will explain why containerizing legacy applications makes sense. We’ll also show you how to do it step by step. Plus, we’ll highlight the benefits of using Cloud Run.
Why use containers?
Think of a container as a lightweight package for your application. It includes everything the app needs to run: the code, settings, and any supporting dependencies/files. This makes it super portable.
Why Containerize a Legacy Application?
Most legacy applications were designed to run on traditional servers, which makes scaling, updating, or deploying them efficiently difficult. Containerization solves these problems by:
- Easy Scaling: Containers allow you to easily create copies of your app, handle increased traffic and scale up or down based on demand. No more server headaches!
- Simple Deployment: Moving the entire application with its dependencies into a single unit to a new environment becomes an easy job. This way, everything’s nicely packaged, it runs the same everywhere, reducing those frustrating “it works on my machine” moments.
- Cost Savings: Cloud Run is a serverless platform. This means you only pay when your app is running. This way you can reduce the cost of always-on servers!
- Enhanced Security: Containers provide isolation, by reducing the risk of other applications and other parts of your system affecting your legacy app.
- New Features: When applications are containerized, they can connect and use the new cloud services like – modern databases, security, networking, and app management tools.
Steps to Containerize Legacy Applications for Cloud Run



Step 1: Analyze Your App
Before you start containerizing your legacy application, you need to understand its architecture. Figure out:
- What programming language is it written in? (Java, Python, .NET, etc.)
- What dependencies does it have? (Database connections, libraries)
- Does it have specific OS configurations?
- Where does it store data? (Persistent storage or local files)
If your application depends heavily on the operating system, a multi-stage build may be necessary to keep the container lightweight.
Step 2: Create a Docker file
A Docker file is a script it tells Docker how your application should be pack into a container. Here’s an example for your Java-based legacy application:
Dockerfile:
FROM openjdk:17-jdk-slim # Use a small, efficient Java image
WORKDIR /app # Set the working directory inside the container
COPY target/my-legacy-app.jar my-legacy-app.jar # Copy your app's code
CMD ["java", "-jar", "my-legacy-app.jar"] # Command to run your app
For Java applications built with Spring Boot, you might need to add some more configurations to your Dockerfile to ensure your application’s ports are accessible from outside the container. This is because Spring Boot apps often run on a specific port (by default, 8080), and you need to make sure that port is expose correctly when the app is running inside a Docker container.
Step 3: Build and Test Your Container Locally
Once your Docker file is ready, build and test your container on your own machine to make sure everything works:
Bash:
docker build -t my-legacy-app. # Build the container image
docker run -p 8080:8080 my-legacy-app # Run the container
Step 4: Upload your container image to Google Container Registry (GCR).
Cloud Run requires your container image to be store in a registry. You can use Google Container Registry (GCR) or Artifact Registry:
gcloud auth configure-docker # Connect to your Google Cloud account
docker tag my-legacy-app gcr.io/YOUR_PROJECT_ID/my-legacy-app #Tag the container image
docker push gcr.io/YOUR_PROJECT_ID/my-legacy-app # Upload it to GCR
Step 5: Deploy on Cloud Run
Finally, deploy your containerized appl to Cloud Run:
gcloud run deploy my-legacy-app \
--image gcr.io/YOUR_PROJECT_ID/my-legacy-app \
--platform managed \
--region us-central1 \
--allow-unauthenticated
Cloud Run takes care of the rest, giving you a live, scalable app accessible over the internet.
Benefits of Running Containerize Legacy Applications on Cloud Run
- No Server Management – Google manages all the servers, so you can focus on your app, not on infrastructure.
- Pay-as-you-go – Only pay for the resources your app actually uses. Cloud Run charges only for the exact CPU and memory your application uses.
- Better Performance – Your legacy app can take advantage of Google’s high-speed network and powerful infrastructure.
- Security & Compliance – Built-in security features like IAM, automatic HTTPS, and DDoS protection.
- Easy Connections – Integrate your app with other Google Cloud services.
Conclusion
Containerize Legacy Applications doesn’t need to completely rewrite them from scratch. It means packaging them in a way that makes them more portable, scalable, and cost-effective. Deploying your containerized application on Cloud Run modernizes your infrastructure. You can still use your existing codebase.
Web Security for Front-End Best Practices & Protection
Introduction to Front-End Security
Web security is not just the back-end’s job. Front-end developers must also protect user data and stop attacks. Even though most checks happen on the server, strong front-end validation gives fast feedback. It is the first line of defense against threats like XSS and CSRF.
Input Validation and Sanitization
User input is a common attack point. Unchecked data can lead to problems like SQL injection, command injection, and XSS.
- Client-Side Validation: Check data types, formats (like emails or phone numbers), and input length. For example, validate an email field before accepting it.
- Input Sanitization: Clean the input to remove harmful code. Tools like DOMPurify can sanitize HTML or scripts. For example:
// Sanitize user input before displaying it
import DOMPurify from 'dompurify';
function renderUserContent(content) {
const safeContent = DOMPurify.sanitize(content);
document.getElementById('user-content').innerHTML = safeContent;
}
Remember, front-end checks help the user but must be backed by server-side validation. Attackers can bypass client-side scripts.
Preventing Cross-Site Scripting (XSS)
XSS happens when an attacker puts harmful scripts into web pages that others view. There are three types:
- Reflected XSS: The malicious code is sent in a request and quickly returned.
- Stored XSS: The bad code is saved on the server and later shown to users.
- DOM-based XSS: The attack happens on the client side when JavaScript uses unsafe data.
To prevent XSS:
- Escape/Encode Output:
Use safe methods when inserting data into HTML. For example:
// Unsafe: may allow XSS
element.innerHTML = userInput;
// Safe: escapes harmful code
element.textContent = userInput;
Frameworks like React and Vue escape content automatically. For example, in React:
// React escapes HTML by default
const UserComponent = () => <div>{userInput}</div>;
- Content Security Policy (CSP):
Use a CSP header to limit where scripts and other resources can load. For example:
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' https://trusted-cdn.com; style-src 'self' https://trusted-cdn.com; img-src 'self' https://trusted-cdn.com data:;">
Cross-Site Request Forgery (CSRF) Protection
CSRF tricks logged-in users into taking actions they did not intend. To prevent this:
- Anti-CSRF Tokens:
Every form or request should have a unique token. The server checks this token to ensure the request is genuine.
When using AJAX, send the token in the header:
javascript
CopyEdit
const csrfToken = document.querySelector('meta[name="csrf-token"]').getAttribute('content');
fetch('/api/data', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-Token': csrfToken
},
body: JSON.stringify(data)
});
- SameSite Cookie Attribute:
Set cookies with the SameSite attribute to control when they are sent. Use “Strict” or “Lax” to stop unwanted cookie sharing.
Authentication and Authorization
Securing user accounts and managing access are key.
- Authentication:
Always use HTTPS for secure data transfer. Consider JSON Web Tokens (JWT) or OAuth2 for managing logins. Multi-factor authentication adds extra security. Also, enforce strong password rules and give real-time feedback on password strength. For example:
// Simple password strength checker
function checkPasswordStrength(password) {
const criteria = {
length: password.length >= 12,
uppercase: /[A-Z]/.test(password),
lowercase: /[a-z]/.test(password),
numbers: /[0-9]/.test(password),
special: /[^A-Za-z0-9]/.test(password)
};
const strength = Object.values(criteria).filter(Boolean).length;
return {
score: strength,
feedback: {
warning: strength < 3 ? 'Password is too weak' : '',
suggestions: strength < 3 ? [
'Use at least 12 characters',
'Mix uppercase and lowercase letters',
'Include numbers and special characters'
] : []
}
};
}
- Authorization:
Use role-based access control (RBAC). Show users only the parts of the app they are allowed to see. This can be done by conditionally rendering UI elements based on the user’s role.
Security Headers and HTTPS
Security headers add extra protection. They tell browsers how to handle content and block attacks.



- Strict-Transport-Security (HSTS):
Forces browsers to use HTTPS. - X-Content-Type-Options:
Stops browsers from guessing content types. - X-Frame-Options:
Stops other sites from embedding your website. - X-XSS-Protection:
Enables the browser’s built-in XSS filter. - Referrer-Policy and Permissions-Policy:
Control what referrer data is sent and which browser features can be used.
These headers can also be added via meta tags:
html
CopyEdit
<meta http-equiv="X-Frame-Options" content="DENY">
<meta http-equiv="X-XSS-Protection" content="1; mode=block">
<meta http-equiv="X-Content-Type-Options" content="nosniff">
<meta http-equiv="Referrer-Policy" content="strict-origin-when-cross-origin">
Third-Party Dependencies and Secure Storage
Many apps use third-party libraries. Keep them secure by:
- Auditing dependencies with tools like npm audit or Snyk.
- Pinning dependency versions and using lockfiles (like package-lock.json).
- Using subresource integrity (SRI) for scripts from CDNs:
html
CopyEdit
<script src="https://cdn.example.com/library.js"
integrity="sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8wC"
crossorigin="anonymous"></script>
For sensitive data, store API keys in environment variables. Prefer server-side storage over client-side. Use secure methods like session storage or HTTP-only cookies, and encrypt data if needed. For example:
async function encryptData(data, key) {
const encoded = new TextEncoder().encode(data);
const iv = window.crypto.getRandomValues(new Uint8Array(12));
const encryptedData = await window.crypto.subtle.encrypt(
{ name: 'AES-GCM', iv },
key,
encoded
);
return { encryptedData, iv };
} Avoid storing tokens in local storage to reduce the risk of misuse.
Web security Testing and Regular Audits
Security is not a one-time task. It needs regular checks and updates.
- Static Analysis Tools (SAST):
Use ESLint plugins for security to catch issues during development. - Dynamic Testing (DAST):
Tools like OWASP ZAP and Burp Suite can find runtime vulnerabilities. - Manual Testing:
Regular code reviews and penetration tests help find subtle issues.
Here’s a sample ESLint configuration with security plugins:
// .eslintrc.js configuration
module.exports = {
extends: [
'eslint:recommended',
'plugin:react/recommended',
'plugin:security/recommended'
],
plugins: [
'react',
'security'
],
rules: {
'security/detect-object-injection': 'error',
'security/detect-non-literal-regexp': 'error',
'security/detect-unsafe-regex': 'error'
}
};
Conclusion
Web security is a shared duty. Front-end developers must check inputs, stop XSS/CSRF, secure authentication, and test often. By following these practices and using the code examples, you can build strong web applications that protect users and their data.
Google Lighthouse: Why Every Developer Must Use This Tool?
In the current world of development, the performance of the website is a critical aspect. A fast, user-friendly, optimized website not only invite more visitors to your site but also keep them engaged. Lighthouse is an open-source tool created by Google that has become an essential part of the tools available to web developers and digital marketers striving for higher website performance. In this blog, we will learn what Lighthouse is, how it works and how it is an essential tool for web optimization.
What Is Google Lighthouse?
Google Lighthouse is an open-source tool that automatically checks web page’s quality based on the set of parameters and offers the appropriate recommendations. The tool assesses websites based on the following five broad categories:
- Performance: Lighthouse checks how fast your webpage loads and becomes interactive. It measures when content appears, when the page is fully loaded, and when it’s ready for user input, helping developers make faster, more user-friendly websites.
- Accessibility: Lighthouse checks if your site is accessible to people with disabilities by evaluating things like colour contrast, keyboard navigation, and image alt text. It also looks at form layout and overall HTML to ensure it’s easy for everyone to use, leading to better websites for all.
- Best Practices: Lighthouse’s Best Practices ensure your website is safe, up-to-date, and secure. It checks for HTTPS, updated code, and safe libraries, as well as proper permissions and compatibility across devices and browsers, leading to a faster and safer user experience.
- SEO: SEO checks in Lighthouse help improve your site’s visibility on search engines. It ensures your page is accessible to search engines, uses proper headings, has a good title and description, and is mobile-friendly. Following these tips can boost your site’s ranking and attract more organic traffic.
- Progressive Web App (PWA): PWA tests make your website behave like an app by ensuring it’s fast, reliable, and engaging. It checks offline functionality, fast loading, HTTPS security, installability, and responsiveness across devices. Following these standards helps provide a smooth app-like experience for users.
How Does Google Lighthouse Work?
Lighthouse can perform audits by approximating a user experience on your website. Here’s how it works:
- Initiating the Audit: Users can use Lighthouse through Google Chrome DevTools, the command line or Node.js. To start the audit, just open your website and then wait when the Lighthouse will analyse the page.
Example Script: Using Node.js Script
const lighthouse = require('lighthouse');
const chromeLauncher = require('chrome-launcher');
(async () => {
const chrome = await chromeLauncher.launch({ chromeFlags: ['--headless'] });
const options = { logLevel: 'info', output: 'html', onlyCategories: ['performance'], port: chrome.port };
const runnerResult = await lighthouse('https://example.com', options);
// Use results
console.log('Report is done:', runnerResult.lhr.finalUrl);
console.log('Performance score is', runnerResult.lhr.categories.performance.score * 100);
await chrome.kill();
})();
The script runs Lighthouse on the specified URL and generates performance scores and an HTML report.
- Simulating User Conditions: Lighthouse simulates how an average user might experience your website. For example, by testing it on a mid-range Android phone with a 4G internet connection. This helps you understand how your site performs under typical conditions, especially for users with slower internet speeds or less powerful devices
Example: Using Command Line
lighthouse https://example.com --throttling.cpuSlowdownMultiplier=4 --throttling-method=simulate --output html --view
C:\Users\user>lighthouse https://www.neovasolutions.com/ --throttling.cpuSlowdownMultiplier=4 --throttling-method=simulate --output html --view We're constantly trying to improve Lighthouse and its reliability.
Learn more: https://github.com/GoogleChrome/Lighthouse/blob/main/docs/error-reporting.md
May we anonymously report runtime exceptions to improve the tool over time?
We'll remember your choice, but you can also use the flag --[no-]enable-error-reporting (y/N) · false
LH:ChromeLauncher Waiting for browser... +0ms
LH:ChromeLauncher Waiting for browser... +1ms
LH:ChromeLauncher Waiting for browser... +509ms
LH:ChromeLauncher Waiting for browser... +521ms
LH:ChromeLauncher Waiting for browser... +18ms
LH:status Connecting to browser +15
LH:status Navigating to about:blank +7ms
LH:status Benchmarking machine +78ms
LH:status Preparing target for navigation mode +1s
LH:status Cleaning origin data +133ms
LH:status Cleaning browser cache +1s
LH:status Preparing network conditions +2s
LH:status Navigating to https://www.neovasolutions.com/ +822ms
- Generating Reports on Google Lighthouse: After the audit, Lighthouse prepares few pages, and at the end of each page there will be a concise conclusion about how your site navigates and where it fails. It has advantages and disadvantages for your website and provides tips. Example Report:



Why Should You Use Lighthouse for Web Optimization?
1. Improves Core Web Vitals
Core Web Vitals focus on making websites fast, easy to use, and stable while loading. Lighthouse measures these factors and gives helpful suggestions to improve them. Fixing these issues makes your site work better for visitors and can improve your Google ranking too.
2. Boosts User Experience
Performance affects the user satisfaction on the website. Long time-to-locate, and minimal visibility makes users turn away from such sites. It is a web app that detects areas of high-performance cost and accessibility problems so you can deliver a frictionless experience.
3. Enhances SEO
It will also reveal that search engines favour well optimized web sites. Lighthouse reviews a site’s metadata, responsiveness, and other SEO factors. It also offers tips to improve its search rankings.
4. Ensures Modern Standards
The web development continues to grow fast and remain standards-conscious to make it secure and compatible. Lighthouse looks for old technologies, insecure links, and low-quality coding patterns, so you will know when it’s time to improve.
5. Supports Progressive Web App (PWA) Development
We guarantee that if you are going to create a PWA, none is as useful as Lighthouse. It helps ensure that key requirements like offline mode, fast loading, and mobile-first design are properly implemented in your app.
How to Access Google Lighthouse
1. Using Chrome DevTools:
- Open the website in Chrome, right-click, select “Inspect,” go to the Lighthouse tab, and click “Generate report.”



2. Command Line Interface:
- Install Lighthouse via npm (npm install -g lighthouse).
C:\Users\user>npm install -g lighthouse
added 164 packages in 2m
11 packages are looking for funding
run `npm fund` for details
- Run audits directly from your terminal.
C:\Users\user>lighthouse https://www.neovasolutions.com/ --throttling.cpuSlowdownMultiplier=4 --throttling-method=simulate --output html --view
We're constantly trying to improve Lighthouse and its reliability.
Learn more: https://github.com/GoogleChrome/Lighthouse/blob/main/docs/error-reporting.md
May we anonymously report runtime exceptions to improve the tool over time?
We'll remember your choice, but you can also use the flag --[no-]enable-error-reporting (y/N) · false
LH:ChromeLauncher Waiting for browser... +0ms
LH:ChromeLauncher Waiting for browser... +1ms
LH:ChromeLauncher Waiting for browser... +509ms
LH:ChromeLauncher Waiting for browser... +521ms
LH:ChromeLauncher Waiting for browser... +18ms
LH:status Connecting to browser +15
LH:status Navigating to about:blank +7ms
LH:status Benchmarking machine +78ms
LH:status Preparing target for navigation mode +1s
LH:status Cleaning origin data +133ms
LH:status Cleaning browser cache +1s
LH:status Preparing network conditions +2s
LH:status Navigating to https://www.neovasolutions.com/ +822ms
LH:status Getting artifact: DevtoolsLog +19s
LH:status Getting artifact: Trace +0ms
LH:status Getting artifact: RootCauses +1ms
LH:status Getting artifact: Accessibility +1s
LH:status Getting artifact: AnchorElements +1ms
By default, Lighthouse generates an HTML report in the current working directory.
3. Google Lighthouse CI:
- Set it up by installing the Lighthouse CI CLI and configuring it in your CI workflow to run audits and generate reports.
- Check Results Lighthouse CI gives you reports with performance scores and highlights issues to fix.



In this example, the action kicked in and failed, since I have a large image on the about page.
Conclusion
Google Lighthouse is a valuable tool that helps improve your website in many ways. It checks your site’s speed, accessibility, and best practices, providing clear feedback on areas for improvement. Regular use helps keep your site fast, user-friendly, and up to date with modern web standards, ultimately contributing to a better web experience.























