April 10, 2020
Estimated Post Reading Time ~

Sling Authentication

User authentication is the core of any application; it can be a desktop application, a web application or a web service. CQ is a tool that is mainly used for building component-based web sites and provides content management capabilities. The underlying frameworks Apache Felix and Sling together provide the foundation for CQ’s authentication capabilities.

Let’s explore the web authentication in brief and then I’ll try to explain the CQ’s authentication in detail. Authentication for web applications mainly works on request/response headers, sessions, and cookies.

Request/response-based authentication: When a page is requested from a web server, the server looks for some specific information in the header (to be specific in request “Authorization” header) and if the information is available in the header then that information is used by the server to validate a user. So Authorization header can contain the credentials in Basic form or Digested form. The basic authentication header has the following format:

Authorization: Basic username:password (encrypted in Base64 encoding)

There are few other Basic schemes that are used by some sites, some examples are:
a) User/Password scheme
Authorization: user fred:mypassword
b) Kerberos
Authorization: kerberos kerberosauthenticationsparameters

Once the authorization header information is received by the server, it’s the responsibility of the server to decode the credentials and validate it based on the Authorization scheme. If the request is authenticated on the server then a 200 (OK) response is sent back to the browser and subsequent request uses the same header information for validating user requests. If a request can not be authenticated on the server because of invalid information in Authorization header, the server sends back a 401/Unauthorized response and the user is present a login form to enter username/password.

Path-based restriction for header authorization: Let’s say we have a web application that is structured as /a/b/c/ and /a/d/c/. When a user is trying to access a resource that is under /a/b/c path, they need to enter a valid user name and password so that server can validate the provided information and will cache it in the request Authorization header. Now, the user is authenticated to access any resource under /a/b/c but, when user will try to access anything under /a/d/c/ the authorization information in the header (cached for /a/b/c/ path) will not be used by the server and a 401/Unautorized response is sent back to the browser (and a form will be presented to the user where user can enter user name and password). So authorization headers are path specific.

Request header-based authorization does not deal with cookies or session, the authorization information is cached by browsers and is available till the browser is not closed or a user does not forcibly clear the header information (active logins)

NOTE: Authorization header is a request header (and NOT a response header). We can (and should) not explicitly set this header.

Session-based authentication: When a resource is requested from the server a unique id (called as a session ID) is generated by the server and is maintained on the server (something like JSESSIONID=053d59e1-3398-cd4a-8a12-61d371f9f120). For any subsequent requests server checks that whether it already contains a session ID for the received request or not? If a session ID is already there then the same session ID will be used. We can store arbitrary data/objects in the session. When the user provides a valid user name and password then the application validates it and store some information in session to indicate that the user has already provided credentials and is authorized to access resources on the server. If a user has not requested any resource for more than a timeout setting value from the server then that user is treated as inactive and his/her session (and data in it) is destroyed. Almost all servers have a setting for timeout value that can be configured based on the requirement to invalidate sessions. The main problem that we have with session is that sometimes it becomes difficult to replicate user session (on different servers) in clustered environment and authentication does not work properly but, its a deployment issue and there are various ways to fix it (like a sticky session at load balancer level, etc.)

Cookie-based authentication: Cookies are a small piece of data that is stored on the user’s machine. Again the format of cookies is very simple, they are just key-value pairs (the value can be plain or encrypted, it depends on how do we sent it to browser). When a user requests some resource on the server, the browser sends cookies in the request header and on server-side applications extract the required information from cookies. Some sites use cookies for authentication and store an encrypted value in cookies that are used by application/server to identify a user. For security reasons many users disable cookies on their browser so, an application that is completely dependent on cookie-based authentication will not work (if cookies are disabled). Also, it is worth noting that the size of the cookie is restricted to a max of 4K size.

Before actually implementing any of the available authentication scheme/mechanism it is important to consider the following things:
1) Who are our end users? Do they need to provide credentials for accessing resources on a server?
2) What kind of application we are developing? What APIs and framework we are using?
3) Is it a secure site and the credentials should be encrypted before transmitting over the wire?
4) What kind of deployment environment we have in production? Is it a clustered environment with a load balancer? Do we have a session replication mechanism in place if we are planning to deploy the same application on multiple servers?

Based on the type of content security, available resource, and deployment environment we need to judge which combination fits well with our needs.

CQ’s/Sling’s Authentication
We just explored the various ways of authenticating a user on the server now let’s explore the CQ’s authentication implementation and how to develop our own site using CQ’s underlying frameworks (Sling and Felix). Sling is a REST-based web framework and Felix is an OSGI specification implementation and the authentication in CQ works little differently as compared to traditional applications. In the traditional application, we normally have a controller (at the entry point of our application) that validates user but, in CQ a request goes through a chain of authenticators exposed as OSGI services (CQ’s own authenticator and any custom authenticator that we have) for validating the user.

