nemoxi
nemoxi

Reputation: 620

How to secure a REST API with an API key

I'm currently creating a Rest API with NestJS (it's really cool by the way).

In this API, I'm using JWT (JSON Web Token) to allow users to log in and view different resources based on their role.

However, I want to implement an API Key system to protect the API itself. I don't want any developer to be able to use my API. I want him to go through this API Key to use my API.

Either by a query in the URL: https://domaine.com?api_key=${API_KEY} or via the header:

GET /v1/some-resource
Host: docmaine.com
Accept: application/json
X-API-KEY: MyAwes0m3API_KeY

Do you have a tutorial, course or a track to advise me?

Upvotes: 12

Views: 11662

Answers (2)

Hugo Bayoud
Hugo Bayoud

Reputation: 234

The simplest guard you can made is:

import {
  Injectable,
  CanActivate,
  ExecutionContext,
  UnauthorizedException,
} from '@nestjs/common';

@Injectable()
export class ApiKeyGuard implements CanActivate {
  async canActivate(context: ExecutionContext): Promise<boolean> {
    const request = context.switchToHttp().getRequest();

    const apiKey = request.headers['api-key']; // give the name you want

    if (!apiKey) {
      throw new UnauthorizedException('API key is missing.');
    }

    // call your env. var the name you want
    if (apiKey !== process.env.API_KEY) {
      throw new UnauthorizedException('Invalid API key.');
    }

    return true;
  }
}

And use it globally for every routes of your controller.

@Controller()
@UseGuards(ApiKeyGuard)
export class YourController {
  // constructor () {}

  // your routes POST,GET,PATCH, ....
}

And give the env. var API_KEY to your client-side (he needs to protect it).

Upvotes: 5

Jay McDoniel
Jay McDoniel

Reputation: 70061

Why not create a guard that checks for the validity of that header?

@Injectable()
export class ApiKeyGuard implements CanActivate {
  constructor(private readonly apiKeyService: ApiKeyService) {} // made up service for the point of the exmaple

  async canActivate(context: ExecutionContext): Promise<boolean> {
    const req = context.switchToHttp().getRequest();
    const key = req.headers['X-API-KEY'] ?? req.query.api_key; // checks the header, moves to query if null
    return this.apiKeyService.isKeyValid(key);
  }
}

And now you can use @UseGuards(ApiKeyGuard) on whatever route, or bind it globally, and you've got some basic authentication up for your server.

Upvotes: 28

Related Questions