Nevis Component Configuration Examples
This appendix provides the configuration snippets for the Nevis components required to cover the described use cases.
All use cases require the configuration of nevisProxy, nevisAuth, nevisFIDO and nevisIDM. Read the snippets carefully to ensure a correct configuration.
nevisProxy
Use Case Independent Configuration
In all the following scenarios, nevisProxy requires connectors to the nevisAuth and nevisFIDO components. The global configuration of nevisProxy therefore requires the presence of
- an
Esauth4ConnectorServlet
to connect to nevisAuth, and - an
HttpsConnectorServlet
to connect to nevisFIDO.
See Default HTTP API Endpoints]" for details.
In addition to the Authentication Request endpoint, the FIDO client must access various facets of an application. The facets are exposed by nevisFIDO. For details, see the Facets Service. For more information on facets, see FIDO AppID and Facet Specification]".
The following snippets show the connector configuration for locally running nevisAuth and nevisFIDO instances.
<!-- Connector servlet to connect to the local nevisAuth authentication service -->
<servlet>
<servlet-name>NevisAuthConnector</servlet-name>
<servlet-class>ch::nevis::isiweb4::servlet::connector::soap::esauth4::Esauth4ConnectorServlet</servlet-class>
<init-param>
<param-name>TargetURI</param-name>
<param-value>/nevisauth/services/AuthenticationService</param-value>
</init-param>
<init-param>
<param-name>Encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>Transport.InetAddress</param-name>
<param-value>localhost:8991</param-value>
</init-param>
<init-param>
<param-name>Transport.ConnectTimeout</param-name>
<param-value>45000</param-value>
<description>
msec, nevisAuth startup in tomcat5 takes some time and
listener is open too early
</description>
</init-param>
<init-param>
<param-name>Transport.RequestTimeout</param-name>
<param-value>90000</param-value>
<description>
msec, 1/3 of this timeout is used to poll nevisAuth for
terminated session.
</description>
</init-param>
<init-param>
<param-name>Transport.SSLClientCertificateFile</param-name>
<param-value>/var/opt/keybox/default/node_keystore.pem</param-value>
</init-param>
<init-param>
<param-name>Transport.SSLCACertificateFile</param-name>
<param-value>/var/opt/keybox/default/truststore.pem</param-value>
</init-param>
<init-param>
<param-name>Transport.SSLCheckPeerHostname</param-name>
<param-value>false</param-value>
</init-param>
</servlet>
<!-- Connector servlet to connect to the local nevisFIDO instance -->
<servlet>
<servlet-name>FidoHttpApiConnector</servlet-name>
<servlet-class>ch::nevis::isiweb4::servlet::connector::http::HttpsConnectorServlet</servlet-class>
<init-param>
<param-name>InetAddress</param-name>
<param-value>localhost:8443</param-value>
</init-param>
<init-param>
<param-name>AutoRewrite</param-name>
<param-value>off</param-value>
</init-param>
<init-param>
<param-name>Transport.SSLCACertificateFile</param-name>
<param-value>/var/opt/certs/X509-nevisfido-server.cer</param-value>
</init-param>
</servlet>
As the out-of-band authentication scenario requires additional client-side JavaScript to support the login flow, the configuration examples use a nevisLogrend instance instead of nevisAuth's built-in login renderer:
For more information on JavaScript and out-of-band scenarios, see Integrating the JavaScript Client Application in Out-of-Band Login Flows.
<!-- nevisLogrend (HTML Form) -->
<servlet>
<servlet-name>NevisLoginRenderer</servlet-name>
<servlet-class>ch::nevis::isiweb4::servlet::rendering::LoginRendererServlet</servlet-class>
<init-param>
<param-name>RenderingProvider</param-name>
<param-value>
remote:NevisLogrendConnector:/nevislogrend/index.jsp?logrendresourcepath=/nevislogrend/login/resources
</param-value>
</init-param>
<init-param>
<param-name>UseFallback</param-name>
<param-value>false</param-value>
</init-param>
</servlet>
<servlet>
<servlet-name>NevisLogrendConnector</servlet-name>
<servlet-class>ch::nevis::isiweb4::servlet::connector::http::HttpConnectorServlet</servlet-class>
<init-param>
<param-name>InetAddress</param-name>
<param-value>localhost:8188</param-value>
</init-param>
</servlet>
In-Band Registration
In the in-band registration scenario, nevisProxy is responsible for protecting the endpoint of nevisFIDO's Registration Service. This is typically realized by configuring an IdentityCreationFilter
in nevisProxy and the associated connectors. If a non-authenticated HTTP client tries to access the protected resource (the Registration Service endpoint), nevisProxy will redirect the HTTP client to nevisAuth for authentication.
The following nevisProxy configuration snippets (in web.xml
) redirect non-authenticated access via https://<proxy host>/nevisfido/uaf/1.1/request/registration
to the locally installed nevisAuth instance. The nevisAuth instance is configured with port 8991
, and nevisFIDO with port 8443
. The name of the realm for the in-band registration operation is "PASSWORD_AUTHENTICATION"
.
The properties InterceptionRedirect
and StoreInterceptedRequest
must be set to "never"
and "false"
,
respectively. These settings are relevant for JSON clients, and must prevent the proxy from interfering with the requests. For details, see the nevisProxy Technical Documentation.
To provide an authentication endpoint for a mobile authentication client, nevisProxy exposes the /auth/pwd
path. A client can perform a password login via nevisAuth using this endpoint.
<!-- Authentication filter to protect the nevisFIDO Registration Service -->
<filter>
<filter-name>PasswordAuthenticationFilter</filter-name>
<filter-class>ch::nevis::isiweb4::filter::auth::IdentityCreationFilter</filter-class>
<init-param>
<param-name>AuthenticationServlet</param-name>
<param-value>NevisAuthConnector</param-value>
<description>The configured name of the authentication servlet</description>
</init-param>
<init-param>
<param-name>LoginRendererServlet</param-name>
<param-value>NevisLoginRenderer</param-value>
<description>The configured name of the login renderer servlet</description>
</init-param>
<init-param>
<param-name>Realm</param-name>
<param-value>PASSWORD_AUTHENTICATION</param-value>
<description>The realm of the authentication</description>
</init-param>
<init-param>
<param-name>InactiveInterval</param-name>
<param-value>7200</param-value>
<description>The maximum interval between two request associated to the same session
(if deleted or 0, value is taken from nevisAuth 'Domain' element)</description>
</init-param>
<init-param>
<param-name>EntryPointID</param-name>
<param-value>localhost</param-value>
<description>The entry point id (will be part of the sectoken)</description>
</init-param>
<init-param>
<param-name>InterceptionRedirect</param-name>
<param-value>never</param-value>
</init-param>
<init-param>
<param-name>StoreInterceptedRequest</param-name>
<param-value>false</param-value>
</init-param>
</filter>
<!-- URL Mapping for the nevisFIDO Registration Request Service, the filter mapping includes the SecToken and the servlet mapping associates the servlet with a path. -->
<filter-mapping>
<filter-name>PasswordAuthenticationFilter</filter-name>
<url-pattern>/nevisfido/uaf/1.1/request/registration/*</url-pattern>
</filter-mapping>
If an already authenticated user tries to access the auth/pwd
endpoint, the system returns a 404 error by default. You can define a LUA filter to avoid this. The LUA filter will return a 204 HTTP status code.
<!-- This filter is invoked *only* if the user is already authenticated: if the user is not
authenticated, nevisAuth (through PasswordAuthenticationFilter or FidoUafAuthenticationFilter)
is invoked, and nevisAuth returns a direct response in those cases.
This filter just returns an HTTP response with status code 204. -->
<filter>
<filter-name>AlreadyAuthenticatedFilter</filter-name>
<filter-class>ch::nevis::isiweb4::filter::lua::LuaFilter</filter-class>
<init-param>
<param-name>Script.InputHeaderFunctionName</param-name>
<param-value>rewriteResponse</param-value>
</init-param>
<init-param>
<param-name>Script</param-name>
<param-value>
function rewriteResponse(request, response)
response:send(204)
end
</param-value>
</init-param>
</filter>
nevisProxy introduces an additional filter mapping to auth/pwd
. This creates a singular endpoint, against which client applications can perform (password) authentication. This allows clients to authenticate with legacy credentials,
which is required before starting the registration process.
<!-- The /auth/pwd filter provides and endpoint for doing "legacy" username/password authentication -->
<filter-mapping>
<filter-name>PasswordAuthenticationFilter</filter-name>
<url-pattern>/auth/pwd</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>AlreadyAuthenticatedFilter</filter-name>
<url-pattern>/auth/pwd</url-pattern>
</filter-mapping>
To forward all incoming requests to nevisFIDO, the respective connector is mapped to the nevisFIDO base path:
<!-- Servlet forwarding every incoming request to /nevisfido/* to nevisFIDO -->
<servlet-mapping>
<servlet-name>FidoHttpApiConnector</servlet-name>
<url-pattern>/nevisfido/*</url-pattern>
</servlet-mapping>
In the in-band registration use case, nevisAuth creates a SecToken once the client application is authenticated. The SecToken is used to guarantee that the authentication was successful and to transmit the username. The username must be transmitted to nevisFIDO when the client accesses the Registration Request endpoint. nevisFIDO will validate the
UserIdprovided in the BasicAuth
header against the user ID encoded in the SecToken. Therefore, the UserId
must be exposed as the first information in the BasicAuth
header.
Furthermore, the delegation filter must be mapped to the nevisFIDO base URL.
<!-- Delegation filter for transmitting the Sectoken. -->
<filter>
<filter-name>NinjaBasicAuthDelegationFilter</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>NinjaBasicAuthDelegationFilter</filter-name>
<url-pattern>/nevisfido/*</url-pattern>
</filter-mapping>
In the in-band registration use case, only nevisAuth needs access to the nevisFIDO endpoints. This allows you to block all other nevisFIDO endpoints (notably the Authentication Request endpoint) for external HTTP clients in this scenario.
In-Band Authentication
In the in-band authentication scenario, nevisProxy is responsible for protecting a relying party backend, for example an e-banking web server. This is typically realized by configuring an IdentityCreationFilter
in nevisProxy and the associated connectors. When a non-authenticated HTTP client tries to access the protected resource (such as the e-banking web server), nevisProxy will redirect the client to nevisAuth for authentication.
The following nevisProxy configuration snippets (in web.xml
) redirect non-authenticated access to URLs of type https://<proxy host>/ebanking/
to the locally installed nevisAuth instance. The nevisAuth instance is configured with port 8991.
The properties InterceptionRedirect
and StoreInterceptedRequest
must be set to "never"
and "false"
,
respectively. These settings are relevant for JSON clients, and must prevent the proxy from interfering with the requests. For details, see the nevisProxy Technical Documentation.
In this example, the name of the realm that performs the authentication is "FIDO_UAF_AUTHENTICATION"
.
<!-- Authentication filter for FIDO UAF Authentication -->
<filter>
<filter-name>FidoUafAuthenticationFilter</filter-name>
<filter-class>ch::nevis::isiweb4::filter::auth::IdentityCreationFilter</filter-class>
<init-param>
<param-name>AuthenticationServlet</param-name>
<param-value>NevisAuthConnector</param-value>
<description>The configured name of the authentication servlet</description>
</init-param>
<init-param>
<param-name>LoginRendererServlet</param-name>
<param-value>BuiltinLoginRenderer</param-value>
<description>The configured name of the login renderer servlet</description>
</init-param>
<init-param>
<param-name>Realm</param-name>
<param-value>FIDO_UAF_AUTHENTICATION</param-value>
<description>The realm of the authentication</description>
</init-param>
<init-param>
<param-name>InactiveInterval</param-name>
<param-value>7200</param-value>
<description>The maximum interval between two request associated to the same session
(if deleted or 0, value is taken from nevisAuth 'Domain' element)</description>
</init-param>
<init-param>
<param-name>InterceptionRedirect</param-name>
<param-value>never</param-value>
</init-param>
<init-param>
<param-name>StoreInterceptedRequest</param-name>
<param-value>false</param-value>
</init-param>
<init-param>
<param-name>EntryPointID</param-name>
<param-value>localhost</param-value>
<description>The entry point id (will be part of the sectoken)</description>
</init-param>
</filter>
<!-- URL Mapping for E-Banking application. -->
<filter-mapping>
<filter-name>FidoUafAuthenticationFilter</filter-name>
<url-pattern>/ebanking/*</url-pattern>
</filter-mapping>
Similar to the in-band registration use case, a singular endpoint is mapped to the mobile authentication filter. This allows client applications to authenticate with Nevis Mobile Authentication against a single endpoint. When an already authenticated client tries to access this endpoint, an HTTP 404 error is returned. To avoid this, the Lua filter AlreadyAuthenticatedFilter
is also mapped to the endpoint.
<filter-mapping>
<filter-name>FidoUafAuthenticationFilter</filter-name>
<url-pattern>/auth/fidouaf</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>AlreadyAuthenticatedFilter</filter-name>
<url-pattern>/auth/fidouaf</url-pattern>
</filter-mapping>
FIDO UAF Credential Deregistration
In the FIDO UAF credential deregistration use case, nevisProxy is responsible for protecting the endpoint of nevisFIDO's Deregistration Request Service. This is typically realized by configuring an IdentityCreationFilter
in nevisProxy and the associated connectors. When a non-authenticated HTTP client tries to access the protected resource (
the Deregistration Request Service endpoint), nevisProxy will redirect the HTTP client to nevisFIDO for authentication.
The following nevisProxy configuration snippets (in web.xml
) redirect non-authenticated access to URLs of type https://<proxy host>/nevisfido/uaf/1.1/request/deregistration
to the locally installed nevisAuth instance. The nevisAuth instance is configured with port 8991
, and nevisFIDO with port 8443
.
You can reuse the authentication filter as well as the servlet mapping from the in-band authentication use case. You only need to add another filter mapping to explicitly protect the Deregistration Request Service endpoint with FIDO authentication:
<!-- URL Mapping for the nevisFIDO Deregistration Request Service, the filter mapping includes the
SecToken and the servlet mapping associates the servlet with a path. -->
<filter-mapping>
<filter-name>FidoUafAuthenticationFilter</filter-name>
<url-pattern>/nevisfido/uaf/1.1/request/deregistration/*</url-pattern>
</filter-mapping>
Out-of-Band Registration
The required configuration for the out-of-band registration scenario is almost the same as for the in-band registration scenario. In both use cases, the Registration Service endpoint must only be accessible for authenticated users with the username present in the session context.
In the out-of-band registration use case, registration must be processed by a relying party backend application.
Therefore, the registration endpoint of this application must require authentication. In this example, the application endpoint orchestrating the registration is named /oob-demo/register
(see the next code block).
For additional information regarding the out-of-band registration code required on the relying party application side ( client side), see Out-of-Band Registration Client Code Examples.
<filter-mapping>
<filter-name>NevisIdmAuthenticationFilter</filter-name>
<url-pattern>/oob-demo/register</url-pattern>
</filter-mapping>
Out-of-Band Authentication
In the out-of-band authentication scenario, nevisLogrend is used to present the out-of-band login page to the user. This is because the out-of-band login process requires additional client-side JavaScript. For details, see Integrating the JavaScript Client Application in Out-of-Band Login Flows. You find the nevisLogRend configuration for nevisProxy in the use case independent configuration section further above.
The nevisProxy configuration for out-of-band authentication scenarios is similar to the in-band authentication scenario.
In both cases, an IdentityCreationFilter
forwards unauthenticated requests to nevisAuth. However, the out-of-band authentication scenario requires a new, separate realm. In the code sample below, this realm is called "FIDO_UAF_DEMO"
.
The protected application can be accessed via the endpoint /nevisdemo
:
<filter>
<filter-name>FidoUafDemoFilter</filter-name>
<filter-class>ch::nevis::isiweb4::filter::auth::IdentityCreationFilter</filter-class>
...
<init-param>
<param-name>Realm</param-name>
<param-value>FIDO_UAF_DEMO</param-value>
<description>The realm of the authentication</description>
</init-param>
<init-param>
<param-name>StoreInterceptedRequest</param-name>
<param-value>false</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>FidoUafDemoFilter</filter-name>
<url-pattern>/nevisdemo/*</url-pattern>
</filter-mapping>
Dispatch Target Management
To properly use out-of-band authentication, also configure dispatch target management endpoints in nevisProxy. Client applications need these endpoints to maintain their dispatch target information.
The Modify Dispatch Target endpoint is special compared to any other endpoint: It does not require authentication provided by nevisAuth, but uses JWS to validate the endpoint access. The Modify Dispatch Target endpoint shares the same URL as the Delete Dispatch Target endpoint, but differs in the authentication. Because of this, you need a special
FilterMappingFilter
.
This special FilterMappingFilter
serves a singular purpose: It invokes the FidoUafAuthenticationFilter
if the HTTP method used is not PATCH
. This in turn requires FIDO UAF authentication. If the HTTP method is PATCH
, the UAF authentication filter will not be invoked. And because there is no other filter in the chain, no prior authentication is required to execute the HTTP PATCH
request.
!-- This filter only invokes the FidoUafAuthenticationFilter if the HTTP method is NOT PATCH. -->
<filter>
<filter-name>DispatchTargetAuthenticationFilter</filter-name>
<filter-class>ch::nevis::isiweb4::filter::mapping::FilterMappingFilter</filter-class>
<init-param>
<param-name>FilterRules</param-name>
<!-- do NOT require authentication for PATCH requests -->
<param-value>
!Condition:ENV:REQUEST_METHOD:PCRE/^PATCH/
FidoUafAuthenticationFilter
</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>DispatchTargetAuthenticationFilter</filter-name>
<url-pattern>/nevisfido/token/dispatch/targets/*</url-pattern>
</filter-mapping>
nevisAuth
Use Case Independent Configuration
nevisFIDO consumes the SecToken generated by nevisAuth. The SecToken must contain the uniquely identifiable username
of the user. In the context of nevisIDM, the extId
of the user will be stored in the userid
attribute of the SecToken (default behaviour) and the login ID in the loginId
attribute. The TokenAssembler
of nevisAuth must be configured accordingly:
<TokenAssembler name="DefaultTokenAssembler">
<Selector default="true" />
<TokenSpec version="CSSO-1.0" ttl="28800" useGmt="true" algorithm="SHA1withRSA">
<field src="session" key="ch.nevis.session.sessid" as="sessid" />
<!-- the userId must be part of the secToken for nevisFIDO -->
<field src="session" key="ch.nevis.session.userid" as="userid" />
<field src="session" key="ch.nevis.session.authlevel" as="authLevel" />
<field src="session" key="ch.nevis.session.esauthid" as="esauthid" />
<field src="session" key="ch.nevis.session.entryid" as="entryid" />
<!-- the loginId must be part of the secToken for nevisFIDO -->
<field src="session" key="ch.nevis.session.loginid" as="loginId" />
<field src="session" key="ch.nevis.session.domain" as="domain" />
<field src="session" key="ch.nevis.session.secroles" as="roles" />
<field src="session" key="ch.adnovum.nevisidm.profileId" as="profileId" />
<field src="session" key="ch.adnovum.nevisidm.clientId" as="clientId" />
</TokenSpec>
<Signer key="DefaultSigner" />
</TokenAssembler>
In-Band Registration
In the in-band registration scenario, nevisAuth must protect the Registration Service endpoint of nevisFIDO. A client (user) wanting to register must authenticate first, with credentials stored in nevisIDM. This is why an IdmPasswordVerifyStateis used.
In the sample below, the in-band registration flow uses the domain "PASSWORD_AUTHENTICATION"
.
Note that the domain name must match the realm name in the nevisProxy configuration.
Furthermore, the entry point configuration of the domain uses the selector
attribute to:
- Send all requests to
/auth/pwd
to theIdmUserIdPasswordLogin
AuthState. - Send all requests to other endpoints directly to the
UnauthorizedRequiringPassword
AuthState
In-band registration requires the endpoints to be protected by some form of authentication. The example snippets here simply reflect one possible solution. The final configuration depends on customer requirements or already existing authentication flows.
<AuthEngine name="AuthEngine"
classLoadStrategy="PARENT_FIRST"
useLiteralDictionary="true"
addAutheLevelToSecRoles="true"
compatLevel="none"
inputLanguageCookie="LANG"
classPath="/opt/nevisauth/plugin:/var/opt/nevisauth/<instance-name>/plugin:/opt/nevisidm/nevisauth/lib:/opt/nevisidmcl/<nevisidm-version>/nevisauth/lib/">
<!-- Userid/Password login -->
<AuthState name="IdmUserIdPasswordLogin" class="ch.nevis.idm.authstate.IdmPasswordVerifyState" authLevel="auth.weak" final="false">
<ResultCond name="ok" next="IdmPostProcessing" />
<ResultCond name="pwChange" next="IdmPasswordChange" />
<ResultCond name="lockWarn" next="IdmUserIdPasswordLogin" />
<ResultCond name="nowLocked" next="IdmUserIdPasswordLogin" />
<ResultCond name="locked" next="IdmUserIdPasswordLogin" />
<ResultCond name="tmpLocked" next="IdmUserIdPasswordLogin" />
<ResultCond name="failed" next="IdmUserIdPasswordLogin" />
<ResultCond name="clientNotFound" next="IdmUserIdPasswordLogin" />
<ResultCond name="disabled" next="IdmUserIdPasswordLogin" />
<Response value="AUTH_CONTINUE">
<Gui name="AuthUidPwDialog" label="login.uidpw.label">
<GuiElem name="lasterror" type="error" label="${notes:lasterrorinfo}" value="${notes:lasterror}" />
<GuiElem name="isiwebuserid" type="text" label="userid.label" value="${notes:loginid}" />
<GuiElem name="isiwebpasswd" type="pw-text" label="password.label" />
<GuiElem name="submit" type="submit" label="submit.button.label" value="Login" />
</Gui>
</Response>
<property name="login.service.connection.1" value="http://localhost:8989/nevisidm/services/v1/LoginService" />
<property name="login.service.connection.2" value="" />
<property name="client.name" value="myclient" />
<property name="ticket" value="false" />
</AuthState>
<!-- nevisIdm password change -->
<AuthState name="IdmPasswordChange" class="ch.nevis.idm.authstate.IdmChangePasswordState" authLevel="auth.weak">
<ResultCond name="ok" next="IdmPostProcessing" />
<ResultCond name="policyFailure" next="IdmPasswordChange" />
<ResultCond name="default" next="IdmPasswordChange" />
<Response value="AUTH_CONTINUE">
<Gui name="AuthUidPwDialog" label="login.pwchange.label">
<GuiElem name="lasterror" type="error" label="${notes:lasterrorinfo}" value="${notes:lasterror}" />
<GuiElem name="isiwebnewpw1" type="pw-text" label="newpassword.label" />
<GuiElem name="isiwebnewpw2" type="pw-text" label="newpassword.label" />
<GuiElem name="submit" type="submit" label="submit.button.label" value="Reset" />
</Gui>
</Response>
<propertyRef name="IdmUserIdPasswordLogin" />
</AuthState>
<!-- profile selection and credential update -->
<AuthState name="IdmPostProcessing" class="ch.nevis.idm.authstate.IdmGetPropertiesState" final="false">
<ResultCond name="ok" next="AuthDone" />
<ResultCond name="showGui" next="IdmPostProcessing" />
<!-- profiles; staying implicitely sets final=true -->
<ResultCond name="SOAP:showGui" next="AuthDone" />
<!-- missing roles: get us a nice 403 -->
<ResultCond name="default" next="AuthDone" />
<Response value="AUTH_CONTINUE">
<Gui name="AuthProfileSelectionDialog">
<GuiElem name="lasterror" type="error" label="${notes:lasterrorinfo}" value="${notes:lasterror}" />
<!-- IdmGetPropertiesState will dynamically construct the radio button selection for the profiles -->
</Gui>
</Response>
<propertyRef name="IdmUserIdPasswordLogin" />
<property name="user.attributes" value="extId" />
</AuthState>
<!-- FIDO UAF In-Band Registration Domain/Realm. The name must match the realm name in nevisProxy configuration. -->
<Domain name="PASSWORD_AUTHENTICATION" default="false" reauthInterval="0" inactiveInterval="1800">
<!-- If the client does not use the /auth/pwd endpoint to authenticate: return simply a 401 HTTP error. -->
<Entry method="authenticate" state="IdmUserIdPasswordLogin" selector="/auth/pwd" />
<Entry method="authenticate" state="UnauthorizedRequiringPassword" />
</Domain>
If you have an HTTP JSON based client, the use of a DirectResponseState
AuthState as final AuthState is recommended,
because it will make integration easier. For example, the AuthDone
AuthState below will return a successful HTTP response with { "status" : "completed"}
as body:
<!-- Final state on successful authentication -->
<AuthState name="AuthDone"
class="ch.nevis.esauth.auth.states.directResponse.DirectResponseState"
final="true"
resumeState="false">
<Response value="AUTH_DONE"/>
<property name="contentType" value="application/json"/>
<property name="content" value="{"status":"completed"}"/>
<property name="statusCode" value="200" />
</AuthState>
<!-- Generic error -->
<AuthState name="AuthError"
class="ch.nevis.esauth.auth.states.directResponse.DirectResponseState"
final="true"
resumeState="false">
<Response value="AUTH_ERROR"/>
<property name="contentType" value="application/json"/>
<property name="content" value="{"status":"failed"}"/>
<property name="statusCode" value="401" />
</AuthState>
<!-- Auth state informing the client about not being authenticated and requiring password authentication when trying to access an endpoint without a secToken present -->
<AuthState name="UnauthorizedRequiringPassword"
class="ch.nevis.esauth.auth.states.directResponse.DirectResponseState"
final="true"
resumeState="false">
<Response value="AUTH_ERROR"/>
<property name="contentType" value="text/plain;charset=utf-8"/>
<property name="content" value="Unauthorized"/>
<property name="statusCode" value="401" />
<property name="header.WWW-Authenticate" value="Basic realm="${request:domain}""/>
</AuthState>
In-Band Authentication
In the in-band authentication scenario, nevisAuth must use FIDO UAF for authentication. This is realized by configuring a FidoUafAuthState
. This AuthState provides a nevisFIDO server that interacts with the FIDO Client. For details regarding the FidoUafAuthState
, refer to the nevisFIDO Reference Guide.
nevisFIDO uses a certificate to establish a secure (TLS) HTTP communication with nevisAuth. It is important that you include the public key of this certificate as a keystore ("TrustStoreForNevisFido" in the following configuration snippet). The nevisFIDO server instance in the sample configuration is configured locally on port 8443, whereas the domain name is "FIDO_UAF_AUTHENTICATION"
.
Similar to the in-band registration use case, also here the domain name must match the nevisProxy realm name for mobile authentication.Furthermore, the entry point configuration of the domain uses the selector
attribute to:
- Send all requests to `/auth/fidouaf
to the
FidoUafAuthState` AuthState. - Send all requests to other endpoints directly to the
UnauthorizedRequiringFidoUaf
AuthState.
<SessionCoordinator name="SessionCoordinator" mode="default">
...
<KeyStore name="TrustStoreForNevisFido">
<KeyObject id="NevisFidoPublicKey" certificate="/var/opt/certs/nevisauth-truststore.jks" passPhrase="password" />
</KeyStore>
</SessionCoordinator>
<AuthEngine name="AuthEngine"
classLoadStrategy="PARENT_FIRST"
useLiteralDictionary="true"
addAutheLevelToSecRoles="true"
compatLevel="none"
inputLanguageCookie="LANG"
classPath="/opt/nevisauth/plugin:/var/opt/nevisauth/<instance-name>/plugin:/opt/nevisidm/nevisauth/lib:/opt/nevisidmcl/<nevisidm-version>/nevisauth/lib/">
<AuthState name="FidoUafAuthState" class="ch.nevis.auth.fido.uaf.authstate.FidoUafAuthState" final="false" resumeState="false">
<ResultCond name="ok" next="AuthDone" />
<ResultCond name="error" next="AuthError" />
<ResultCond name="failed" next="AuthError" />
<property name="fidoUafUsername" value="${inargs:o.loginId.v}" />
<property name="fidoUafServerUrl" value="https://localhost:8443/nevisfido" />
<property name="httpclient.tls.truststoreRef" value="TrustStoreForNevisFido" />
<property name="fidoUafTransactions" value="${inargs:o.transactions.v}" />
</AuthState>
<!-- FIDO UAF Domain/Realm. The name must match the realm name in nevisProxy configuration. -->
<Domain name="FIDO_UAF_AUTHENTICATION" default="true" reauthInterval="0" inactiveInterval="1800">
<!-- If the client does not use the /auth/fidouaf endpoint to authenticate: return simply a 401 HTTP error. -->
<Entry method="authenticate" state="FidoUafAuthState" selector="/auth/fidouaf" />
<Entry method="authenticate" state="UnauthorizedRequiringFidoUaf" />
<Entry method="stepup" state="FidoUafAuthState" />
</Domain>
The DirectResponseState
AuthStates used for in-band registration can be re-used for the in-band authentication scenario.
An additional DirectResponseState
AuthState informs authentication clients that are not authorized.
<!-- unauthorized requiring FIDO UAF authentication -->
<AuthState name="UnauthorizedRequiringFidoUaf"
class="ch.nevis.esauth.auth.states.directResponse.DirectResponseState"
final="true"
resumeState="false">
<Response value="AUTH_ERROR"/>
<property name="contentType" value="text/plain;charset=utf-8"/>
<property name="content" value="Unauthorized"/>
<property name="statusCode" value="401" />
<property name="header.WWW-Authenticate" value="FIDO realm="${request:domain}""/>
</AuthState>
FIDO UAF Credential Deregistration
nevisAuth must be able to protect the Deregistration endpoint of nevisFIDO with FIDO UAF, as outlined in the nevisProxy configuration. You can re-use the nevisAuth configuration of the in-band authentication for the FIDO UAF credential deregistration scenario. No additional configuration is required.
Out-of-Band Registration
nevisAuth requires no special configuration to support the out-of-band registration scenario. However, you need protection of some kind for the endpoint supporting the registration process.
Out-of-Band Authentication
The out-of-band authentication scenario requires that the FIDO_UAF_DEMO domain must match the nevisProxy realm for out-of-band mobile authentication.
The out-of-band authentication flow in nevisAuth is more complex than the in-band scenario. You need more AuthStates to support the login procedure executed by the JavaScript client application. For more information on the JavaScript client application, see Integrating the JavaScript Client Application in Out-of-Band Login Flows .
In the following sample code snippet, the first AuthState allows a user to only submit his username to start the out-of-band login process. The second AuthState provides additional information to the JavaScript client application as well as "triggers" the authentication process itself. The third AuthState is called repeatedly by the JavaScript client application until authentication either succeeds or fails.
Note that visual string channel linking is configured in the third AuthState DemoOutOfBandFidoUafAuthState
. For more information, see Channel Linking.
<AuthState name="SubmitUsername" class="ch.nevis.esauth.auth.states.standard.AuthGeneric">
<ResultCond name="default" next="SubmitOutOfBandParameters" />
<ResultCond name="error" next="AuthError" />
<ResultCond name="failed" next="AuthError" />
<Response value="AUTH_CONTINUE">
<Gui name="submitusername" label="Login">
<GuiElem name="isiwebuserid" type="text" label="userid.label" value="${notes:loginid}" />
<GuiElem name="submit" type="button" label="continue.button.label" value="continue" />
</Gui>
</Response>
</AuthState>
<AuthState name="SubmitOutOfBandParameters" class="ch.nevis.esauth.auth.states.standard.AuthGeneric">
<ResultCond name="default" next="DemoOutOfBandFidoUafAuthState" />
<ResultCond name="error" next="AuthError" />
<ResultCond name="failed" next="AuthError" />
<Response value="AUTH_CONTINUE">
<Gui name="oobloginform">
<GuiElem name="isiwebuserid" type="text" value="${inargs:isiwebuserid}" optional="true" />
<GuiElem name="redeemUrl" type="hidden" value="https://<public domain name>/nevisfido/token/redeem/authentication" optional="true" />
<GuiElem name="submit" type="button" label="continue.button.label" value="continue" optional="true" />
</Gui>
</Response>
</AuthState>
<AuthState name="DemoOutOfBandFidoUafAuthState" class="ch.nevis.auth.fido.uaf.authstate.OutOfBandFidoUafAuthState" final="false">
<ResultCond name="ok" next="AuthDone" />
<ResultCond name="error" next="AuthError" />
<ResultCond name="failed" next="AuthError" />
<property name="fidoUafUsername" value="${inargs:o.loginId.v}" />
<property name="fidoUafServerUrl" value="https://localhost:8443/nevisfido" />
<property name="httpclient.tls.truststoreRef" value="TrustStoreForNevisFido" />
<property name="fidoUafTransactions" value="${inargs:o.transactions.v}" />
<property name="channelLinkingMode" value="visualString" />
</AuthState>
<Domain name="FIDO_UAF_DEMO" default="false" reauthInterval="0" inactiveInterval="1800">
<Entry method="authenticate" state="SubmitUsername" />
</Domain>
Legacy or Mobile Authentication Based on the User's Registration Status
An example of a more specialised scenario is where the authentication flow for a given user is based on the user's registration status. This allows Nevis to:
- either present a password-based login (legacy login) to users who have not registered for mobile authentication yet, or
- present the out-of-band login to users who registered for mobile authentication.
To support this scenario, an additional AuthState checks the availability of (active) FIDO UAF credentials for the given user. The outcome of the check determines the next step in the flow.
In the example below, it is assumed that there exists an AuthState named LegacyAuthState
, which performs the required authentication when no FIDO UAF credentials are defined (like password-based authentication).
The check works as follows:
- The
IdmUserVerifyState
loads all user information. - The
IdmCredStatusCheckState
checks the user information for active FIDO UAF credentials and sets the next AuthState based on the result.
For more details, check the Legacy or Mobile Authentication use case .
<AuthState name="FetchUser" class="ch.nevis.idm.authstate.IdmUserVerifyState" authLevel="auth.weak" final="false">
<ResultCond name="ok" next="DemoCheckFidoCredential" />
<ResultCond name="default" next="DemoCheckFidoCredential" />
<Response value="AUTH_CONTINUE">
<Gui name="AuthUidPwDialog" label="login.uidpw.label">
<GuiElem name="lasterror" type="error" label="${notes:lasterrorinfo}" value="${notes:lasterror}" />
<GuiElem name="isiwebuserid" type="text" label="userid.label" value="${notes:loginid}" />
<GuiElem name="submit" type="submit" label="submit.button.label" value="Login" />
</Gui>
</Response>
<property name="user.loginId" value="${inargs:isiwebuserid}" />
<property name="login.service.connection.1" value="https://siven.ch:8443/nevisidm/services/v1/LoginService" />
<property name="client.name" value="myclient" />
<property name="ticket" value="false" />
</AuthState>
<AuthState name="CheckFidoCredential" class="ch.nevis.idm.authstate.IdmCredStatusCheckState" final="false">
<ResultCond name="noCredential" next="SetFidoUafNotFoundMessage"/>
<ResultCond name="ok" next="SubmitOutOfBandParameters"/>
<ResultCond name="default" next="AuthErrorGui"/>
<property name="credentialType" value="FIDO_UAF"/>
<property name="updateLoginState" value="false"/>
<property name="incrementFailureCounter" value="false"/>
<property name="singleCredential" value="false"/>
<property name="strictMode" value="false"/>
<property name="initialAllowed" value="false"/>
</AuthState>
<AuthState name="SetFidoUafNotFoundMessage" class="ch.nevis.esauth.auth.states.standard.TransformAttributes" final="false">
<ResultCond name="default" next="LegacyAuthState"/>
<property name="notes:lasterror" value="0"/>
<property name="notes:lasterrorinfo" value="No FIDO UAF Credentials found for the user. Provide password credentials."/>
</AuthState>
<AuthState name="SubmitOutOfBandParameters" class="ch.nevis.esauth.auth.states.standard.AuthGeneric">
<ResultCond name="default" next="OutOfBandFidoUafAuthState" />
<ResultCond name="error" next="AuthError" />
<ResultCond name="failed" next="AuthError" />
<Response value="AUTH_CONTINUE">
<Gui name="oobloginform">
<GuiElem name="isiwebuserid" type="text" value="${sess:ch.adnovum.nevisidm.user.loginId}" optional="true" />
<GuiElem name="redeemUrl" type="hidden" value="https://siven.ch/token/redeem/authentication" optional="true" />
<GuiElem name="submit" type="button" label="continue.button.label" value="continue" optional="true" />
</Gui>
</Response>
</AuthState>
<AuthState name="OutOfBandFidoUafAuthState" class="ch.nevis.auth.fido.uaf.authstate.OutOfBandFidoUafAuthState" final="false">
<ResultCond name="ok" next="AuthDone" />
<ResultCond name="error" next="AuthError" />
<ResultCond name="failed" next="AuthError" />
<property name="fidoUafUsername" value="${sess:ch.adnovum.nevisidm.user.loginId}" />
<property name="fidoUafServerUrl" value="https://{{ inventory_hostname }}:{{ nevisfido_port }}{{ nevisfido_context }}" />
<property name="httpclient.tls.trustStoreRef" value="TrustStoreForNevisFido" />
<property name="httpclient.tls.keyObjectRef" value="KeyStoreForNevisFido/ClientKeyForNevisFido" />
</AuthState>
Instead of an OutOfBandFidoUafAuthState
you could use an in-band FidoUafAuthState
, depending on whether in-band or out-of-band is required. The snippet referenced above uses an OutOfBandFidoUafAuthState
.
Dispatch Target Management
nevisAuth must protect the dispatch target management endpoints of nevisFIDO with FIDO UAF, as outlined in the nevisProxy configuration (apart from HTTP PATCH
). For this scenario, you can re-use the nevisAuth configuration for in-band authentication. No additional configuration is required.
nevisFIDO
Use Case Independent Configuration
Important points regarding the use-case-independent configuration of nevisFIDO:
- The main configuration file for the nevisFIDO component is named
nevisfido.yml
. - In all configuration scenarios, the configured HTTP port of nevisFIDO must match the port referenced in
the
HttpsConnectorServlet
of nevisProxy. - In all example scenarios, nevisFIDO uses nevisIDM as main credential storage. The connection between nevisFIDO and nevisIDM is based on client-TLS, which requires certificate configuration.
The following code snippet shows a typical configuration of the nevisIDM backed credential repository:
credential-repository:
type: nevisidm
# Using the full qualified name is important here to have a proper host name match during
# TLS handshake.
administration-url: https://muvonda.com:8443/nevisidmcc/services/v1_42/AdminService
keystore: /var/opt/certs/nevisfido-client-keystore.p12
keystore-passphrase: password
keystore-type: pkcs12
truststore: /var/opt/certs/nevisfido-truststore.p12
truststore-passphrase: password
truststore-type: pkcs12
admin-service-version: v1_42
client-name: myclient
username-mapper:
attributes:
- loginId
- extId
In-Band Registration
In the in-band registration scenario, the registered credentials are associated with the user who logged in with nevisIDM. The SecToken
generated by nevisAuth contains the information about this user. Therefore, use "SecToken" as the type of authorization. The configuration of the authorization must provide a truststore containing the public key of nevisAuth. You need this public key to check that the SecToken was really generated by nevisAuth.
The following configuration assumes that the username in the registration GetUAFRequest
is defined by either the
userIdattribute (nevisIDM extId
) or the loginId
attribute of the SecToken:
authorization:
registration:
type: sectoken
truststore: /var/opt/certs/nevisfido-truststore.p12
truststore-passphrase: password
truststore-type: pkcs12
username-attribute-names:
- loginId
- userid
In-Band Authentication
No special configuration is required for non-step-up in-band authentication scenarios. In such scenarios, the endpoint requires no authentication configuration:
authorization:
authentication:
type: none
FIDO UAF Credential Deregistration
In the FIDO UAF credential deregistration scenario, the credentials-to-be-removed are associated with the user who logged in with nevisIDM. The SecToken
generated by nevisAuth contains the information about this user. Therefore,
use SecToken
as the type of authorization. The configuration of the authorization must provide a truststore containing the public key of nevisAuth. You need this public key to check that the SecToken was really generated by nevisAuth.
The next code block shows a sample configuration:
authorization:
deregistration:
type: sectoken
truststore: /var/opt/certs/nevisfido-truststore.p12
truststore-passphrase: password
truststore-type: pkcs12
username-attribute-names:
- loginId
- userid
Out-of-Band Registration
The out-of-band registration scenario requires the same configuration as the in-band registration scenario. For details, see In-Band Registration.
Refer to the nevisFIDO Reference Guide for details regarding the nevisFIDO configuration.
Out-of-Band Authentication
The out-of-band authentication scenario requires the same configuration as the in-band authentication scenario. For details, see In-Band Authentication.
However, the correct support of out-of-band scenarios usually requires a dispatcher configuration. The following code snippet shows an example using the included Firebase Cloud Messaging dispatcher:
dispatchers:
- type: firebase-cloud-messaging
dry-run: false
service-account-json: /var/opt/nevisfido/default/conf/service-account.json
registration-redeem-url: https://<hostname>/nevisfido/token/redeem/registration
authentication-redeem-url: https://<hostname>/nevisfido/token/redeem/authentication
deregistration-redeem-url: https://<hostname>/nevisfido/token/redeem/deregistration
Dispatch Target Management
It is necessary to protect the dispatch target endpoints in out-of-band scenarios, because only authenticated clients should be allowed to modify them.
The following snippet shows the most common scenario. It uses SecToken authorization to protect the dispatch target endpoints.
Note that the modify-dispatch-target
entry is missing in the authorization configuration as authorization is mandatory and handled with JWS.
authorization:
create-dispatch-target:
type: sectoken
truststore: /var/opt/certs/nevisfido-truststore.p12
truststore-passphrase: password
truststore-type: pkcs12
username-attribute-names:
- loginId
- userid
query-dispatch-target:
type: sectoken
truststore: /var/opt/certs/nevisfido-truststore.p12
truststore-passphrase: password
truststore-type: pkcs12
username-attribute-names:
- loginId
- userid
delete-dispatch-target:
type: sectoken
truststore: /var/opt/certs/nevisfido-truststore.p12
truststore-passphrase: password
truststore-type: pkcs12
# Using userid is required: there is no username in the delete request (i.e. no loginId), so
# all the user identifier we have is the userid (extId in the context of nevisIDM).
username-attribute-names:
- userid
nevisIDM
Dispatch Target Management
If you use nevisIDM 2.75.0 or later, all the required schema definitions for the dispatch targets are provided out-of-the-box by nevisIDM. In this case, you can skip this section.
nevisFIDO manages persisted dispatch targets by using nevisIDM custom credential objects. Install custom properties for dispatch targets in the nevisIDM database before you can use the targets with nevisFIDO.
You can set up the custom properties used by nevisFIDO with the following SQL script. Note that you might need to adjust the IDs used here (1001, 1002, ... – should not be in use yet), depending on your setup of nevisIDM.
-- Set up custom properties used by nevisFIDO Dispatching.
DELETE FROM TIDMA_PROPERTY_VALUE WHERE property_value_id >= 1001 and property_value_id <= 1018;
DELETE FROM TIDMA_CREDENTIAL WHERE credential_id in (1011, 1012, 1013);
DELETE FROM TIDMA_PROPERTY WHERE property_id in (1001, 1002, 1003, 1004, 1005, 1006);
DELETE FROM TIDMA_POLICY_CONFIGURATION WHERE policy_configuration_id in (1001);
commit;
-- Generic Credential policy is required to be able to create Generic Credentials.
INSERT INTO TIDMA_POLICY_CONFIGURATION
(POLICY_CONFIGURATION_ID, CTL_TCN, CTL_CRE_UID, CTL_CRE_DAT, CTL_MOD_UID, CTL_MOD_DAT, POLICY_TYPE, DESCRIPTION, NAME, EXTID, DEFAULT_POLICY, CLIENT_ID)
VALUES
(1001, 0, 'Default/bootstrap', NOW(), 'Default/bootstrap', NOW(), 'GenericCredentialPolicy', 'Store dispatch target information', 'Dispatch targer policy', 1001, 1, 100);
commit;
-- Add Dispatcher target property definitions for Generic Credential type
-- Note: using bootstrap user, the fields will be editable on nevisIdm UI, because the root user has permission "PropertyAttributeAccessOverride"
INSERT INTO TIDMA_PROPERTY
(PROPERTY_ID, CTL_TCN, CTL_CRE_UID, CTL_CRE_DAT, CTL_MOD_UID, CTL_MOD_DAT, NAME, DESCRIPTION, TYPE, SCOPE, ENCRYPTED, PROPAGATED, MANDATORY_ON_GUI, STR_MAX_LEN, STR_REGEX, ACCESS_CREATE, ACCESS_MODIFY, UNIQUENESS_SCOPE, GUI_PRECEDENCE, DISPLAYNAME_DICT_ENTRY_ID, APPLICATION_ID, CLIENT_ID)
VALUES
(1001, 0, 'Default/bootstrap', NOW(), 'Default/bootstrap', NOW(), 'fidouaf_name', 'Human readable name of the device', 2, 21, 0, 0, 1, 1000, NULL, 'rw', 'rw', NULL, 0, NULL, NULL, NULL),
(1002, 0, 'Default/bootstrap', NOW(), 'Default/bootstrap', NOW(), 'fidouaf_app_id', 'The appId of the application where the device is registered', 2, 21, 0, 0, 1, 100, NULL,'rw', 'rw', NULL, 1, NULL, NULL, NULL),
(1003, 0, 'Default/bootstrap', NOW(), 'Default/bootstrap', NOW(), 'fidouaf_target', 'The target identifier of the channel', 2, 21, 0, 0, 1, 4096, NULL, 'rw', 'rw', NULL, 2, NULL, NULL, NULL),
(1004, 0, 'Default/bootstrap', NOW(), 'Default/bootstrap', NOW(), 'fidouaf_dispatcher', 'The name of the dispatcher', 2, 21, 0, 0, 1, 100, NULL, 'rw', 'rw', NULL, 3, NULL, NULL, NULL),
(1005, 0, 'Default/bootstrap', NOW(), 'Default/bootstrap', NOW(), 'fidouaf_encryption_key', 'Encryption key used for encrypting the channel data', 2, 21, 0, 0, 1, 10000, NULL, 'rw', 'rw', NULL, 4, NULL, NULL, NULL),
(1006, 0, 'Default/bootstrap', NOW(), 'Default/bootstrap', NOW(), 'fidouaf_signature_key', 'The signature key used for signing dispatch channel information', 2, 21, 0, 0, 1, 10000, NULL, 'rw', 'rw', NULL, 5, NULL, NULL, NULL),
(1007, 0, 'Default/bootstrap', NOW(), 'Default/bootstrap', NOW(), 'fidouaf_device_id', 'The device identifier', 2, 21, 0, 0, 1, 10000, NULL, 'rw', 'rw', NULL, 6, NULL, NULL, NULL;
commit;
The script must be applied to the nevisIDM database:
mysql -u root --password="" nevisidm < nevisfido-customprops.sql
In case the policy and properties have been installed earlier, for example during testing and integration, you might need to delete them first. The following SQL script shows how to delete the policy configuration and properties from the earlier SQL script.
-- Set up custom properties used by nevisFIDO Dispatching.
DELETE FROM TIDMA_POLICY_CONFIGURATION WHERE policy_configuration_id in (1001);
DELETE FROM TIDMA_PROPERTY WHERE property_id in (1001, 1002, 1003, 1004, 1005, 1006);
COMMIT;
Treat the above SQL snippets as examples; the specific IDs can be different in any given setup of nevisIDM.
In any case, the deployment options with nevisAdmin 4 (which hides the low-level SQL details) are preferred.