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 headerAuthorization
or its value does not start withBasic
. -
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;
}
}