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

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.
    • parseStrategies: A list of authentication strategies that should be used to parse HTTP requests. Defaults to the same as authStrategies.
    • entity: The name of the field that will contain the entity after successful authentication. Will also be used to set params[entity] (usually params.user) when using the authenticate hook. 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.

    Authentication flows

    The following

    To create a new JWT

    For any strategy allowed in authStratgies, a user can call app.service('/authentication').create(data) or POST /authentication with data as { strategy: name, ...loginData }. Internally authentication will then

    • Call the strategy .authenticate method with data
    • Create a JWT for the entity returned by the strategy
    • Return the JWT (accessToken) and the additional information from the strategy

    For local strategy, the user has to be created before doing auth, otherwise, a 401 NotAuthenticated error will be send.

    To authenticate an external request

    For any HTTP request and strategy allowed in parseStrategies or - if not set - authStrategies authentication will:

    • Call strategy.parse and set the return value of the first strategy that does not return null as params.authentication
    • Verify params.authentication using the authenticate hook which calls the strategy .authenticate method with params.authentication as the data
    • Merge the return value of the strategy with params (e.g. setting params.user)

    To authenticate your own service request

    For any service that uses the authenticate hook called internally you can set params.authentication in the service call which will then:

    • Verify params.authentication using the authenticate hook which calls the strategy .authenticate method with params.authentication as the data
    • Merge the return value of the strategy with params (e.g. setting params.user)

    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.