The look and feel of all user-facing screens in the entire Curity Identity Server is customizable. Each profile – the OAuth Server, Authentication Service, OpenID Connect Identity Provider, etc. – has specific resources that can be customized, but the process is the same for all. In this section, we will explain the customization process in general and refer to the specific details that vary per profile (e.g., template names).
All screens that are presented to the user are based on templates. The Curity Identity Server uses the Velocity templating language to allow for reusability between screens. The delivered templates use an include hierarchy that can be reused if desired. However the system allows for complete freedom to customize everything with your own template hierarchy.
A normal page without any modifications has the following template components:
Fig. 180 Template layout of a login screen
The entry point for the screen is the inner most template authenticator/html-form/authenticate/get.vm. It defines the form as a body variable. In the end of the file it includes a layout, since this is a single column page, it includes the layouts/default.vm layout. The layout is the common ground for all pages with this characteristic. It in turn pulls in the fragments/logo.vm as well as the fragments/alerts.vm which is conditionally included based on if the page has any errors in the $_errors velocity variable.
authenticator/html-form/authenticate/get.vm
layouts/default.vm
fragments/logo.vm
fragments/alerts.vm
$_errors
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
#define ($_body) <form method="post" action="$_authUrl"> ... <label for="userName" class="$!_errorClass">#message("authenticator.html-form.authenticate.view.username")</label> <input type="text" name="userName" class="block full-width mb1 field-light $!_errorClass" autocapitalize="none" value="$!userNameValue"> <label>#message("authenticator.html-form.authenticate.view.password")</label> <input type="password" name="password" class="block full-width mb1 field-light"> <button type="submit" class="button button-fullwidth button-primary">#message( "authenticator.html-form.authenticate.view.authenticate")</button> <div class="mt3 clearfix"> <div class="sm-col-12 center py2"> <a href="$_authUrl/forgot-password">#message( "authenticator.html-form.authenticate.view.forgot-password")</a> <br/> <a href="$_authUrl/forgot-account-id">#message( "authenticator.html-form.authenticate.view.forgot-account-id")</a> </div> #if ($_registerUrl) <div class="sm-col-12 center"> <a href="$_registerUrl" class="button button-light-grey button-fullwidth">#message( "${_templatePrefix}.view.no-account")</a> </div> #end </div> </form> #end #parse("layouts/default")
The highlighted line in the end, shows how the template pulls in the layout. Another important note is that the template itself only defines a variable called $_body. This is then used in layouts/default.vm to place the main content in the appropriate place for that type of layout.
$_body
The layout also pulls in things like headers, scripts, css definitions etc, to make those easily customizable without touching many files.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
<main class="container clearfix" role="main"> <div class="login-well"> #parse("fragments/logo") #if ($_errors || $_warnings || $_infoMessages) <div class="mt3 px3 lg-px4"> #parse("fragments/alerts") </div> #end $!_body <div class="login-help center p2"> <a href=""> <i class="icon ion-information-circled"></i> #message("page.problems-logging-in") </a> </div> </div> ##login-well </main> #parse("fragments/footer")
Looking at the layout it becomes more clear how it pulls in the logo and the alerts. Line 13 highlights how it places the content of the $_body variable in the page.
The Curity Identity Server ships with a complete set of screens. The are referred to as core templates. These templates are used as fallbacks when there is no override template defined. Curity uses three levels of templates.
Fig. 181 Template hierarchy
When a template is included via the #parse velocity directive, Curity will first look in the Template Areas location for it, if not found there, it will look in the Overrides location, and then finally in Core.
#parse
This means that a page can be made up of both Core templates and overridden templates. Lets look at a small example:
Fig. 182 Example of overridden template
The example above shows the login template but with updated colors and updated logo. This can be achieved by overriding two templates and letting the rest be reused from core.
fragments/css.vm
With these two overrides all screens using the logo and stylesheets from the fragments includes, will have a new look. Split apart it looks like this:
fragments
Fig. 183 Structure of overrides
The overrides serve as server wide overrides. I.e. all screens may be affected by the override. It does not take into consideration which client or application is making the request, the overrides is always served if it exist. This is useful when you want to make a default look and feel, that is your organizations standard look. It can be anything from changing the logo and colors to completely rewriting the structure of the template include system.
To override a template. Find the template you want to override in $IDSVR_HOME/usr/share/templates/core and make the modifications that are needed.
$IDSVR_HOME/usr/share/templates/core
To deploy the override place the new template in the same relative path under $IDSVR_HOME/usr/share/templates/overrides.
$IDSVR_HOME/usr/share/templates/overrides
Example: Overriding the Logotype
$IDSVR_HOME/usr/share/templates/core/fragments/logo.vm
$IDSVR_HOME/usr/share/templates/overrides/fragments/logo.vm
Notice how the relative path to the templates directory is kept when placing it in overrides. I.e. fragments/ is still there. This is important, since the main template, layouts/default.vm is looking for an include called fragments/logo.vm. So when Curity is trying to resolve the logo.vm it will start by looking in template-areas then in overrides and finally in core.
templates
fragments/
logo.vm
template-areas
overrides
core
Important
As good practice, avoid updating any templates in core directly, always place the updated template the overrides directory. This will ensure that future upgrades of the Curity Identity Server won’t collide with any changes made to the templates.
Templates areas is a more advanced override technique. It allows for app-specific, authenticator or authentication action specific overrides, without changing every screen in the system.
Consider the following use-case.
Company Jivea has a sub-branded web-site where customers can buy shoes. It is quite different in it’s look and feel, so when a customer logs in to the shoe website the company wants to make sure that the feeling is still kept, and when they login to the regular website it must reflect that, with the logo kept etc.
Each site is represented by an Open ID Connect client. In Curity it’s possible to define a Template Area. The template area is set in the session when the clients requests authentication. When such a template area is found, Curity will first try to locate the template in that speciffic template area, if not found, it will look in the overrides, and if not found there, it will look in core.
Lets revisit the override example but also with a template area defined.
Fig. 184 Example when using template-areas
Of course there is nothing preventing the entire layout to be overriden to create a completely different feeling when authenticating from certain websites or apps. By overriding the layouts/default.vm this can easily be achieved as well.
Deploying a template-area
In contrast to the overrides, the template-areas are named. So there can be many template-areas serving different clients. So when deploying a template area, follow the same procedure as when deploying an override template but place the template in a directory with the same name as the template-area that is configured for the client.
To deploy fragments/logo.vm for a template-area called shoe-store place it in the following directory:
shoe-store
$IDSVR_HOME/usr/share/templates/template-areas/shoe-store/fragments/logo.vm
Note
Message keys used by templates can also be specialized for each template area, as explained in Message lookup.
Template files are loaded from the <installation-dir>/usr/share/templates directory.
<installation-dir>/usr/share/templates
Message files are loaded from the <installation-dir>/usr/share/messages directory.
<installation-dir>/usr/share/messages
As with templates, the “root” directories are: core, overrides and template-areas/<template-area>.
template-areas/<template-area>
Each messages directory is further sub-divided into Locales (e.g., sv, en_US, en, etc.).
sv
en_US
en
Each of the Locale subdirectories and the templates directory share a common structure, described below. The Identity Server uses this structure to locate the right message files and templates when rendering the resulting UI page.
The structure of both locale and templates directories is composed of the following subdirectories:
Templates are searched for in the following “root” directories, in this order:
The template-areas directory contains instance-specific overrides. Each authenticator or OAuth client can be configured to use a specific template-area to make it look and feel as an integral part of the application that is calling it.
Any general overrides that are not instance specific should be placed in the overrides directory which serves as a general override area.
The last area is the core. This is where the delivered templates are located. This area should not be modified, instead the overrides or the template-areas should be used to override the delivered templates. This convention makes sure that future upgrades of the server will be painless since all modifications are kept separate from the delivered templates.
An example of this hierarchy is shown in the following figure:
usr/share/messages ├── core │ └── ... ├── overrides │ ├── en │ │ └── messages │ └── sv │ └── messages └── template-areas ├── custom-template-area │ ├── en │ │ ├── messages │ │ └── authenticator │ │ └── html-form │ │ └── messages │ └── sv │ └── messages └── another-template-area └── ... usr/share/templates ├── core │ └── ... ├── overrides │ └── authenticator │ └── html-form │ └── get.vm ├── site.vm └── template-areas ├── custom-template-area │ └── authenticator │ └── html-form │ └── get.vm └── another-template-area └── ...
It is possible to serve templates directly via the anonymous endpoint.
For example: suppose you would like to provide a Contact Information page. One way to do it would be to create a template under the anonymous views directory. For example, under the overrides directory:
anonymous
<installation-dir>/usr/share/templates/overrides/views/anonymous/help/contact.vm
The template is located by following the logic described in the previous section. To be more specific, when the server receives a request to the /<anonymous-endpoint>/help/contact path, it will try to locate a template in the following locations, in this order:
/<anonymous-endpoint>/help/contact
/overrides/views/anonymous/help/contact.vm
/core/views/anonymous/help/contact.vm
The Curity Identity Server error pages are defined in separate files based on the HTTP error code returned views/error/<HTTP_ERROR>/index.vm. These files use the layouts/error.vm, which, depending on where the error is originating and whether expose-detailed-error-messages is set in the profiles, might contain a detailed description of the error (this shouldn’t be enabled in a production environment). Also, the layout defines an _errorIdentifier, which is a combination of the $RequestId and possibly the $SessionId. This is shown in the templates so that when a user stumbles upon an error, it can be easier to find the logs from the request that produced the error and figure out the root cause.
views/error/<HTTP_ERROR>/index.vm
layouts/error.vm
expose-detailed-error-messages
_errorIdentifier
$RequestId
$SessionId
There are certain variables available in the context of the template. Those are summarized in the following table:
$Guid
generate
$Guid.generate()
request ID = $RequestId
session ID = $SessionId
$RegexUtil
$RegexUtil.replaceGroups($_recipientOfCommunication,
"(.*?)(.{4})", [1], "x")
$_tr
$_tr.encodeHtml('<p>&"</p>')
$_tr.encodeEcmaScript($myVar)
$Error
$Error.create("some.messageId.error")
The following methods are available in the $_tr variable. All take a string as input and return the converted string. If null is supplied, the same value is returned.
null
encodeHtml
encodeHtmlAttribute
encodeEcmaScript
encodeJson
encodeUrl
encodeCss
decodeHtml
decodeHtmlAttribute
decodeEcmaScript
decodeJson
decodeUrl
decodeCss
In addition to the common variables, the following variables are available in the context of templates of an Authenticator or Authentication Action:
$_authUrl
$_registerUrl
$_anonymousUrl
$_requestingOAuthClient
$_requestingOAuthClient.id
$_requestingOAuthClient.properties.example
The following properties are exposed in the $_requestingOAuthClient variable:
id
properties
Note that accessing the $_requestingOAuthClient variable may require the Curity Identity Server to fetch client data from a Data Source (e.g. dynamically registered clients), adding some overhead to the ongoing request. This will be done at most once per request.
The following variables are also available in the context of templates of an Authentication Action:
$_username
$_tr.encodeHtml($_username)
One of the core templates is called csp.vm. This produces Content Security Policies (CSP). These close many dangerous attack vectors in all modern browsers. Overriding this template is possible, but it should be done with care. Removing CSP entirely is very ill advised, and may result in security vulnerabilities.
csp.vm