Skip to content

API Key Authentication

We will start by providing the required configuration for this strategy. You should change all of these values as per your requirement.

js
{
  "authentication": {
    ...otherConfig,
    "authStrategies": [ ...otherStrategies, "apiKey" ],
    "apiKey": {
      "allowedKeys": [ "API_KEY_1", "API_KEY_2" ],
      "header": "x-access-token"
    }
  }
}

Note: if all you want is api key authentication, it is still necessary to register a secret, service and entity. Since no other authentication method is used, entity can be null.

A fully working example with just API key authentication:

json
{
  "host": "localhost",
  "port": 3030,
  "public": "../public/",
  "paginate": {
    "default": 10,
    "max": 50
  },
  "authentication": {
    "secret": "some-secret",
    "service": "users",
    "entity": null,
    "authStrategies": ["apiKey"],
    "apiKey": {
      "allowedKeys": [ "API_KEY_1", "API_KEY_2" ],
      "header": "x-access-token"
    }
  }
}

Next we will be creating a custom strategy that returns the params that you would like to use to identify an authenticated user/request.

ts
import { AuthenticationBaseStrategy, AuthenticationResult, AuthenticationService } from '@feathersjs/authentication';
import { NotAuthenticated } from '@feathersjs/errors';
import { ServiceAddons } from '@feathersjs/feathers';
import { Application } from './declarations';


declare module './declarations' {
  interface ServiceTypes {
    'authentication': AuthenticationService & ServiceAddons<any>;
  }
}

class ApiKeyStrategy extends AuthenticationBaseStrategy {
  app: Application;

  constructor(app: Application) {
    super();
    this.app = app;
  }

  async authenticate(authentication: AuthenticationResult) {
    const { token } = authentication;

    const config = this.app.get('authentication').apiKey;

    const match = config.allowedKeys.includes(token);
    if (!match) throw new NotAuthenticated('Incorrect API Key');

    return {
      apiKey: true
    }
  }
}

export default function (app: Application) {
  const authentication = new AuthenticationService(app);

  // This can have multiple .register calls if multiple strategies have been added
  authentication.register('apiKey', new ApiKeyStrategy(app));

  app.use('/authentication', authentication);
}

Next, we create a hook called allow-apiKey that sets params.authentication if it does not exist and if params.provider exists (which means it is an external call) to use that apiKey strategy. We will also provide the capability for the apiKey to be read from the request header: (you could also read the token as a query parameter but you will have to filter it out before it's passed to Feathers calls like get and find.

ts
import { HookContext, NextFunction } from '@feathersjs/feathers';

export default () => async (context: HookContext, next: NextFunction) => {
  const { params, app } = context;

  const headerField = app.get('authentication').apiKey.header;
  const token = params.headers ? params.headers[headerField] : null;

  if (token && params.provider && !params.authentication) {
    context.params = {
      ...params,
      authentication: {
        strategy: 'apiKey',
        token
      }
    };
  }

  return next();
}

This hook should be added before the authenticate hook wherever API Key authentication should be allowed:

js
import { authenticate } from '@feathersjs/authentication/lib/hooks';
import allowApiKey from './hooks/allow-api-key';

all: [ allowApiKey(), authenticate('jwt', 'apiKey') ],

If a user now accesses the service externally with the correct apiKey, the service call will succeed and have params.apiKey set to true.

Released under the MIT License.