Security 101: Securing API Usage

Securing API Usage in Angular and Node.js for Logged-In and Anonymous Users

Introduction

In modern web applications, securing API usage is a critical aspect of both user experience and application security. Whether your users are logged in or accessing your services anonymously, it’s essential to implement robust security practices to prevent abuse, protect data, and ensure the smooth functioning of your APIs.

In this blog post, we’ll explore best practices for securing API usage in applications built with Angular and Node.js. We’ll cover two primary use cases: securing APIs for logged-in users and managing API usage for anonymous users. We’ll dive into key techniques such as JWT authentication, IP-based tracking, rate limiting, and the use of temporary tokens.


Use Case 1: Securing API Usage for Logged-In Users

Setting Up the Environment

Backend with Node.js and Express

To start, we’ll set up a Node.js server using Express. This server will handle API requests and manage user authentication using JSON Web Tokens (JWT).

1. Initialize the Node.js Project

Begin by creating a new Node.js project and installing the necessary dependencies:

mkdir secure-api-demo
cd secure-api-demo
npm init -y
npm install express body-parser jsonwebtoken

2. Create the Express Server

Next, create a server.js file to set up a basic Express server. This server will issue JWTs for authenticated users and protect API routes.

const express = require('express');
const bodyParser = require('body-parser');
const jwt = require('jsonwebtoken');

const app = express();
const port = 3000;
const API_SECRET = 'your-secret-key';

app.use(bodyParser.json());

// Middleware to validate token
function authenticateToken(req, res, next) {
    const token = req.headers['authorization'];
    if (!token) return res.sendStatus(403);

    jwt.verify(token, API_SECRET, (err, user) => {
        if (err) return res.sendStatus(403);
        req.user = user;
        next();
    });
}

// Route to generate a new token
app.post('/login', (req, res) => {
    const username = req.body.username;
    const user = { name: username };

    const accessToken = jwt.sign(user, API_SECRET, { expiresIn: '1h' });
    res.json({ accessToken });
});

// Example API route with token validation
app.get('/data', authenticateToken, (req, res) => {
    res.json({ message: 'Secure data access' });
});

app.listen(port, () => {
    console.log(`Server running on http://localhost:${port}`);
});

Explanation:

  • jsonwebtoken: This library is used to create and verify JWTs, ensuring that only authenticated users can access certain API routes.
  • Token Validation: The authenticateToken middleware checks if a valid token is provided with the API request. If the token is missing or invalid, access is denied.

Frontend with Angular

For the frontend, we’ll create an Angular service to handle API requests, including token-based authentication.

1. Set Up Angular Project

First, set up a new Angular project:

npm install -g @angular/cli
ng new secure-api-demo
cd secure-api-demo
npm install

2. Create API Service

Create an Angular service (api.service.ts) that handles login and secure data access:

import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Observable } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class ApiService {
  private apiUrl = 'http://localhost:3000';

  constructor(private http: HttpClient) { }

  login(username: string): Observable<any> {
    return this.http.post<any>(`${this.apiUrl}/login`, { username });
  }

  getData(token: string): Observable<any> {
    const headers = new HttpHeaders().set('Authorization', token);
    return this.http.get<any>(`${this.apiUrl}/data`, { headers });
  }
}

3. Using the Service in a Component

Use this service in your Angular component to authenticate and access secure data:

import { Component } from '@angular/core';
import { ApiService } from './api.service';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  title = 'secure-api-demo';
  token: string | null = null;
  data: any = null;

  constructor(private apiService: ApiService) { }

  login() {
    this.apiService.login('user1').subscribe(response => {
      this.token = response.accessToken;
      console.log('Token:', this.token);
    });
  }

  getData() {
    if (this.token) {
      this.apiService.getData(this.token).subscribe(response => {
        this.data = response;
        console.log('Data:', this.data);
      });
    } else {
      console.log('Please login first');
    }
  }
}

