Service

npm version Changelog

$ npm install @feathersjs/authentication --save

The AuthenticationService is a Feathers service that allows to register different authentication strategies and manage access tokens (using JSON web tokens (JWT) by default). This section describes

  • The standard setup used by the generator
  • How to configure authentication and where the configuration should go
  • The methods available on the authentication service
  • How to customize the authentication service
  • The Events sent by the authentication service

Setup

The standard setup of the generator initializes an AuthenticationService at the /authentication path with a JWT strategy, Local strategy and oAuth authentication.

    Configuration

    The standard authentication service configuration is normally located in the authentication section of a configuration file (default: config/default.json).

    Note: The authentication service can also be configured dynamically or without Feathers configuration by using app.set, e.g. app.set('authentication', config).

    The following options are available:

    • secret: The JWT signing secret.
    • service: The path of the entity service
    • authStrategies: A list of authentication strategy names to allow on this authentication service to create access tokens.
    • entity: The name of the entity. Can be null if no entity is used (see stateless tokens).
    • entityId: The id property of an entity object. Only necessary if the entity service does not have an id property (e.g. when using a custom entity service).
    • jwtOptions: All options available for the node-jsonwebtoken package.

    An authentication service configuration in config/default.json can look like this:

    {
      "authentication": {
        "secret": "CHANGE_ME",
        "entity": "user",
        "service": "users",
        "authStrategies": [ "jwt", "local" ],
        "jwtOptions": {
          "header": { "typ": "access" },
          "audience": "https://yourdomain.com",
          "issuer": "feathers",
          "algorithm": "HS256",
          "expiresIn": "1d"
        }
      }
    }
    

    Note: typ in the header options is not a typo, it is part of the JWT JOSE header specification.

    Additionally to the above configuration, most strategies will look for their own configuration under the name it was registered. An example can be found in the local strategy configuration.

    AuthenticationService

    constructor(app [, configKey])

    const authService = new AuthenticationService(app, configKey = 'authentication') initializes a new authentication service with the Feathers application instance and a configKey which is the name of the configuration property to use via app.get() (default: app.get('authentication')). Upon initialization it will also update the configuration with the default settings.

    Important: Unless otherwise customized configKey

    authenticate(data, params, ...strategies)

    authService.authenticate(data, params, ...strategies) -> Promise is the main authentication method and authenticates data and params against a list of strategies in strategies.

    data must always contain a strategy property indicating the name of the strategy. If data.strategy is not available or not allowed (included in the strategies list) a NotAuthenticated error will be thrown. Otherwise the result of strategy.authenticate() will be returned.

    create(data, params)

    authService.create(data, params) -> Promise runs authService.authenticate with data, params and the list of strategies from authStrategies in the configuration. As with any other Feathers service, this method will be available to clients, e.g. running a POST /authentication.

    If successful it will create a JWT with the payload taken from authService.getPayload and the options from authService.getTokenOptions. data must always contain a valid and allowed strategy name. Will emit the login event.

    remove(id, params)

    authService.remove(id, params) -> Promise should be called with id set to null or to the authenticated access token. Will verify params.authentication and emit the logout event if successful.

    configuration

    authService.configuration returns a copy of current value of app.get(configKey) (default: app.get('authentication')). This is a deep copy of the configuration and is not intended to be modified. In order to change the configuration, app.set(configKey) should be used:

    const config = app.get('authentication');
    
    // Update configuration with a new entity
    app.set('authentication', {
      ...config,
      entity: 'some other entity name'
    });
    

    register(name, strategy)

    authService.register(name, strategy) registers an authentication strategy under name and calls the strategy methods setName, setApplication, setAuthentication and verifyConfiguration if they are implemented.

    getStrategies(...names)

    service.getStrategies(...names) -> AuthenticationStrategy[] returns the authentication strategies that exist for a list of names. The returned array may include undefined values if the strategy does not exist. Usually authentication strategies do not need to be used directly.

    const [ localStrategy ] = authService.getStrategies('local');
    

    createAccessToken(payload)

    authService.createAccessToken(payload, [options, secret]) -> Promise creates a new access token. By default it is a JWT with payload, using configuration.jwtOptions merged with options (optional). It will either use authService.configuration.secret or the optional secret to sign the JWT. Throws an error if the access token can not be created.

    const token = await app.service('authentication').createAccessToken({
      permission: 'admin'
    });
    

    Note: Normally, it is not necessary to call this method directly. Calling authService.create(data, params) using an authentication strategy will take care of creating the correct access token.

    verifyAccessToken(accessToken)

    authService.verifyAccessToken(accessToken, [options, secret]) -> Promise verifies the access token. By default it will try to verify a JWT using configuration.jwtOptions merged with options (optional). Will either use configuration.secret or the optional secret to verify the JWT. Returns the encoded payload or throws an error.

    getTokenOptions(authResult, params)

    authService.getTokenOptions(authResult, params) -> Promise returns the options for creating a new access token based on the return value from calling authService.authenticate(). Called internally on authService.create(). It will try to set the JWT subject to the entity (user) id if it is available which will then be used by the JWT strategy to populate params[entity] (usually params.user).

    getPayload(authResult, params)

    authService.getPayload(authResult, params) -> Promise returns the access token payload for an authentication result (the return value of authService.create()) and service call parameters. Called internally on .create. Returns either params.payload or an empty object ({}).

    parse(req, res, ...strategies)

    authService.parse(req, res, ...strategies) -> Promise parses a NodeJS HTTP request and HTTP response for authentication information using strategies calling each strategies .parse() method if it is implemented. Will return the value of the first strategy that didn't return null. This does not authenticate the request, it will only return authentication information that can be used by authService.authenticate or authService.create.

    setup(path, app)

    authService.setup(path, app) verifies the configuration and makes sure that

    • A secret has been set
    • If entity is not null, check if the entity service is available and make sure that either the entityId configuration or the entityService.id property is set.
    • Register internal hooks to send events and keep real-time connections up to date. All custom hooks should be registered at this time.

    app.get('defaultAuthentication')

    After registering an authentication service, it will set the defaultAuthentication property on the application to its configuration name (configKey set in the constructor) if it does not exist. app.get('defaultAuthentication') will be used by other parts of Feathers authentication to access the authentication service if it is not otherwise specified. Usually this will be 'authentication'.

    Customization

    The AuthenticationService can be customized like any other ES6 class:

    const { AuthenticationService } = require('@feathersjs/authentication');
    
    class MyAuthService extends AuthenticationService {
      async getPayload(authResult, params) {
        // Call original `getPayload` first
        const payload = await super.getPayload(authResult, params);
        const { user } = authResult;
    
        if (user && user.permissions) {
          payload.permissions = user.permissions;
        }
    
        return payload;
      }
    }
    
    app.use('/authentication', new MyAuthService(app));
    

    Things to be aware of when extending the authentication service:

    • When implementing your own constructor, always call super(app, configKey)
    • When overriding a method, calling super.method and working with its return value is recommended unless you are certain your custom method behaves exactly the same way, otherwise things may no longer work as expected.
    • When extending setup, super.setup(path, app) should always be called, otherwise events and real-time connection authentication will no longer work.

    Events

    For both, login and logout the event data is (authResult, params, context) => {} as follows:

    • authResult is the return value of the authService.create or authService.remove call. It usually contains the user and access token.
    • params is the service call parameters
    • context is the service methods hook context

    app.on('login')

    app.on('login', (authResult, params, context) => {}) will be sent after a user logs in. This means, after any successful external call to authService.create.

    Important: The login event is also sent for e.g. reconnections of websockets and may not always have a corresponding logout event. Use the disconnect event for handling disconnection.

    app.on('logout')

    app.on('logout', (authResult, params, context) => {}) will be sent after a user explicitly logs out. This means after any successful external call to authService.remove.