Develop Security Plugins
This page is being updated.
RESTHeart v6 introduces many news features, improvements and changes.
As a consequence, this page will be updated soon.
Read Upgrade to v6 for a one-page description of the changes.
Introduction
This section provides detailed information on how to implement custom security plugins.
Authentication Mechanisms
An Authentication Mechanism authenticates incoming requests.
RESTHeart provides different implementations: Basic Authentication, Digest Authentication, JSON Web Token Authentication, Token Authentication
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.
See Security Overview for an high level view of the RESTHeart security model.
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 @InjectConfiguration
annotation:
@InjectConfiguration
public void init(Map<String, Object> args) throws ConfigurationException {
// get configuration arguments
int number = argValue(args, "number");
String string = argValue(args, "string");
}
The parameters are defined in the configuration file under the auth-mechanisms
section using the name of the mechanism as defined by the @RegisterPlugins
annotation:
auth-mechanisms:
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 headerAuthotization
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 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 to extends the Authentication Mechanism with different Authenticator
implementations. For instance the BasicAuthMechanism can use Authenticator implementations that hold accounts information on a DB or on a LDAP server.
Tip: Use the @InjectConfiguration
and @InjectPluginsRegistry
to retrieve the instance of the Authenticator
from its name.
private Authenticator authenticator;
@InjectConfiguration
@InjectPluginsRegistry
public void init(final Map<String, Object> args, PluginsRegistry pluginsRegistry) throws ConfigurationException {
// the authenticator specified in auth mechanism configuration
this.authenticator = pluginsRegistry.getAuthenticator(argValue(args, "authenticator")).getInstance();
}
@Override
public AuthenticationMechanismOutcome authenticate(final HttpServerExchange exchange,
final SecurityContext securityContext) {
var account = this.authenticator.verify(id, credential);
if (account != null) {
securityContext.authenticationComplete(sa, "IdentityAuthenticationManager", true);
return AuthenticationMechanism.AuthenticationMechanismOutcome.AUTHENTICATED;
} else {
securityContext.authenticationFailed("authentication failed", getMechanismName());
return AuthenticationMechanismOutcome.NOT_AUTHENTICATED;
}
}
Authenticators
Authenticators verify credentials passed by the client and build the Account
.
RESTHeart provides two implementations of Authenticator
: FileRealmAuthenticator and MongoRealmAuthenticator that handle credentials in a configuration file and on a MongoDb collection respectively.
The Authenticator class must implement the org.restheart.plugins.security.Authenticator
interface.
public interface Authenticator extends IdentityManager {
@Override
public Account verify(Account account);
@Override
public Account verify(String id, Credential credential);
@Override
public Account verify(Credential credential);
}
Registering
The Authenticator class must be annotated with @RegisterPlugin
:
@RegisterPlugin(name="myAuthenticator",
description = "my custom authenticator")
public class MyAuthenticator implements Authenticator {
}
Configuration
The Authenticator can receive parameters from the configuration file using the @InjectConfiguration
annotation:
@InjectConfiguration
public void init(Map<String, Object> args) throws ConfigurationException {
// get configuration arguments
int number = argValue(args, "number");
String string = argValue(args, "string");
}
The parameters are defined in the configuration file under the authenticators
section using the name of the authenticator as defined by the @RegisterPlugins
annotation:
authenticators:
myAuthenticator:
number: 10
string: a string
Authorizers
Authorizers check if the authenticated client can execute the request according to the security policy.
RESTHeart provides two implementations of Authorizer
: FileAclAuthorizer and MongoAclAuthorizer that handle the ACL in a configuration file and on a MongoDb collection respectively.
The Authorizer implementation class must implement the org.restheart.plugins.security.Authorizer
interface.
public interface Authorizer extends ConfigurablePlugin {
/**
*
* @param request
* @return true if request is allowed
*/
boolean isAllowed(final Request request);
/**
*
* @param request
* @return true if not authenticated user won't be allowed
*/
boolean isAuthenticationRequired(final Request request);
}
Registering
The Authorizer class must be annotated with @RegisterPlugin
:
@RegisterPlugin(name="myAuthorizer",
description = "my custom authorizer")
public class MyAuthorizer implements Authorizer {
}
Configuration
The Authorizer can receive parameters from the configuration file using the @InjectConfiguration
annotation:
@InjectConfiguration
public void init(Map<String, Object> args) throws ConfigurationException {
// get configuration arguments
int number = argValue(args, "number");
String string = argValue(args, "string");
}
The parameters are defined in the configuration file under the authorizers
section using the name of the authorizer as defined by the @RegisterPlugins
annotation:
authorizers:
myAuthorizer:
number: 10
string: a string
Token Managers
A Token Manager
generates and verify authentication tokens. When a request gets authenticated in any configured way, for instance via Basic Authentication, the response contains the Auth-Token
header. The value of this token is generated by the Token Manager. Further requests can use this auth token in place of the actual credentials.
The Token Manager works in conjunction with the Authentication Mechanism tokenBasicAuthMechanism
. This is the mechanism that handles the token and delegates its verification to the configured token manager.
RESTHeart provides one implementation of TokenManager
: RndTokenManager that generates random tokens.
The Token Manager implementation class must implement the org.restheart.plugins.security.TokenManager
interface.
Note that TokenManager
extends Authenticator
for token verification methods.
public interface TokenManager extends Authenticator, ConfigurablePlugin {
public static final HttpString AUTH_TOKEN_HEADER = HttpString.tryFromString("Auth-Token");
public static final HttpString AUTH_TOKEN_VALID_HEADER = HttpString.tryFromString("Auth-Token-Valid-Until");
public static final HttpString AUTH_TOKEN_LOCATION_HEADER = HttpString.tryFromString("Auth-Token-Location");
public static final HttpString ACCESS_CONTROL_EXPOSE_HEADERS = HttpString.tryFromString("Access-Control-Expose-Headers");
/**
* retrieves of generate a token valid for the account
*
* @param account
* @return the token for the account
*/
public PasswordCredential get(final Account account);
/**
* invalidates the token bound to the account
*
* @param account
*/
public void invalidate(final Account account);
/**
* updates the account bound to a token
*
* @param account
*/
public void update(final Account account);
/**
* injects the token headers in the response
*
* @param exchange
* @param token
*/
public void injectTokenHeaders(final HttpServerExchange exchange, final PasswordCredential token);
}
Registering
The Token Manager class must be annotated with @RegisterPlugin
:
@RegisterPlugin(name="myTokenManager",
description = "my custom token manager")
public class MyTokenManager implements TokenManager {
}
Only one token manager can be used. If more than one token-manager are defined and enabled, only the first one will be used.
Configuration
The Token Manager can receive parameters from the configuration file using the @InjectConfiguration
annotation:
@InjectConfiguration
public void init(Map<String, Object> args) throws ConfigurationException {
// get configuration arguments
int number = argValue(args, "number");
String string = argValue(args, "string");
}
The parameters are defined in the configuration file under the token-manager
section using the name of the token manager as defined by the @RegisterPlugins
annotation:
token-manager:
myTokenManager:
number: 10
string: a string