Explanation:

  • HttpClient Module: The HttpClient module is used to make HTTP requests. The Authorization header is set with the JWT token to access secure API routes.
  • Token Management: The component manages the token after login and uses it for subsequent API calls.

Tracking API Usage on Server Side

1. Server-Side Tracking

On the server side, we’ll track how many times each user accesses the API. This prevents abuse by ensuring that users cannot exceed predefined usage limits.

Example Code:

const apiUsage = {};

app.get('/data', authenticateToken, (req, res) => {
    const username = req.user.name;

    // Track API usage
    if (!apiUsage[username]) {
        apiUsage[username] = 1;
    } else {
        apiUsage[username]++;
    }

    if (apiUsage[username] > 10) { // Example limit
        return res.status(429).json({ message: 'API usage limit exceeded' });
    }

    res.json({ message: 'Secure data access', usageCount: apiUsage[username] });
});

Explanation:

  • Usage Tracking: The server tracks API usage per user, enforcing a limit on the number of API calls a user can make. This prevents users from abusing the API.

Use Case 2: Securing API Usage for Anonymous Users

When dealing with anonymous users (those who don’t log in), securing API usage presents additional challenges. Without a unique user identity, you’ll need alternative methods to manage and track API usage.

Tracking by IP Address

1. Track API Usage by IP

A simple method to track anonymous users is by their IP address.

Example Code:

const apiUsage = {};

app.get('/data', (req, res) => {
    const ip = req.ip;

    // Track API usage by IP
    if (!apiUsage[ip]) {
        apiUsage[ip] = 1;
    } else {
        apiUsage[ip]++;
    }

    if (apiUsage[ip] > 10) { // Example limit
        return res.status(429).json({ message: 'API usage limit exceeded' });
    }

    res.json({ message: 'Secure data access', usageCount: apiUsage[ip] });
});

Explanation:

  • IP-Based Tracking: API usage is tracked based on the user’s IP address. While effective in many cases, this method has limitations, such as handling multiple users sharing the same IP address.

Using Temporary Tokens

1. Issue Temporary Tokens

For a more robust solution, generate temporary tokens for anonymous users, stored in cookies or local storage.

Example Code:

const crypto = require('crypto');

app.get('/generate-token', (req, res) => {
    const token = crypto.randomBytes(16).toString('hex');
    res.cookie('anonymousToken', token, { maxAge: 3600000, httpOnly: true });
    res.json({ message: 'Token generated', token });
});

app.get('/data', (req, res) => {
    const token = req.cookies.anonymousToken;

    if (!token) return res.status(403).json({ message: 'No token provided' });

    // Track API usage by token
    if (!apiUsage[token]) {
        apiUsage[token] = 1;
    } else {
        apiUsage[token]++;
    }

    if (apiUsage[token] > 10) { // Example limit
        return res.status(429).json({ message: 'API usage limit exceeded' });
    }

    res.json({ message: 'Secure data access', usageCount: apiUsage[token] });
});

Explanation:

  • Temporary Token: This approach assigns a temporary token to each anonymous user, allowing for more precise tracking of API usage per session.
  • Security: Tokens are stored in cookies, enhancing security by limiting access to the token data.

Best Practices Recap

  • Use Server-Side Tracking: Always track API usage and enforce limits on the server side.
  • JWT Authentication: Secure your API with JWT tokens to authenticate users and protect routes.
  • Rate Limiting: Implement rate limiting to prevent abuse, especially for anonymous users.
  • IP and Token-Based Tracking: For anonymous users, consider IP-based tracking or issuing temporary tokens.

Conclusion

Securing API usage is crucial for maintaining the integrity and security of your application. Whether you’re dealing with logged-in users or anonymous visitors, implementing best practices such as JWT authentication, server-side tracking, and rate limiting will help protect your API from abuse.

By following the strategies outlined in this blog post, you’ll be well-equipped to manage API usage in your Angular and

Node.js applications, ensuring that your services remain secure and reliable.


References:

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *