Authentication methods for REST API

The REST API supports different methods of authentication. This section describes necessary configuration and methods.

1 Session Identification

When using the REST API, a session is identified by the session secret cookie and the session id, which must be sent as query parameter sid. This ensures that

  • A session can never be fully identified by URLs (because the session secret is not part of the URL)
  • One client may have multiple sessions. They share the same session secret, but use different session ids

2 Authentication with credentials

To login with credentials, the client must


POST /CNPortletapp/rest/auth/login
{
  "login": ...,
  "password": ...
}

The response will be of the form


{
  "sid" : "1022",
  "user" : {
    "id" : 34,
    "description" : "",
    "login" : "editor",
    "email" : "",
    "firstName" : "Max",
    "lastName" : "No-Publish"
  },
  "responseInfo" : {
    "responseCode" : "OK",
    "responseMessage" : "Successfully performed login"
  }
}

Note that the “User-Agent” has to be set in the header of the request as it will be saved in the session, otherwise the login will fail.

Additionally, the session secret cookie will be set to the client.

3 SSO with CAS

3.1 Configuration

The CAS client configuration for the REST API is done by adding necessary filters in the file /Node/tomcat/conf/web.xml

/Node/tomcat/conf/web.xml

<!-- =============== CAS SSO Filter Start =============== -->
<!-- Filter parameter shared by some filters -->
<context-param>
	<param-name>casServerLoginUrl</param-name>
	<param-value>{CAS Server Login URL}</param-value>
</context-param>
<context-param>
	<param-name>casServerUrlPrefix</param-name>
	<param-value>{CAS Server URL Prefix}</param-value>
</context-param>
<context-param>
	<param-name>serverName</param-name>
	<param-value>{Server Name}</param-value>
</context-param>

<!-- The authentication filter will check, whether CAS Authentication is necessary and will redirect to the CAS Server -->
<filter>
	<filter-name>CAS Authentication Filter</filter-name>
	<filter-class>org.jasig.cas.client.authentication.AuthenticationFilter</filter-class>
   	<init-param>
     	<param-name>gateway</param-name>
     	<param-value>true</param-value>
   	</init-param>
</filter>
<filter-mapping>
	<filter-name>CAS Authentication Filter</filter-name>
	<url-pattern>/rest/auth/ssologin</url-pattern>
</filter-mapping>

<!-- The Ticket Validation Filter will validate the Ticket (found in the query as additional parameter) against the CAS Server and will do a redirect to the service without the ticket -->
<filter>
	<filter-name>CAS Ticket Validation Filter</filter-name>
	<filter-class>org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter</filter-class>
	<init-param>
		<param-name>acceptAnyProxy</param-name>
		<param-value>true</param-value>
	</init-param>
</filter>
<filter-mapping>
	<filter-name>CAS Ticket Validation Filter</filter-name>
	<url-pattern>/rest/auth/ssologin</url-pattern>
</filter-mapping>

<!-- The Assertion Thread Local Filter makes the assertion available as ThreadLocal (the CASIntegrationFilter depends on it) -->
<filter>
	<filter-name>CAS Assertion Thread Local Filter</filter-name>
	<filter-class>org.jasig.cas.client.util.AssertionThreadLocalFilter</filter-class>
</filter>
<filter-mapping>
	<filter-name>CAS Assertion Thread Local Filter</filter-name>
	<url-pattern>/rest/auth/ssologin</url-pattern>
</filter-mapping>

<!-- The CASIntegrationFilter reads the assertion, matches it against the User-DB (or creates a new user). And performs the login. -->
<filter>
	<filter-name>CAS Gentics CMS Integration Filter</filter-name>
	<filter-class>com.gentics.contentnode.auth.filter.CASIntegrationFilter</filter-class>
	<init-param>
		<param-name>initGroups</param-name>
		<param-value>[4, 6]</param-value> <!-- expression that returns the ids of the initial groups -->
	</init-param>