Also, as I have mentioned in my earlier posts that CQ uses JCR (CRX) for storing data (files, images, etc.) and user’s information. If a user wants to access something on the server (basically from JCR) then either they need to provide credentials (if the requested resource is secured using ACL) or they can access it directly if the resource does not have any ACL (access control list restrictions). So there are basically two types of users we can have in JCR:

a) Anonymous: an anonymous user can only access unsecured/public resources and does not require any credentials.
b) Registered: If a user wants to access secured/private resources on the server then they must be registered on JCR and need to provide their credentials. JCR validates the provided credentials against the user account data/information stored in the JCR repository and creates a JCR session (note that it is not an HTTP session) and a resource resolver. I’ll cover the session and resource resolver later in this post.

Following are few main interfaces/classes that we need to explore for understanding Sling’s authentication framework:
1) Authenticator (Interface)
This is an interface that defines basic login() and logout() methods that are implemented by an actual Authenticator class (e.g. SlingAuthenticator). What and how to implement login and logout functionality is the complete responsibility of class that implements this interface.

2) AuthenticationSupport (Interface): This interface defines handleSecurity() method which must be implemented in a class that is exposed as an Authenticator. Sling framework internally calls the handleSecurity() method for extracting the user credentials and returns a Boolean flag depending upon whether user validation is successful or failed.

3) SlingAuthenticator (class): This class implements Authenticator and AuthenticationSupport interfaces (see label 1 in below diagram) and is registered as an OSGI service with Felix. This class is the foundation for Authentication in Sling, apart from implementing the methods from Authenticator and AuthenticationSupport class this class holds a mapping between request paths and corresponding authentication handlers (a class that implements AuthenticationHandler interface) that will be used for authenticating a request. When a user request for a resource from server, sling authenticator extracts the request path from request and it’ll try to find whether there is an authentication handler that is mapped for the path (see label 2 & 4 in below diagram), if an authentication handler is mapped for the requested path then the authentication control is delegated to the authentication handler class.

4) AuthenticationHandler (Interface): This interface defines extractCredentials(), requestCredentials() and dropCredentials() methods (see label 5 in below diagram) that must be implemented by an Authentication Handler implementation class that we need to register/map as authentication handler with SlingAuthenticator service. Once Sling authentication has selected an authentication handler based on request path and deligated authentication job to a particular authentication handler implementation class then, the following methods are called:

a. extractCredentials(): Based on the authentication scheme that we want to implement (Authorization header-based authentication, session-based authentication or cookie-based authentication) the implementation class should implement this method to extract credentials from (header, session or cookies) and must one of the following:
i. AuthenticationInfo: If authentication is successful then this method must return an AuthenticationInfo object (that holds the authentication scheme used, user name and password) to SlingAuthenticator. If valid AuthenticationInfo object is returned by this method then the sling authenticator will NOT execute any other Authentication Handler that is in the list after the current authentication handler.
ii. Null: If authentication failed then this method must return null. When a null value is returned from this method, the sling authenticator will try the next authentication handler that is mapped for this path (if any).
iii. AuthenticationInfo.DOING_AUTH: if authentication is failed or because of some reason we want to redirect the request to some other page (that can be a login page or any other resource) then this method must return DOING_AUTH to sling authenticator. When the sling authenticator receives a DOING_AUTH as return value then it stops there and a request is suspended.
b. requestCredentials(): An authentication handler that implements an AuthenticationHandler interface must implement this method for rendering a login form. If validation fails and sling framework wants to ask for a user name and password in that case sling authenticator will call this method and users will be presented a login form to enter the user name and password.
c. dropCredentials(): An authentication handler that implements the AuthenticationHandler interface should implement this method to removed user credentials (from either authorization header, session or cookies). When a user logs out, the sling authenticator calls this method to remove/clean user credentials.

Once all authentication handler has been executed and no one responded back with a valid credential (AuthenticationInfo), then the sling authenticator will try to see if the anonymous users can access the requested resource (see label 3 in the below diagram).

5) AbstractHTTPAuthHandler (abstract class): This is an abstract class that has basic authentication handler implementation. This class implements a Basic Authorization scheme with base 64 encodings of username and password. This class has basic implementation of extractCredentials(), requestCredentials() and dropCredentials() methods (see label 6 & 7 in below in diagram) and provides an abstract method getLoginPage() that can be implemented by child classes to return a custom login page.

6) HTTPAuthHandler (class): This class extends AbstractHTTPAuthHandler and implements Basic Authorization (see label 9 & 10 in below diagram) and CUG support) and is mapped for root path (/) i.e. by default this is the authentication handler that will be called by Sling Authenticator.


UML representation of Sling/CQ Authentication Implementation

 

The AuthenticationInfo class

For more information on sling authentication, you can refer http://sling.apache.org/site/authentication-framework.html

Source: http://suryakand-shinde.blogspot.com/2011/04/sling-authentication.html



By aem4beginner

No comments:

Post a Comment

If you have any doubts or questions, please let us know.