Ninja
Concept Description and Technical Architecture
Scope of Ninja
Perimeter Authentication
In a single sign-on environment, user authentication is typically managed by a central reverse proxy (or entry server). Often, the entry server delegates the actual authentication process to a separate authentication service. Regardless of this delegation, all application servers that can be reached through the reverse proxy have to trust the reverse proxy to forward only authenticated requests. Because an application server no longer authenticates the user directly, it must at least be able to extract the user's identity from a request. Most modern application servers offer this feature, and some vendors use the term perimeter authentication for this setup.
Secure Token Delegation
To prove that a user has been authenticated, the entry server adds a so-called secure token to every request - either as a 'basic authentication' header or as a custom HTTP header. Among other things, the secure token contains authentication data about the user; it is digitally signed by the authentication service. In such an environment, the application server must only accept requests containing a valid secure token. Because the information contained in the secure token is digitally signed, any manipulation of this data is detected. After Ninja has verified the token's integrity, it knows for sure that the token has been issued by a trusted authentication service.
To achieve end-to-end security, the secure token should be propagated along the call chain. Each node or service in this chain is responsible for verifying the token.
Nevis Context
Ninja is always active within a J2EE container as part of the protected web application.
The figure below shows the role of Ninja in the Nevis context.
To explain the basic principle of Ninja, the interactions between these components may be simplified as follows:
- The user's browser sends an HTTP request to the reverse proxy. If not yet logged on, the reverse proxy redirects the browser to a login page.
- The actual authentication is delegated to the authentication service. Although the authentication process may consist of multiple (interactive) steps, it is simplified as one arrow in the drawing. This is because we are only interested in the result of the authentication. The authentication server returns a secure token to the reverse proxy, and this token is stored in the user's single sign-on session.
- After successful authentication, subsequent requests of the same user are passed directly to the J2EE container, with one important addition: each request contains the secure token as an additional HTTP header. It is now Ninja's task to verify this token and to reject any invalid request.
What is Ninja?
Ninja is designed to seamlessly integrate a J2EE application platform into a Nevis protected environment. The goal is to protect an existing application without recompiling.
Ninja Commons
Ninja Commons provides the following basic functions:
- SecToken Verification
- Certificate Reader: reads one or multiple certificates from the specified certificate files, supports multiple formats, including JKS and PEM.
- Role Getter: maps and sets container-specific groups. Depending on the container, the terms "role" and "group" are used differently or are even not present.
- User Getter: allows to retrieve the user from the SecToken, for example, to take it out from a custom field.
Ninja Authentication Filter
This is the Ninja implementation as a Java Servlet Filter.
Ninja Uber / Fat JAR
The purpose of the Ninja Uber package is to ease the dependency management of an integration project. It does not provide any additional functionality to the basic functions but makes Ninja deployment easier by packaging all Ninja components and their dependencies into one JAR file, also known as an uber or fat JAR.
Summary
Component | package | Description |
---|---|---|
Ninja Commons | ninja-commons | Separate package for the basic functions.Depends on jcan-sectoken library, including all transitive dependencies like jcan-saml . |
Ninja Authentication Filter | ninja-filter | See the Ninja Authentication Filter chapter. Depends on ninja-commons . |
Ninja Uber | ninja-uber | Ninja uber / fat JAR containing ninja-commons and ninja-filter with all runtime dependencies. |
Ninja Features
Secure Token Verification: Only requests containing a correct token are accepted and passed to the web application. A token is considered correct if it has been issued by a trusted authenticator and if the contained data has not been tampered with, i.e., the token is authentic.
Often, the token is verified on the first request only. For subsequent requests, the application server identifies the original caller with the aid of a tracking mechanism (like a HTTP cookie).
SAML Assertion Verification: Ninja can verify and consume SAML assertions instead of Nevis SecTokens. No configuration change is necessary for this, but all the JARs of the jcan-saml library (available from Nevis, notably: jcan-saml, jcan-saml-xmlbeans) must be placed in the same class path as the Ninja JARs. Alternatively this could be also achieved by using the Ninja uber JAR. Note that even though a SAML assertion (or a SAML response) is consumed, the means of communicating this message depends on Ninja and does not conform to the transport mechanisms of the SAML 2.0 protocol specification.
Servlet Authentication: This is the simple authentication method defined in the Java Servlet specification. It uses a Principal object to identify users and in turn contains public and private credentials. The central methods for obtaining the principal and doing authorization checks are HttpServletRequest.getUserPrincipal() and HttpServletRequest.isUserInRole(String).
JAAS Authentication: The authentication modules plug into the application servers authentication framework to support declarative and programmatic security according to the J2EE standards. The authentication results in a JAAS Subject representing the users identity and roles. All further security checks are based on this Subject. The container propagates the Subject to other tiers to achieve end-to-end security.
Container Session Invalidation: To save valuable resources, and for security reasons, a user's application server session should be invalidated before the single sign-on session terminates (or as it terminates at the latest). Ninja can terminate the user session with an HTTP request. The kill-request is normally either sent by the authentication server or by the reverse proxy (see nevisProxy RefGuide on "LogoutURI").
Multiple Signer Certificates: Allows the configuration of one or more trusted signer certificates, or even a trusted certificate directory. This is not only useful if the tokens to be verified are issued by more than one authentication service, but also for the transition phase when an issuer certificate expires.
Optional Authentication: This mode of operation allows applications to respond both to authenticated and non-authenticated (anonymous) requests. A session may run for a time without authentication and then require the user to authenticate at a particular point. This may be triggered by the user accessing a protected functionality (on which optional authentication is disabled) or by the application sending a response that triggers the authentication procedure.
Development Mode: Maintaining a Nevis environment for application development may not be feasible or wanted. The development mode (DevMode) of Ninja simulates such an integrated environment without the need for any additional components or complex configuration constructs. In DevMode, Ninja will generate a SecToken for a user request and inject user properties as configured. This is transparent to the application so that it can operate as if in an integrated setup and even use SecTokens to communicate with further servers.
Known Restrictions and Pitfalls
Colons
:
in user names are not supported when Basic Authentication is used for transport.This restriction is caused by the HTTP Basic Authentication. HTTP Basic Authentication uses
:
as separator between user name and password. HTTP Basic Authentication is defined in RFC 2616.Tokens encoded with non-ASCII character-sets may need specific configuration (see TokenEncoding and TokenSourceEncoding) when Basic Authentication is used for transport.
This restriction is caused by the HTTP Basic Authentication mechanism. HTTP Basic Authentication does not clearly specify the character encoding of the payload. Java by default uses ISO-8859-1 (ISO-Latin) in the methods that are typically used for decoding the Basic Auth payload so that character set can be assumed to be in use for most containers. If the token character encoding is not ISO-8859-1 (e.g UTF-8) then the configuration option "TokenEncoding" and "TokenSourceEncoding" should be set accordingly.
JAAS login modules cannot be used for optional authentication.
Due to the binary logic of java application container realms, it is not possible to configure "optional" authentication. If an authentication realm is configured, the authentication must be completed successfully or else no requests will be forwarded to the application.
Also, once an authenticated identity is established, the login modules will not have contact with it. So establishing an anonymous pseudo-identity for unauthentic access and later on"'upgrading" it to a real identity is not possible.
For scenarios that require optional or dynamic authentication, use NinjaAuthenticationFilter and its AuthenticationOptional option instead of a JAAS login module. Multiple NinjaAuthenticationFilter with and without optional authentication can be combined within the same application.
JAAS realms must be configured for specific authentication schemes and cannot combine login modules for different authentication schemes.
JAAS authentication separates the declaration of realm usage from the realm configuration itself. But strangely, it has the application configure the realm type. This means that an application cannot support multiple authentication methods (e.g. client-certificates and Ninja, or Ninja and Forms-based are not possible).
For scenarios that require multiple authentication methods, use either multiple deployments (possibly with cross-application dispatching to avoid duplication) or filters instead of JAAS login modules. For example, NinjaAuthenticationFilter could be used in combination with a filter that does optional X509 client-certificate authentication.
While simple authentication and roles usage do not require Nevis-specific code in the application, some non-trivial use cases will necessitate a few specific adaptations and dependencies on Ninja artefacts.
Unfortunately, Java's JAAS / principal specification is very rudimentary: it only defines a simple principal name and a method for basic permissions checking. All other functionalities must be done via specialized implementation of the Principal interface.
In the case of Ninja, this is done with the NinjaTokenPrincipal. This means that applications that require more than the JAAS specification offers need to get the Principal and cast it to NinjaTokenPrincipal to be able to do non-trivial operations (e.g. forwarding the received token to other services or extracting attribute field data).
Other Supported Platforms
Our recommendation for all non-JavaEE-based backends is to migrate to JWT as token format.
Additional documentation and examples are available in the Nevis Authentication Application Integration Guide.
Configuration - Common Ninja Options
User ID and Roles
Finding User ID: By default, Ninja propagates the user ID contained in the Token to the container, a
Servlet
callingPrincipal.getName()
will get the user ID which is contained in the Nevis SecToken. If a different field of the token should be used as user ID, aUserGetter
can be configured.Finding Roles: In addition to setting the user ID, Ninja also sets "Roles" (depending on the container, the container takes these "Ninja roles" and does some additional mapping). By default, Ninja sets the role "member".
Note that the JAAS and
Servlet
principal interfaces do not allow listing roles. Instead, theServlet
specification has methods to test whether a user has a particular role or not. Thus, Java applications which require these roles have to cast the JAAS principal to theNinjaTokenPrincipal
, a class that can provide the roles and offers many options unknown to the bare JavaPrincipal
class.
User Getters
The UserGetter
s allow customisation of the user ID (in Java actually the name of the principal) that is propagated to the application. By default, Ninja uses the TokenUserGetter
that reads the field userId (from SecTokens) or Subject/NameID (from SAML Assertions). The following UserGetter
s are provided by Ninja:
Class Name | Parameters | Notes |
---|---|---|
TokenUserGetter | <no parameters> | Default UserGetter , sets the principal name to userId (when using SecTokens) or Subject/NameID (when SAML Assertions are used). |
AttributeUserGetter | source=<field-name> | Sets the value of a token attribute or field as principal name. |
Custom User Getters
To customise the process of finding the user ID, implement the interface ch.nevis.ninja.commons.mapping.UserGetter
. The constructor must take a single Map<String,String>
in which the configuration parameters will be passed. Place the JAR file containing the code together with all required dependencies in the class path or module of the container.
To activate a custom UserGetter
, specify its full class name in the Ninja configuration in option UserGetter
. Any name/value pairs contained in your UserGetter
configuration are passed to the constructor in the Map
.
Role Getters
RoleGetter
s are responsible for determining a set of roles and propagating them into the JAAS and Servlet
contexts. The Ninja default behavior is implemented by the StaticRoleGetter
setting a single role "member". This minimal default configuration allows easy configuration of authentic and unauthentic zones within the application, irrespective to the roles indicated by received tokens.
The table below shows the available standard role getters:
Class Name | Parameters | Notes |
---|---|---|
StaticRoleGetter | roles=<list separated with ","> | Default role getter, to configure default behavior use: StaticRoleGetter(roles="member") . If no roles or an empty role is specified, the default is used. |
TokenRoleGetter | <no parameters> | Takes the roles from the Nevis SecToken |
EmptyRoleGetter | <no parameters> | Does not set any roles. |
MappedRoleGetter | mappingfile=<path/to/roles-mapping.properties> | Assignes roles according to a mapping of attributes to roles defined in the configured properties file. Entries must have the following syntax: <attribute-name>.<attribute-value> : <role>,<role>… Note that the MappedRoleFilter will split attribute values by commas so that single attribute fields can trigger multiple mapping rules. |
Custom Role Getters
To customize the process of finding roles, Ninja allows implementing custom RoleGetter
s: Implement the interface ch.nevis.ninja.commons.mapping.RoleGetter
. The constructor must take a single Map<String,String>
in which the configuration parameters will be passed. Place the JAR file containing the code together with all required dependencies in the class path or module of the container.
To activate a custom RoleGetter
, specify its full class name in the Ninja configuration in option RoleGetters
. Any name/value pairs contained in the RoleGetter
configuration are passed to your constructor in the Map
.
Logging
Ninja uses SLF4J as a logging facade, which allows seamless integration with any bigger logging framework. To enable Ninja logs next to the web application logs, a binding JAR matching to the logging framework of the web application is to be provided during runtime. Ninja does not provide any concrete logging provider or binding out of the box, this is to be done by the web application integration project itself. See the related chapter of the SLF4J manual.
Ninja uses the ch.nevis.ninja
logger for its log messages, which we recommend to be added to the configuration file of the logging framework used by the web application. After that, configuring Ninja log appenders and log levels can be done the same way as for the web application itself.
Development Mode
Ninja can run in an operation mode which does not require a Nevis access management setup. This is useful in development environments, where focus is set on the application, and maintaining a complete SSO system is both unnecessary and prohibitive for testing. The configuration of the development mode (DevMode) introduces only a minimal modification to a productive Ninja setup, requiring only a few settings in the Ninja configuration block.
Enable the DevMode by setting the Ninja parameter DevMode="true"
and by configuring the following:
- DevDir: Filesystem directory where Ninja stores generated SecTokens and their source properties.
- DevTokenSignerCert: The signer certificate of SecTokens. This is usually identical to NevisSignerCertificate, but exactly one certificate must be identified. Accepts PEM, DER and JKS (with
<file>?alias=<alias>
notation) files. - DevTokenSignerKey: The signer key to use when generating SecTokens. Accepts JKS (with
<file>?alias=<alias>
notation) and PKCS8 encoded DER key files. - DevTokenSignerKeyPassphrase: The passphrase for the signer key. Use
file://<file>
,pipe://<passphrase-getter>
orsecret://<obfuscated-passphrase>
to hide the password from the configuration. - DevPassword: The password the users must provide to use DevMode.
Once DevMode is enabled, an incoming request is processed as follows:
- If no BasicAuth Header is provided, request entry of a password from user with a 401 Basic Auth challenge.
- If a BasicAuth Header has provided a user name and a password, the password is compared to the configured DevPassword.
- If the password is correct, Ninja searches for specific files in the configured DevDir. Ninja searches for the following two files in the configured DevDir:
devmode_<userId>.properties
: contains the properties of the userdevmode_<userId>_generated_sectoken.xml
: contains the last generated SecToken- The user name provided in the BasicAuth header is used to find the properties file. The same user name is checked against the userId in the SecToken generated by DevMode. Use the UserGetter mechanism if the user name in the application should be different from the userId that is be set by Nevis
- If no user properties file was found, Ninja generates a default SecToken for the user, creating the above files in the process.
- If the user properties were found, but no SecToken file or the SecToken file is older than the user properties file, then Ninja regenerates the SecToken according to the user properties, rewriting the SecToken file in the process.
- If the user properties are older than the SecToken file and the SecToken loaded from the file is still valid, then Ninja (re-)uses that SecToken.
- Once a SecToken has been loaded or generated, normal processing is resumed. The token is validated and consumed as usual.
The above procedure allows dynamically adding users simply by logging in initially. Then, the new user's property files can be modified to differentiate them. The default user properties are:
_signAlg=SHA1withRSA
_format=CSSO-1.0
_age=10000
_ttl=62208000
esauthid=Ninja-DevMode
entryid=Ninja-DevMode
domain=NINJA_DEVMODE
userid=<userid>
loginId=<userid>
roles=user
sessid=<timestamp>
authLevel=auth.weak
Properties beginning with an underscore _
modify the metadata fields of the SecToken. The known fields are:
_signAlg
: algorithm of signature_format
: SecToken format_age
: age of SecToken when generated, in milliseconds_ttl
: time-to-live of SecToken, in seconds
All other properties is passed as attributes to the SecToken format builder. Note that depending on the SecToken format, some attributes may be handled as special cases. Removing any of the default attributes is therefore not advised.
Common Configuration Properties
NevisSignerCertificate
The certificate(s) files to be used for verification of the SecToken signature. It is recommended to specify using an absolute path to the file (instead of assuming a current working directory, which is the default).
The file type is determined by the file name extension. The following list shows the supported file formats and extensions.
- .pem: Base-64 encoded x.509 certificate with PEM headers. The certificate file must contain a single certificate only. The certificate file must not contain any comments.
- .der: Single DER encoded x.509 certificate
- .jks: Java Key Store, Ninja uses all certificates in this store to verify the Nevis SecToken, so all certificates in this store must be trusted for this purpose.
Syntax: <filename>,<filename>,...
default: /var/opt/neviskeybox/default/nevis/truststore.jks
RoleGetters
Specifies the Ninja RoleGetter to use to find the roles for the user. Defaults to use the “StaticRoleGetter” with roles=”member”. Alternatively, configure TokenRoleGetter to set the roles found in the SecToken.
A custom RoleGetter can be implemented, see the chapter "Custom RoleGetters" for details.
Syntax: <classname>[“(“ <name>=”<value>” { “,” <name>=”<value>” } “)”]
Default: StaticRoleGetter(roles="member")
This default is also used if “roles” is not specified or empty.
UserGetter
Specifies the Ninja UserGetter.
Syntax: <classname>[“(“ <name>=”<value>” { “,” <name>=”<value>” } “)”]
Default: TokenUserGetter
TokenTolerance
The Nevis SecToken, generated by esAuth has limited validity time. This parameter specifies a tolerance used when verifying this validity time. This value should be only a fraction of the actual token lifetime.
Syntax: <time-out>
Default: 600 (= 10 minutes)
TokenEncoding
When set, this property causes Ninja to correct the encoding of incoming SecTokens and SAML assertions.
This only applies for JAAS login modules, where ISO-8859-1 encoding is enforced by the container and consequently tokens with any other encoding will be garbled. Set this property to the actual encoding of the SecToken or SAML assertion if special characters cause validation of the token to fail.
Syntax: <encoding>
Default: none (no charset-correction is performed)
TokenSourceEncoding
Defines whether the security token is base64 encoded. Used for XML tokens containing newlines.
Syntax: true | false
Default: false
Note that for BasicAuth transport (which is also Base64 encoded), this option must not be set as the Base64-decoding is already implied by the transport protocol.
TokenIsBase64Encoded
Defines whether the security token is base64 encoded. Used for XML tokens containing newlines.
Syntax: true | false
Default: false
Note that for BasicAuth transport (which is also Base64 encoded), this option must not be set as the Base64-decoding is already implied by the transport protocol.
LogDebug
NOTE: This configuration variable is not available for all variants, see specific documentation to enable logging.
Controls logging of container plug-ins and Ninja Session Filter. If set to "true", debug-level tracing information is written to the server log file of the container.
Syntax: boolean
Default: false
DevMode
Activates the development mode of Ninja. See also: the chapter "Development Mode"
Syntax: boolean
Default: false
DevDir
Directory to store user properties and generated SecTokens in DevMode.
Syntax: <path>
Default: none (required if DevMode=true)
DevTokenSignerCert
Signer‘s certificate for SecTokens generated in DevMode. Must specify exactly one certificate.
DER, PEM or JKS files are accepted. Use the following syntax to specify the alias of the certificate entry in a JKS file:<jks-file>?alias=<alias>
Syntax: <path>
Default: none (required if DevMode=true)
DevTokenSignerKey
Signer‘s key for SecTokens generated in DevMode. Must specify exactly one RSA private key.
DER (pkcs8 encoded) or JKS files are accepted. Use the following syntax to specify the alias of the key entry in a JKS file:<jks-file>?alias=<alias>
Syntax: <path>
Default: none (required if DevMode=true)
DevTokenSignerKeyPassphrase
Passphrase of the signer private key for SecTokens generated in DevMode.
Syntax: <passphrase>
or file://<passphrase-file>
or pipe://<passphrase-getter>
or secret://<obfuscated-passphrase>
Default: none (required if DevMode=true)
DevPassword
Password for login using DevMode.
Syntax: <password>
Default: ninja
Accessing the Principal in Applications
The following sections describe how to fetch and work with the user principal from within application code. Many applications do not require information about the authenticated user apart from the user ID and possibly roles associated. However, some use cases necessitate accessing additional attributes of the user identity and/or forwarding the authentication token received.
The basic principle how to do this is always similar:
- Fetch the user principal. How this is done depends on the framework / API used by the application.
- Cast the user principal into a
NinjaTokenPrincipal
. This is the base class into Ninja place the token data and the received and validated token itself. - Use the
NinjaTokenPrincipal
instance to get attributes and to perform additional checks.
The following sections show short code snippets demonstrating this approach for different application technologies.
Using the Servlet API
Using the Servlet API, the principal can be retrieved directly and it can be casted to a NinjaTokenPrincipal
to fetch additional information:
NinjaTokenPrincipal principal = (NinjaTokenPrincipal) req.getUserPrincipal();
// get userID
String userId = principal.getName();
// get token as String, can be forwarded to further token-consuming services
String token = principal.getAuthTokenAsString();
// Attributes contained in security token
Map<String, String> userAttributes = principal.getAttributes();
// Roles contained in security token
String[] roles = principal.getRoles();
Using JSF 2.0
From JSF 2.0, the FacesContext
and the ExternalContext
can be retrieved to get the Principal
. Then it can be casted to a NinjaTokenPrincipal
to get access to further fields:
FacesContext fc = FacesContext.getCurrentInstance();
ExternalContext context = fc.getExternalContext();
NinjaTokenPrincipal principal = (NinjaTokenPrincipal) context.getUserPrincipal();
// get userID
String userId = principal.getName();
// get token as String, can be forwarded to further token-consuming services
String token = principal.getAuthTokenAsString();
// Attributes contained in security token
Map<String, String> attributes = principal.getAttributes();
// Roles contained in security token
String[] roles = principal.getRoles();
Developers are advised to pack the above code into a managed bean (for example 'AuthenticatedUser') that can be injected to controllers and views wherever permission checks are in order or token attributes need to be accessed.
Using CDI 2.0 in Java EE6/7 Containers
With CDI 2.0 in Java EE6/7 containers, the principal can be injected directly with the @Resource
annotation:
@Namedpublic class MyBean {
...
@Resource Principal principal;
...
Public String getMyTokenAttribute() {
NinjaTokenPrincipal principal = (NinjaTokenPrincipal) this.principal;
return principal.getAttributes().get(MY_ATTRIBUTE_NAME);
}
...
}
Using JAX-RS 2.0
With JAX-RS 2.0, published methods and resource classes can be protected with the @DenyAll
, @PermitAll
and @RolesAllowed
annotations. To fetch the Principal
, a SecurityContext
can be injected directly to a published method:
@GET
@RolesAllowed({"admin", "group1"})
public String doSomething(@Context SecurityContext context) {
// Get the principal instance
NinjaTokenPrincipal principal = (NinjaTokenPrincipal) context.getUserPrincipal();
// get userID
String userId = principal.getName();
// get token as String, can be forwarded to further token-consuming services
String token = principal.getAuthTokenAsString();
// Attributes contained in security token
Map<String, String> attributes = principal.getAttributes();
// Roles contained in security token
String[] roles = principal.getRoles();
...
}
Using JAX-WS 2.0
In a JAX-WS SOAP service, the principal is retrieved from the WebServiceContext
which in turn can be injected with @Resource
:
@WebService
public class TheWebService {
@Resource
private WebServiceContext context;
...
private NinjaTokenPrincipal getPrincipal() {
return (NinjaTokenPrincipal) context.getUserPrincipal();
}
}
Configuration - Ninja Authentication Filter
Functionality
The NinjaAuthenticationFilter is a Java ServletFilter that can enforce authentication or optional authentication, depending on its configuration. It provides the following functionality (for details see the chapter: Ninja Features):
- Secure Token Verification: The filter validates incoming SecTokens to establish an identity.
- SAML Assertion Verification: The filter can validate SAML Assertions to establish an identity in the same way as SecTokens.
- Servlet Authentication: The filter establishes a container identity according to the Java Servlet Specification (by injecting a UserPrincipal into the request instance).
- JAAS Authentication: The filter can, if configured, establish a JAAS authentication (using the
Subject.doAs()
method). - Container Session Invalidation: The filter can be configured to invalidate sessions upon receiving requests on a particular URL path. It can respond to that by redirecting or by dispatching to an internal resource.
- Multiple Signer Certificates: The filter can verify token signature trust using multiple configured certificates.
- Optional Authentication: The filter can be configured to forward requests with unauthenticated sessions to the application, even if no token is provided.
- Development Mode: This filter supports DevMode (see the chapter: Development Mode)
- Variable Token Source Header: The filter can extract tokens from arbitrary request headers and can decode BasicAuth transport in the same way as Ninja JAAS login modules. In addition, it can decode Bearer-type Authorization headers as defined by the OAuth Bearer Token standard.
Authentication Filter Configuration
The NinjaAuthenticationFilter supports all common configuration properties. In addition the following options can be configured:
Header
Sets the HTTP header in which the filter expects the authentication token. If this is set to ‘Authorization’, then a Basic Authentication scheme is assumed and decoded.
Syntax: <header>
Default: Authorization
Realm
If Header=Authorization is configured and the filter sends a WWW-Authenticate challenge, this setting defines the realm for which authentication is requested.
Syntax: <Realm>
Default: Nevis
AuthenticationOptional
Enables optional authentication that lets requests without tokens of unauthentic sessions pass. Used for scenarios where both authentic and anonymous access shall be allowed. Note that requests of unauthentic sessions with invalid tokens will always be blocked.
Syntax: true | false
Default: false
CachePrincipal
If set to true, the Principal will be stored in the session (i.e. it will be cached). If set to false, the SecToken will be checked on each request. Caching the Principal provides a potential performance optimization, but not caching the Principal is considered the more secure approach.
Syntax: true | false
Default: false
LogoutURI
URI path (relative to server host and application context) on that logout (session termination) should be initiated. Use this option for applications without dedicated logout facilities.
Syntax: <URL-path>
Default none
OnLogout
Instruction for how to handle logout if an authentic request was detected accessing the LogoutURI. If OnLogout is not configured, the filter will respond with the simple text/plain-encoded response “session terminated”.
Syntax:
- redirect:
<URL>
(respond with a redirect to an fully-qualified or relative URL) - dispatch:
<URL-path>
(respond by dispatching request to a resource before invalidating the session)
Default: none
OnLogin
Instruction for how to handle requests of unauthentic sessions not carrying any tokens. If not set, the filter will either let the request pass (if AuthenticationOptional is enabled), with a BasicAuth challenge (if Header=Authorization) or respond with a 401 “Unauthorized”.
Syntax:
- redirect:
<URL>
(respond with a redirect to an fully qualified or relative URL) - dispatch:
<URL-path>
(respond by dispatching request to a resource)
Default: none
JAASAuthentication
Enables propagation of user identity with JAAS means (Subject.doAs()).
Syntax: true | false
Default: false
Integration
Configuring applications to use Nevis Authentication Filter
To activate the NinjaAuthenticationFilter
, define the filter in the application web.xml
as follows:
<filter>
<filter-name>NinjaAuthenticationFilter</filter-name>
<filter-class>ch.nevis.ninja.filter.NinjaAuthenticationFilter</filter-class>
<init-param>
<param-name>NevisSignerCertificate</param-name>
<param-value>.../path/to/truststore.jks</param-value>
</init-param>
<init-param>
<param-name>LogoutURI</param-name>
<param-value>/sessions/logout</param-value>
</init-param>
<init-param>
<param-name>OnLogout</param-name>
<param-value>redirect:/portal/logout-confirmation.html</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>NinjaAuthenticationFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
The sample configuration defines /sessions/logout
as the logout URI. The logout URI is prefixed with the application context root, and you can configure it as LogoutURI in nevisProxy to destroy the HttpSession when the global SSO session is terminated. For further information, see nevisProxy Reference Guide on LogoutURI.
The code for NinjaAuthenticationFilter is distributed both in the ninja-filter
and in the ninja-uber
packages. Distribute the JAR therein in the application web archive, or add it to the container class path. When using ninja-uber
, no further JARs are needed. However, in case of ninja-filter
package, the ninja-commons
and all other dependencies are to be provided also.
Integration with nevisProxy
To integrate with NinjaAuthenticationFilter, BasicAuth identity propagation needs to be configured in nevisProxy. Ninja expects the secure token as the password, while the user name must match the userid in the token.
Add the following filter to the web.xml of your nevisProxy instance and map it to the path of your application:
<filter>
<filter-name>DelegationFilter</filter-name>
<filter-class>
ch::nevis::isiweb4::filter::delegation::DelegationFilter
</filter-class>
<init-param>
<param-name>DelegateBasicAuth</param-name>
<param-value>
AUTH:user.auth.UserId AUTH:user.auth.SecToken
</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>DelegationFilter</filter-name>
<url-pattern>/myapp/*</url-pattern>
</filter-mapping>
Further Integration Use Cases
The NinjaAuthenticationFilter can be used in several use cases. The following sections describe some of those scenarios and how to deploy NinjaAuthenticationFilter to enable them.
Multiple Authentication Mechanisms
In this scenario, an application needs to allow two different authentication methods for the application resource URLs. Optional X509 client certificate authentication shall be attempted first and Ninja shall be used to perform token-based authentication as a fallback. We assume that there is a filter named CertLoginFilter that performs a login if the request is associated with a trusted and valid transport-layer client certificate and that it forwards the request without action if no such certificate is present.
The NinjaAuthenticationFilter is configured to perform JAAS authentication:
<filter>
<filter-name>NinjaAuthenticationFilter</filter-name>
<filter-class>ch.nevis.ninja.filter.NinjaAuthenticationFilter</filter-class>
<init-param>
<param-name>NevisSignerCertificate </param-name>
<param-value>...../cert.der</param-value>
</init-param>
<init-param>
<param-name>RoleGetters</param-name>
<param-value>TokenRoleGetter</param-value>
</init-param>
<init-param>
<param-name>JAASAuthentication</param-name>
<param-value>true</param-value>
</init-param>
</filter>
… and the filter is mapped on all paths of the application after the CertLoginFilter:
<filter-mapping>
<filter-name>CertLoginFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>NinjaAuthenticationFilterSimple</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
For this simple example, this is enough to implement support for two authentication schemes.
Mixed Anonymous/Authentic Application with Dynamic Enforcement
While many applications are designed to be either public or protected from the start, there are also situations where applications need to allow both anonymous and authentic (non-anonymous) access. Often, the anonymous parts of the application should also be informed about the user identity if the user is known (i.e. authenticated). So each sub-location of the application falls into one of the following three categories:
- Anonymous sub-locations, where anonymous access is allowed and where requests of authentication do not need to be injected with the context of the user identity. Often, static resources or very simple query services fall under this classification.
- Semi-anonymous sub-locations, where anonymous access is allowed, but where the access should be in the context of the user identity if the session has been authenticated. There are no declarative rules based on which a login can be enforced because only the application knows whether the request would trigger a switch from an anonymous to an authenticated session. Most pages of shop-like applications are in this category because browsing in anonymous access should be allowed, but once the user is known, additional information should be displayed.
- Protectedsub-locations, where anonymous access is never allowed and where anonymous access attempts always trigger a login-process. In a shop-like scenario, the user-profile and the checkout functionalities are examples for this behavior.
The above classification applies for sub-locations which are recognizable by their request paths. If that is not the case, then the configuration must fall back to the most secure mode that permits the required functionality and the application must dynamically trigger logins where necessary. For example this could be done by the application redirecting to a protected login page.
To enable mixed authentication on an application, nevisProxy needs to be instructed to pass through unauthentic requests. This means that the IdentityCreationFilter must not be mapped on that sub-location.
<filter>
<filter-name>NevisIdmAuthenticationFilter</filter-name>
<filter-class>ch::nevis::isiweb4::filter::auth::IdentityCreationFilter</filter-class>
...
<init-param>
<param-name>StateKey</param-name>
<param-value>MyStateKey</param-value>
</init-param>
...
</filter>
<filter>
<filter-name>OptionalNinjaDelegationFilter</filter-name>
<filter-class>ch::nevis::isiweb4::filter::delegation::DelegationFilter</filter-class>
<init-param>
<param-name>DelegateBasicAuth</param-name>
<param-value>
AUTH:user.auth.UserId AUTH:user.auth.SecToken
</param-value>
</init-param>
<init-param>
<param-name>DelegateSource</param-name>
<param-value>MyStateKey</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>AuthenticationFilter</filter-name>
<url-pattern>/myapp/protected/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>OptionalNinjaDelegationFilter </filter-name>
<url-pattern>/myapp/*</url-pattern>
</filter-mapping>
The above configuration sets up a protected sub-location on /myapp/protected
but allows optional authentication on all other sub-locations of /myapp/*
. This is done by not mapping the IdentityCreationFilter in any non-protected sub-locations and by configuring the DelegationFilter to fetch data with MyStateKey even if a request did not pass through an IdentityCreationFilter. The DelegationFilter will not block requests, for which is cannot find an authentication state, so that such requests are passed non to the application without BasicAuth header and with token.
Once the nevisProxy configuration is ready, the application can define anonymous, semi-anonymous and protected sub-locations by configuring NinjaAuthenticationFilters accordingly. The configuration example below assumes that the application has a context root of myapp so that the nevisProxy configuration shown above is matched:
<filter>
<filter-name>ProtectedLocationFilter</filter-name>
<filter-class>ch.nevis.ninja.filter.NinjaAuthenticationFilter</filter-class>
<init-param>
<param-name>NevisSignerCertificate </param-name>
<param-value>...../cert.der</param-value>
</init-param>
<init-param>
<param-name>AuthenticationOptional</param-name>
<param-value>false</param-value>
</init-param>
</filter>
<filter>
<filter-name>SemiAnonymousLocationFilter</filter-name>
<filter-class>ch.nevis.ninja.filter.NinjaAuthenticationFilter</filter-class>
<init-param>
<param-name>NevisSignerCertificate </param-name>
<param-value>...../cert.der</param-value>
</init-param>
<init-param>
<param-name>AuthenticationOptional</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>ProtectedLocationFilter</filter-name>
<url-pattern>/protected/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>SemiAnonymousLocationFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
The above enforces a protected sub-location on protected/ and allows for both anonymous and authenticated access to all other locations. To enforce the authentication of a previously anonymous session, the application might redirect to a sub-location such as /protected/login at which point nevisProxy will trigger an authentication. Or if the user has performed an authentication somewhere else within the SSO-realm already, then the user's identity will become known to all sub-locations of myapp due to the configuration of DelegationFilter and the use of the SemiAnonymousLocationFilter.