Thursday, October 6, 2016

Oracle JET and ADF BC REST Security Integration Approach

I have promised to atendee of my OOW'16 session (Building Enterprise-Grade Mobile Apps with Oracle JET and Cordova [CON5731]) to post a blog about ADF BC REST security and integration with Oracle JET. This post is to demonstrate how we could reuse cookie ID generated by ADF BC REST Web session for REST requests from JET.

First thing first, here you can download source code - This archive contains ADF BC REST application and JET source code (you need to copy it into your local JET application).

Take a look first into JET login form. This is where we collect username/password and call login function. One important detail - invalidComponentTracker, this allows to report required validation error, when user hits login button with empty username or password:

Here is the login function in JET. If there are no validation errors, it executes POST against ADF BC REST service custom method. In response we could return user info, preferences, etc. This is the only one request where we are using username/password. Key point of this request is to get JSESSIONID from ADF BC REST server, so we could use it for subsequent requests, without sending username/password again. This is similar concept to ADF Faces, it is also using JSESSIONID to track web user and HTTP session on the server. If login is successful, we are reading custom parameter from response with JSESSIONID value. JET router is updated to render different menu structure after login:

Custom response parameter is populated on the server in Filter class. On authentication request this parameter is set once:

ADF BC REST application is enabled with standard ADF Security:

This is how it works. Login form in JET:

Login is successful with redsam/welcome1 user. Two tabs are rendered - Home and People. Home tab displays chart with employees:

We should dive deeper and check what happens with REST communication. POST method in response gets custom parameter with JSESSIONID value, if authentication is successful based on Authorization header parameter:

Chart data in Home tab is retrieved through GET method and this method is not using Authorization header anymore. It calls REST method using JSESSIONID in URL. JESSIONID must be before URL parameters:

Home tab is implemented with JET chart component:

JSESSIONID is included into REST call URL through getURL method, which is referenced by JET collection:

People tab implements table with pagination support:

Same approach is applied in People tab. JSESSIONID is appended into URL through getURL method, before URL parameters:

People UI with paginated table in JET:

REST request URL contains JSESSIONID:


Saurabh Mehta said...

Thanks a lot. Was looking for it from long time.

Sanjeev Chauhan said...

Hi Andrejus, We also tried to implement similar scenario but not using Oracle JET/ADF BC. We have Angular/Rest-Jersey(J2EE). Conceptually both are same to rely on JSESSIONID for subsequent request.
I see few differences though
1. For us JSESSIONID goes in next request ownwards automatically as a request header. Browser should take care of sending sessionid from subsequent request as in first request it got it using set-cookie. Server side J2EE is smart enough to use request header JSESSIONID or Authorization (whichever is present) to invoke service. I assume jSessionId in url might be a requirement of ADF-BC REST services. Is it?

2. What if user calls login service and then again go to login page and to relogin? We faced issue that Authorization header goes as usual but browser also appends JSESSIONID in request-header. JSESSIONID in request header takes preference and server provides access to user if even if username/password is wrong in second attempt. To avoid it we have to remove JSESSIONID from request header. It can only be removed by server code as cookie is HttpOnly. At this time we are invoking logout service, which clear JSESSIONID header before doing login and then we send subsequent login service call. In future we are planning to do clear header from login service only and then do a auto redirection to login2 service, which will do actual login.

3. If user keeps on working on client side for 10-15 min without calling a server side api, server side session may end and next call to server side will not identify JSESSIONID. To overcome this we need to keep pinging server after 5 min or so to keep server session awake. Angular has concept of idletimeout and keepalive, which takes care of counting end user's idle and if he is not idle, keep pinging server side to keep server session running. Do we have anything in JET like that?

Just thought of sharing our findings and issue with you.


Andrejus Baranovskis said...

Hi Sanjeev,

Thanks for follow up.

1. ADF BC REST is nothing to do with JSESSIONID. This is Web app deployed on WLS. I assume it would work with request header. But I prefer to pass it through URL, this way there is better control in my opinion.

2. This is why I prefer to control JSSIONID and pass it through URL, there are no issues like that. You can simply clear up client side variable with JSESSIONID

3. In ADF apps we usually set Web session timeout long enough, lets say 8 hours. This allows to keep session as long as it is needed. On logout in JET will close session. If browser is closed without logout - we also close session, with WebSocket.


Sanjeev Chauhan said...

Thank you Andrejus for clarifying things. I believe your solution (url-rewriting) is better as it will work even if cookie is disabled and also you can handle url more effectively on client side as you said. We will try to change our solution in similar lines.


Andrejus Baranovskis said...

Sounds good. With REST it is easier to switch UI, so may be you also going to use JET in future :)