</filter>
<filter-mapping>
	<filter-name>CAS Gentics CMS Integration Filter</filter-name>
	<url-pattern>/rest/auth/ssologin</url-pattern>
</filter-mapping>
<!-- =============== CAS SSO Filter End   =============== -->

{CAS Server Login URL} must be set to the login URL of the CAS server, {CAS Server URL Prefix} to the URL prefix and {Server Name} to the server name (base URL).

The init parameter initGroups is an expression that must return the ids of the initial groups, new users shall be put in. The expression can resolve systemuser attributes via user.* or attributes returned from the CAS service via attr.*.

For security reasons, it is not possible to add users to groups 1 or 2.

3.2 Client side implementation

The client implementation of SSO login using CAS is problematic, when done with AJAX. The authentication is done by redirecting the client to the CAS Server (which probably is hosted on another domain) which will create a service ticket and redirect back to the authenticated service. Redirects to other domains violate the same domain policy, which normally applies to AJAX requests (with default security settings), therefore the authentication will not be possible using AJAX requests.

However, this problem can be solved, using an iframe, as in the following demo implementation:


// SSO login works with an iframe
var $iframe = $("<iframe></iframe>");
$iframe.hide();
$("body").append($iframe);

// add an onload handler
$iframe.load(function() {
	// get the reponse
	var response = $iframe.contents().text();
	switch(response) {
	case 'NOTFOUND':
	case 'FAILURE':
		// TODO do some error handling
		break;
	default:
		// TODO the response contains the sid now. Store and use in subsequent calls
	}
	// finally remove iframe
	$iframe.remove();
});

// set the source
$iframe.attr("src", '/CNPortletapp/rest/auth/ssologin?ts=' + (new Date()).getTime());

4 SSO with SiteMinder

4.1 Configuration

The SiteMinder client configuration for the REST API is done by adding necessary filters in the file /Node/tomcat/conf/web.xml

/Node/tomcat/conf/web.xml

<!-- =============== SiteMinder SSO Filter Start =============== -->
<filter>
	<filter-name>Http Auth Filter</filter-name>
	<filter-class>com.gentics.contentnode.auth.filter.HttpAuthFilter</filter-class>
</filter>
<filter-mapping>
	<filter-name>Http Auth Filter</filter-name>
	<url-pattern>/rest/auth/ssologin</url-pattern>
</filter-mapping>
<!-- =============== SiteMinder SSO Filter End   =============== -->

Additionally, the feature http_auth_login must be set to true and the names of the headers must be configured in the file /Node/etc/node.conf


// ** Start ** Http Authentication (SiteMinder)
$FEATURE["http_auth_login"] = true;

$HTTP_AUTH_LOGIN["login"] = "HTTP_UID";
$HTTP_AUTH_LOGIN["firstname"] = "HTTP_GIVENNAME";
$HTTP_AUTH_LOGIN["lastname"] = "HTTP_SN";
$HTTP_AUTH_LOGIN["email"] = "HTTP_MAIL";
$HTTP_AUTH_LOGIN["group"] = "HTTP_CN_GRUPPE";
$HTTP_AUTH_LOGIN["splitter"] = ";";

// ** End ** Http Authentication (SiteMinder)

For security reasons, it is not possible to add users to groups 1 or 2.

4.2 Client side implementation

Since the SiteMinder Authentication works without a redirect, the login can be done by just doing an AJAX call to

GET /CNPortletapp/rest/auth/ssologin

The response will simply contain the sid as plain text.

5 SSO with Keycloak

5.1 Configuration

The Keycloak client configuration for the REST API is done by adding necessary filters in the file /Node/tomcat/conf/web.xml.

/Node/tomcat/conf/web.xml

<!-- =============== Keycloak SSO Filter Start   =============== -->

<!-- The Keycloak OpenID Connect filter handles authentication
     against Keycloak. -->
