The IBM Worklight platform allows developers to implement HTTP adapters - an easy and convenient way of integrating with HTTP based backends. In this blog I will explore the different ways adapter can communicate with HTTP backend.
The chart below outlines a basic architecture of invoking an HTTP adapter procedure from a client application, getting data from enterprise HTTP backend and returning it back to the client app.
Understanding the process details and roles allows to gain a deeper level of control of whether backend connectivity should be stateful or stateless and even automatically propagate client's user identity all the way to the backend (in case required).
The crucial pre-requisite for understanding this blog is familiarity with what HTTP session is and how does it work. In a nutshell - HTTP session is something that allows server to recognize clients it is in process of working with.
1. Every time client request reaches the server the latter checks the received request for a sessionId cookie
2. In case sessionId cookie is not present (or present, but invalid, e.g. expired) the server creates a new HTTP session, meaning allocates a segment of memory to store information related to this specific client.
3. Once HTTP session is created (a memory segment is allocated) it receives a unique identifier called sessionId. The sessionId is returned to the client in a cookie.
4. Next time client makes request to the server it sends the sessionId cookie received in #3
5. Server retrieves sessionId cookie from request and looks it up in the list of currently allocated sessions
6. In case corresponding session is found - it is used by the server
7. In case not - go to #2.
Now, lets go back to our backend invocation flow and try to investigate all the roles involved in each step of the process. An important thing should be noted before we do a deep dive - “client” doesn’t always mean mobile application. Client is someone who initiates request. Similarly “server” doesn’t always mean IBM Worklight Server. Server is someone who receives request and returns response. In order to prevent confusion I’ll refer to the traffic between mobile application and IBM Worklight server as segment A and to the traffic between IBM Worklight server (to be exact - HTTP adapter) and HTTP backend as segment B.
First time a mobile application sends request to the Worklight Server an HTTP session is established (see HTTP session A on a chart below). In a context of this HTTP session the mobile application is considered a “client", since it is the one who initiated the request, and the IBM Worklight server is considered a “server”, since it is the one who will serve this client.
Remember - session state is something saved on a server side while the sessionId cookie associated with this session is reported and stored on a client. Now let’s add segment B to the picture.
When IBM Worklight server (HTTP adapter) sends request to the HTTP backend it basically becomes its client. Backend is unaware of mobile applications out there. From its perspective - it’s serving the IBM Worklight server (HTTP adapter). A whole different HTTP session will be established on a backend with a whole different sessionId cookie. So basically, IBM Worklight server in this scenario operates as a server for mobile applications (segment A) and as a client for HTTP backends (segment B).
If we look at the real world scenario - numerous instances of mobile applications are connecting to the IBM Worklight server simultaneously. Each one of them has it own dedicated session on IBM Worklight server with unique sessionId. As explained previously the sessionIds are used to identify the relevant session on a server side.
Now to the interesting part. By default HTTP adapter procedures operate in a so called connectAs=“server” mode. What this means is that IBM Worklight server to HTTP backend connectivity (segment B) is considered server-to-server, thus a single shared instance of HTTP client is used per adapter for all mobile application instances. As a result a single HTTP session is created between IBM Worklight server and HTTP backend.
Note that each adapter is a black box for other adapters. This means that in case you have several adapters they will never share HTTP session information between them (even if they’re communicating to the same HTTP backend).
Single HTTP session means that regardless of which mobile application instance has sent the request (on segment A) that caused adapter to communicate to a backend (on segment B), the same HTTP session for segment B will be reused. The advantages of this approach is that the load on a HTTP backend is much lower. It doesn’t have to maintain multiple HTTP sessions and spend precious resources on managing session states for different clients. The HTTP backend in this case operates in a so called stateless mode - it doesn’t have to maintain session states, all the communications are performed via single shared session. However since a single shared session is used to get information for all mobile application instances the backend needs some way of differentiating between users. Consider the following scenario - several banking application instances (with different users) simultaneously invoked getListOfAccounts adapter procedure. Adapter will make several requests to the backend to retrieve list of accounts for each user. Backend works in stateless mode, it doesn’t remember anything from previous requests of each of the mobile application instances. Therefore identity of the users invoking getListOfAccounts procedure needs to be reported to this backend in every single request. Usually it is done as a part of SOAP message envelope but any other similar mechanism can be used (e.g. custom HTTP header). The bottom line - a single HTTP session in segment B is used for all HTTP sessions in segment A. HTTP protocol mechanisms, such as sessionId cookie, are not used to differentiate clients and developer is responsible to report user identities to backend according to the backend specifications/requirements.
While this approach works flawlessly in many scenarios it might be insufficient in cases where the backend requires a stateful connection. In such cases backend relies on a sessionId cookie for being able to uniquely differentiate users. Working in a connectAs=“server” mode with this type of backend can be somewhat dangerous. Consider user1 logging in using his mobile application instance. Suddenly all other mobile application instances are logged in with the same user1 credentials. Why? Because during user1 login process an HTTP session was established on segment B and the stateful backend relies on it to identify user. Since all other mobile application instances are also getting data through the same session - backend will treat all of them as if they’re all user1’s mobile application instance.
In order to support stateful backend HTTP adapter procedures can be configured to work in a connectAs=“endUser” mode. This mode means that a separate instance of HTTP session will be created for each client session on segment A.
This way every session on segment A will have a dedicated HTTP session on segment B to communicate with backend (same as noted above - all of this is relevant as long as we’re talking about procedures of the same adapter. Regardless of the configuration different adapters will never use the same HTTP session)
Since connectAs property can be defined per procedure you might want to consider the case where part of your procedures use connectAs=“server” and other part connectAs=“endUser”. In this case all procedures marked as connectAs=“endUser” will use separate HTTP sessions in segment B per every HTTP session on segment A and all procedures marked as connectAs=“server” will use the same shared HTTP session in segment B for all HTTP sessions on segment A. Using this approach you can reduce the number of HTTP clients spawned by HTTP adapter and, therefore, the number of HTTP sessions created on a backend. Of course it is developers responsibility to know which backend services are stateful and which are stateless.
Now lets see how we can take connectAs=“endUser” one step further. Besides telling Worklight server to use separate HTTP sessions this property can do one more thing - it can propagate user identity all the way from mobile application to enterprise backend. This is why using connectAs=“endUser” has one hard requirement - even if automatic identity propagation is not being actively used the user still must be authenticated in user identity realm protecting the procedure in order for it to run in connectAs=“endUser” mode. In order for automatic identity propagation to work following requirements must be met:
Backend should be protected by one of the following authentication mechanisms - HTTP Basic, HTTP Digest, SPNEGO, NTLM
Since all of the above authentication types require username and password the realm protecting adapter procedure must use a login module extending from from UsernamePasswordLoginModule. This will make sure that relevant credentials are available
There's no additional configuration required to enable automatic identity propagation. In case HTTP backend returns an HTTP 401 response (requesting to authenticate) the Worklight adapter will automatically extract end user credentials from a previously created user identity and try to make requests again using these credentials.
Lets see a sample project implementing some of the above topics. The sample will be based on a simple adapter authentication flow, if you’re not familiar with how adapter authentication works please review the relevant tutorial at IBM Worklight Getting Started page before you continue.
First of all I will create a dummy “stateful" backend. It will consist of two servlets. Authentication servlet will get username and password from request parameters and validate them.
The rule of validation is username equals password. In case credentials are successfully validated - backend returns HTTP 200 status (and 403 otherwise). Note that this is something completely custom that I’ve chosen for simplicity, backend can use any other mechanism for reporting credentials validation success or failure.
Next component of the backend is an Accounts Servlet. It will return some info regarding user session and fake list of accounts
As you can see, accounts list is hardcoded, however sessionId and session creation time will be taken out of server session context.
Next part is Worklight project. For the sake of clarity and simplicity I’ve created two separate adapters - AuthenticationAdapter and BackendAdapter. AuthenticationAdapter will handle authentication, BackendAdapter will handle sending requests to the backend. Note that AuthenticationAdapter will not directly invoke backend in order to validate received credentials, it will delegate this to the BackendAdapter instead.
The application will be very simple too. It will have a single Get Accounts List button that will invoke backend adapter’s getAccountsList() procedure which will go to the Account Servlet on the backend and retrieve data (including sessionId and session creation time as shown above). All of this info will be displayed.
Now, if I start my app with adapter procedures working in a default connectAs=“server” mode, click the Get accounts list button and authenticate I will get the following picture:
Even though I’m using three different browsers (Chrome, Firefox, Safari) which have NOTHING in common and I’ve logged in with different credentials I’m still getting the same sessionId and session creation time. This is, as explained above, because of the same HTTP session being reused across all application instances. Now I configure my BackendAdapter procedures to work in a connectAs=“endUser” mode and redeploy the adapter.
Keep in mind that, as explained above, once I want my procedures to work in connectAs=“endUser” mode I’m also responsible for user being authenticated (active user identity to be set). The security test that protects my procedures has one of the realms defined as userIdentity realm, so it is my responsibility as a developer to make sure that there’s an active user identity for this realm prior to invoking BackendAdapter procedures. I do it in AuthenticationAdapter:
Note that I’m setting active user identity prior to invoking BackendAdapter and nullifying it in case credentials validation has failed. Once I've implemented the above and restart my application instances I see a whole different picture
This time I have a different HTTP session in every one of my app instances (even if I want to use the same set of credentials).
Feel free to examine the attached sample in order to get a better understanding how the whole thing works.