Edit Page

Authentication Mechanisms

Authentication Mechanisms

An Authentication Mechanism authenticates incoming requests.

RESTHeart provides different implementations:

These are all different methods for the client to pass some sort of credentials to the server. For example, BasicAuthMechanism extracts the credentials from the Authorization request header following the Basic Authentication specs RFC 7617.

Once the Authentication Mechanism has retrieved the credentials from the request, it can delegate the actual verification to an Authenticator that checks them against a credentials db that can be as simple as a configuration file or a database or an LDAP server.

Multiple Authentication Mechanisms can be enabled. The request is authenticated if any of the enabled Authentication Mechanisms successfully verifies the request.

Implementation

The Authentication Mechanism class must implement the org.restheart.plugins.security.AuthMechanism interface.

public interface AuthMechanism extends AuthenticationMechanism, ConfigurablePlugin {
    @Override
    public AuthenticationMechanismOutcome authenticate(final HttpServerExchange exchange, final SecurityContext securityContext);

    @Override
    public ChallengeResult sendChallenge(final HttpServerExchange exchange, final SecurityContext securityContext);

    default String getMechanismName() {
        return PluginUtils.name(this);
    }
}

Registering

The Authentication Mechanism implementation class must be annotated with @RegisterPlugin:

@RegisterPlugin(name="myAuthMechanism",
        description = "my custom auth mechanism")
public class MyAuthMechanism implements AuthMechanism {

}

Configuration

The Authentication Mechanism can receive parameters from the configuration file using the @Inject("config") annotation:

@Inject("config")
private Map<String, Object> config;

@OnInit
public void init() throws ConfigurationException {
    // get configuration arguments
    int number  = argValue(this.config, "number");
    String string = argValue(this.config, "string");
}

The parameters are defined in the configuration using the name of the mechanism as defined by the @RegisterPlugins annotation:

myAuthMechanism:
    number: 10
    string: a string

authenticate()

The method authenticate() must return:

  • NOT_ATTEMPTED: the request cannot be authenticated because it doesn’t fulfill the authentication mechanism requirements. An example is in BasicAuthMechanism when the request does not include the header Authorization or its value does not start with Basic.

  • NOT_AUTHENTICATED: the Authentication Mechanism handled the request but could not authenticate the client, for instance because of wrong credentials.

  • AUTHENTICATED: the Authentication Mechanism successfully authenticated the request.

To mark the authentication as failed in authenticate():

securityContext.authenticationFailed("authentication failed", getMechanismName());
return AuthenticationMechanismOutcome.NOT_AUTHENTICATED;

To mark the authentication as successful in authenticate():

// build the account
final Account account;

securityContext.authenticationComplete(account, getMechanismName(), false);

return AuthenticationMechanismOutcome.AUTHENTICATED;

sendChallenge()

sendChallenge() is executed when the authentication fails.

The mechanism should not set the response code; instead, that should be indicated in the AuthenticationMechanism.ChallengeResult, and the most appropriate overall response code will be selected.

This is due to the fact that several mechanisms can be enabled, and as an in-bound request is received, the authenticate method is called on each mechanism in turn until one of the following occurs: a mechanism successfully authenticates the incoming request, or the list of mechanisms is exhausted.

sendChallenge() can also be used to set a response header.

An example is BasicAuthMechanism that, in case of failure, indicates the response code 401 Not Authenticated and sets the following challenge header:

WWW-Authenticate: Basic realm="RESTHeart Realm"

This is the code:

@Override
public ChallengeResult sendChallenge(HttpServerExchange exchange, SecurityContext securityContext) {
    exchange.getResponseHeaders().add(WWW_AUTHENTICATE, challenge);
    return new ChallengeResult(true, UNAUTHORIZED);
}

Build the Account

To build the account, the Authentication Mechanism can use a configurable Authenticator. This allows extending the Authentication Mechanism with different Authenticator implementations. For instance, the BasicAuthMechanism can use Authenticator implementations that hold accounts information on a DB or on an LDAP server.

Tip
Use the @Inject("config") and @Inject("registry") to retrieve the instance of the Authenticator from its name.
private Authenticator authenticator;

@Inject("config")
private Map<String, Object> config;

@Inject("registry")
private PluginsRegistry registry;

@OnInit
public void init() throws ConfigurationException {
    // the authenticator specified in auth mechanism configuration
    this.authenticator = this.registry.getAuthenticator(argValue(this.config, "authenticator")).getInstance();
}

@Override
public AuthenticationMechanismOutcome authenticate(final HttpServerExchange exchange, final SecurityContext securityContext) {
        var account = this.authenticator.verify(id, credential);
        if (account != null) {
          securityContext.authenticationComplete(account, "IdentityAuthenticationManager", true);
          return AuthenticationMechanism.AuthenticationMechanismOutcome.AUTHENTICATED;
        } else {
          securityContext.authenticationFailed("authentication failed", getMechanismName());
          return AuthenticationMechanismOutcome.NOT_AUTHENTICATED;
        }
}