<filter>
    <filter-name>Keycloak Filter</filter-name>
    <filter-class>org.keycloak.adapters.servlet.KeycloakOIDCFilter</filter-class>
    <init-param>
        <param-name>keycloak.config.file</param-name>
        <param-value>/Node/var/httpd/htdocs/customer-config/config/keycloak.json</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>Keycloak Filter</filter-name>
    <url-pattern>/rest/auth/ssologin</url-pattern>
</filter-mapping>

<!-- The KeycloakIntegrationFilter reads the access token from the
     user principals security context, matches its user information
     against the database (or creates a new user), and performs the
     login. -->
<filter>
    <filter-name>Keycloak Gentics CMS Integration Filter</filter-name>
    <filter-class>com.gentics.contentnode.auth.filter.KeycloakIntegrationFilter</filter-class>

    <init-param>
        <param-name>userCreatedCallback</param-name>
        <param-value>your.companyname.custom.SsoUserCreated</param-value>
    </init-param>

    <init-param>
        <param-name>initGroups</param-name>
        <param-value><![CDATA[
            if(attr.roles.realm CONTAINSONEOF 'admin-user-role',
                [3],
                if(attr.roles.resource.gcms CONTAINSONEOF 'editor-user-role',
                    [4],
                    [5]
                )
            )
        ]]></param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>Keycloak Gentics CMS Integration Filter</filter-name>
    <url-pattern>/rest/auth/ssologin</url-pattern>
</filter-mapping>

<!-- =============== Keycloak SSO Filter End   =============== -->

The init parameter keycloak.config.file must be set to the path of Keycloaks configuration file keycloak.json. When using the new UI, this path must also be accessible under YOURHOSTNAME/.Node/customer-config/config/keycloak.json so that the UI can load the configuration.

The optional init parameter userCreatedCallback is the full class name of a callback implementing SsoUserCreatedCallback. When a new user is created by the SSO process, the callback will be executed with the data of the new user. The attributes map contains the following information:

  • firstname: the “given name” claim in the access token
  • lastname: the “family name” claim in the access token
  • email: the “email” claim in the access token
  • roles: a map containing
    • realm: the list of realm roles for the user
    • resource: a mapping from resource names to the respective resource roles for the user

The init parameter initGroups is an expression that must return the IDs of the initial groups, new users shall be put in. The expression can resolve systemuser attributes via user.* or claims from the access token via attr.* (the most interesting attributes probably being the realm roles attr.roles.realm, and the resource specific roles attr.roles.resource.RESOURCENAME).

For security reasons, it is not possible to add users to groups 1 or 2.

5.2 Client side implementation

The same restrictions apply as for the client side implementation for CAS.

6 Synchronizing group IDs

In the configuration in web.xml, you can add following parameters to the integration filter:


<filter>
	<filter-name>Gentics CMS Integration Filter</filter-name>
	<filter-class>com.gentics.contentnode.auth.filter.CASIntegrationFilter</filter-class>
	<init-param>
		<param-name>initGroups</param-name>
		<param-value>eval(attr.cmsgroup)</param-value>
	</init-param>
	<init-param>
		<param-name>syncGroups</param-name>
		<param-value>true</param-value>
	</init-param>
</filter>
Name Description Default
initGroups Expression that will return the group IDs for the users. All attributes passed along by the authenticating system will be available in the object attr attr.group
syncGroups When set to true, groups will be synchronized on every login request, not only when the user is created false

The example above is shown for the CASIntegrationFilter, but the same parameters can be applied to the HttpAuthFilter as well.

6.1 Restricting group assignments to nodes

In order to restrict group assignments to nodes/channels, the group IDs must be encoded like [groupId]|[nodeId]~[nodeId]~[nodeId]....

The following example shows the definition of the initial groups statically set to the group 17 (restricted to nodes 8 and 11) and the group 18 (not restricted to specific nodes):


	...
	<init-param>
		<param-name>initGroups</param-name>
		<param-value>["17|8~11", 18]</param-value>
	</init-param>
	...

7 Logout

A logout can be performed with the following request

POST /CNPortletapp/rest/auth/logout/{sid}

in the response, the session secret cookie is removed.