Basic Concepts
This chapter introduces you to the main concepts of nevisAuth, starting with an overview of the nevisAuth architecture, descending into nevisAuth's core - the AuthEngine - and giving insight into the building blocks of an authentication flow - the AuthStates. At last, we present how and when a response is generated.
The concepts discussed in this chapter should give you the basic knowledge required for developing your own custom authentication plugins.
nevisAuth Architecture
nevisAuth is a middleware product that provides authentication and authorization services. It supports many authentication back ends and can be configured flexibly to guide the end user through one or more authentication steps.
nevisAuth's clients rely on nevisAuth to authenticate end users. To support different clients, nevisAuth provides different interfaces on the server side. The most common nevisAuth client is nevisProxy, which interacts with nevisAuth through the nevisProxy interface. Other interfaces are, for example, the WS-Trust security token service, which supports WS-Trust clients, the RADIUS service, which provides authentication for RADIUS clients, and the REST services, which can be accessed with any HTTP-based clients.
The following diagram gives an overview of the nevisAuth interfaces and the Nevis products that are typically involved in the authentication process within the Nevis Security Suite.
Upon successful authentication of the end user, nevisAuth issues a proof of authentication to the client. Depending on the interface through which the request is received, the proof of authentication can take on different forms.
Since most setups with nevisAuth use nevisProxy as a client, the next chapter will familiarize you with specifics about the nevisProxy interface and the proof of authentication issued to nevisProxy.
nevisProxy as a Client to nevisAuth
In most cases, nevisAuth is accessed via nevisProxy through the identity creation filter or the security role filter (see the nevisProxy reference guide for details). In this setup, nevisProxy acts as an interceptor for requests to the back-end application(s).
Before a request is forwarded to the desired location, the identity creation filter checks whether a user requires authentication and the security role filter verifies that the authorization is sufficient.
In case authentication or authorization is required, nevisProxy wraps the original HTTP request, which it received from the user agent, into a SOAP request which will be sent to nevisAuth.
GET and POST parameters are forwarded as so called inArgs
(incoming arguments).
Cookies, language settings, IP address of the user agent and other HTTP headers are forwarded
as inCtx
(incoming context).
The request is received by nevisAuth's nevisProxy interface and processed by the AuthEngine. After the request is processed, a response is prepared and sent back to nevisProxy. At that point, nevisProxy decides whether a GUI must be rendered to request more information from the user (e.g., user credentials), whether the authentication process resulted in an error and the request to the protected application must be prohibited, or whether the authentication or logout process was successful.
In case the authentication process was successful, nevisProxy expects a proof of authentication from nevisAuth in the form of a security token - a SecToken. nevisProxy verifies the content and signature of the SecToken and upgrades the user agent session to an authenticated session. If the verification is successful and the necessary authorization is granted, nevisProxy will allow the initial request to be forwarded to the protected application.
By passing the SecToken to back-end applications, these, in turn, are able to verify the authentication and retrieve the forwarded user identity. The user agent does not receive the SecToken, but instead associates the session with nevisProxy through an HTTP cookie.
The following diagram shows the identity passing between the components:
The SecToken is a structured and cryptographically signed XML document that holds user data such as the name and the roles. It enables the receiver to verify that the signed data has not been changed and was generated by a trusted entity. The extensible design of the token also allows to include additional, site-specific attributes that are available during the (site-specific) authentication process.
The security token also holds an expiration time, after which access to the session will no longer be granted and the session will eventually terminate. The proprietary data structure of the SecToken encodes the information configured in the nevisAuth configuration. This typically includes information such as the session ID, user ID, login ID, level of authentication, roles and a timestamp when the signature was computed.
The signature, signature timestamp and session ID are generated by the AuthEngine when the authentication is successful. User ID, login ID and roles must be set in the AuthStates during the authentication flow. The level of authentication is configured in the nevisAuth configuration and set by the AuthEngine if a certain authentication process is passed.
Service Operations
nevisAuth supports four different authentication and authorization operations: authenticate
, stepup
, unlock
and logout
. The following list summarizes their functionality:
authenticate
This operation is called by the client in case the client does not know who the user is yet. The purpose of this operation is to identify and authenticate the user.
stepup
This operation is called by the client in case the client already knows who the user is, but the user requires additional roles to access the desired resources.
unlock
This operation is called by the client in case it requires re-authentication of the user by nevisAuth.
logout
This operation is called by the client upon a logout request initiated by a user.
The AuthEngine processing might differ depending on which operation was triggered by the client. For example, at the end of an authenticate operation, the initial user session is upgraded to an authenticated session, while at the end of a logout operation, the user's session is destroyed.
AuthEngine
The AuthEngine is responsible for processing authentication and authorization requests. The authentication process starts as soon as a request is received by nevisAuth through one of the nevisAuth interfaces. The AuthEngine transforms the request into a context object and passes it through a set of authentication process steps - the AuthStates. The AuthEngine also identifies whether more information from the user is required, which usually results in the generation of an HTML form. Eventually, the AuthEngine constructs a final response for nevisProxy, which either indicates an error or a successful authentication, session upgrade or logout.
The AuthEngine creates, updates and removes sessions of users. The sessions hold data that are required during the authentication flow, or, beyond that, during a later retrieval of roles or other user attributes. In nevisAuth, sessions are either initial or authenticated. Initial sessions are short-living. They hold data of users who did not yet finish the authentication. Whenever a user finishes an authentication flow in a successful final state, the initial session is upgraded to an authenticated session. On the other hand, if an authentication flow is finished in an error state, the initial session is discarded.
In the following chapters, we introduce the concept of the AuthStates, discuss when and which responses are generated by the AuthEngine and when and how HTML forms are generated.
AuthStates
AuthStates
are the building blocks of the authentication process.
Each AuthState has one particular responsibility, e.g. to authenticate a user against a certain authentication back end, to manipulate a request, to cache information, etc. They can be chained together in a directed graph to allow complex configurations for authentication processing.
AuthStates can be separated into two groups: those that are actually authenticating a user and those that support the processing of the request. In a valid nevisAuth setup there must be at least one authenticating AuthState, which is responsible for setting the user id, after it has been verified, into the response.
Data Manipulation in and out of Context
An AuthState has access to the authentication request's context and manipulates it during processing. The context consists of the inCtx
and inArgs
, received from the client, the session
and temporary storage, which can be used to cache information for later usage, and response parameters, which are eventually integrated into the response to the client (outArgs
).
Variables in nevisAuth can be stored and retrieved from different sources. Depending on the source, the lifetime and accessibility of the data varies. While data is usually stored in a user request's context, AuthStates can also store data outside a user request's context (readable by anybody, e.g., in the background without having a request object available). For that, the out-of-context data service can be used.
The following list shows valid nevisAuth variable sources.
inargs
The GET and POST parameters forwarded by the client.
inctx
The client's login context variables, such as HTTP headers, required roles, requested language, client address, etc.
sess
,session
The session variables. They live as long as the user's session.
notes
Temporary variables. They live only until the next response.
outargs
Variables forwarded to the nevisAuth client (e.g., nevisProxy) for propagation to the back-end application or forward-propagation to the user agent.
request
The request attributes that the AuthEngine supplies. Includes data such as required roles, as well as the language that was chosen for this user agent.
response
The response attributes that previous AuthStates supply. Contains authentication and authorization data, such as user names and roles.
litdict
All variables stored in the literal dictionary files. The value is retrieved according to the currently set language (read-only).
cookie
Pseudo-source for accessing cookies sent by the user agent (read-only).
header
Pseudo-source for reading HTTP headers sent by the user agent (read-only).
oocd
Out-of-context data store. Values read from or written into this source are outside of the user's context, which means that anybody can access them.
Request Processing
In mathematical terms, the request processing model represents a finite state machine of AuthStates. In a correctly set-up instance of nevisAuth, there is always a non-empty set of AuthStates, which is available to process requests. There is always an entry state, at which the processing starts, and one or multiple states at which the processing ends. Each state produces a result.
For a practical example, the following setup shows what a login by username / password with two-factor authentication could look like:
In this example, the request processing would start at the LDAP AuthState, which is marked as the entry state of the AuthState flow. If the user name and password were successfully checked against the LDAP back end, the flow continues at the TAN AuthState. Otherwise the GUI form is rendered (again) and may show an error message indicating that the input was either missing or incorrect.
Continuing at the TAN AuthState, nevisAuth initiates the transmission of an SMS to the user's mobile phone, containing a transaction authentication number (TAN). Another GUI form is rendered, prompting the user to provide the TAN to prove that he is in control of the claimed mobile phone number.
If the provided TAN equals the sent TAN, the authentication process is successful and the final AuthState Done is reached. Otherwise, if the provided TAN was not correct, the GUI form is rendered again and the processing remains in the TAN state. In case a system error occurs, e.g., if the SMS gateway is unavailable, the final AuthState Error is reached. In this case the outcome of the processing can not be influenced by the user and therefore the authentication processing must be aborted.
Entry AuthState and Transition Configuration
Once a nevisAuth interface receives a request, the AuthEngine must decide with which AuthState it should start processing. This decision is made based on the configuration and whether a session with the user already exists or not.
If a session already exists, and the previous request did not result in a completed authentication process, the request will be passed to the AuthState that previously failed to continue processing. One reason for previously failing might be that user input was required.
If no session exists, the request processing will start at the configured entry AuthState. The AuthEngine therefore checks whether the configuration contains an entry for the called service operation. As you can see in the configuration below, the service operation is specified by the property 'method' of the 'Entry' XML element. This element is used to identify the first AuthState.
In this example, the request is passed to the AuthState MyCustomState
if no session exists and 'authenticate' is called, and to the AuthState AnotherState
, if no session exists and 'unlock' is called.
If a service operation is called, but not configured, the AuthEngine falls back to the entry AuthState that is configured with the 'authenticate' method.
<Domain name="SSO_TEST" default="true"
reauthInterval="0"
inactiveInterval="1800">
<Entry method="authenticate" state="MyCustomState"/>
<Entry method="unlock" state="AnotherState"/>
</Domain>
To navigate from one AuthState to the next during a request, AuthState transitions are made. Therefore, each AuthState produces a result
, based on the incoming data and its internal logic.
The result of the AuthState is used (among other things) by the AuthEngine to decide which AuthState should process the request next.
The result itself is an arbitrary string value programmatically set by the processing AuthState. The AuthEngine will look in the current AuthState's configuration to find the next AuthState to process the request. The next AuthState is referenced through a name.
A set of triggering conditions helps the AuthEngine decide which AuthState comes next in the process. These triggering conditions are defined in the ResultCond element. Such a configuration might look like this:
<AuthState name="MyCustomState" class="ch.my.custom.MyAuthState" final="false">
<ResultCond name="ok" next="AuthDone"/>
<ResultCond name="failed" next="AuthError"/>
...
</AuthState>
Let's assume our AuthState sets the result 'ok'. This prompts the AuthEngine to pass the request to the AuthState with the name AuthDone after completing processal with the current AuthState. On the other hand, if the AuthState sets the result 'failed', AuthError will be selected as the next AuthState.
There is one implicit transition that does not require (but allows) configuration: default. If the result default is set by an AuthState, but no transition is configured, the AuthEngine stops processing to prepare a response, possibly indicating that a GUI must be rendered.
AuthState Lifecycle
Every time an AuthState is configured using a <AuthState>
element, nevisAuth creates exactly one instance of that AuthState.
This is done during start-up by instantiating and initializing the AuthState. I.e., AuthStates are multithreaded and nevisAuth shares single instances of AuthStates for multiple requests. In other words, nevisAuth does not create a new instance of the AuthState class for each request. Instead, it reuses the existing ones.
Response Generation
In the previous chapters we learned that a request is received through one of nevisAuth's interfaces, and passed through the configured AuthStates. A request is processed as long as possible, until it gets stuck in a situation in which it cannot be processed any more. This happens whenever a request hits an AuthState that is configured to show the GUI before processing, or if the AuthState transitions to itself. This occurs, for example, if the user enters a wrong password. At that point, a response is generated by the AuthEngine and handed to the respective nevisAuth interface.
There are three types of responses:
AUTH_ERROR
Indicates that the AuthEngine or AuthState has detected an error and no user input can circumvent this error.
AUTH_DONE
Indicates that the AuthEngine has finished the authentication successfully and a SecToken was issued.
AUTH_CONTINUE
Indicates that nevisAuth requires more input from the user to be able to continue processing.
Depending on the nevisAuth interface the response is handed to, it is mapped to a response according to that interface's protocol.
GUI Generation
As a general rule, a GUI is always generated if an AuthState responds with an AUTH_CONTINUE
or AUTH_ERROR
response.
nevisAuth does not generate GUIs itself. Instead, it builds a GUI descriptor which is sent back to the client.
If the client is nevisProxy, it in turn forwards it to nevisLogRend, the login renderer application.
The login renderer then responds with an HTML document that implements the GUI described in the GUI descriptor. This rendered GUI will then be forwarded by nevisProxy to the end-user's user agent.
The GUI descriptor is configured in the AuthState configuration as part of the response element of an AuthState. It guides the login renderer to create a certain GUI by describing the content, but not the layout, of the GUI.
The following code snippet shows an example of the GUI descriptor as part of an AuthState configuration.
Refer to Response generation for more detailed information.
<Gui name="LoginPage" label="UIDPWDialog">
<GuiElem name="isiwebuserid" type="text" label="Username" value="" />
<GuiElem name="isiwebpasswd" type="pw-text" label="Password" value="" />
<GuiElem name="submit" type="button" label="Login" value="Login" />
</Gui>
Technology Stack
nevisAuth is a web application written in Java. We are using the following technologies:
Technology | Version | Description |
---|---|---|
Linux | Operating System | |
Java | 1.8 | Compile and runtime environment. As a runtime environment, newer Java versions can (and are) used, because the JVMs are typically backwards compatible. |
Java Servlet API | 3.1 | The Java Servlet API specifies how HTTP requests are received and how HTTP responses are sent back to the client. nevisAuth implements filters and servlets to handle such requests. |
Jax-WS | from JRE 1.8 | The web service stack used to retrieve SOAP web service requests and respond with SOAP responses. |
Jax-RS | 2.6 | The web service stack used to accept and respond to REST web service calls. |