Call REST API with jQuery from JSF page and get current session without cookies - jsf

I have a web application which uses JSF 2.3 for the front end and offers also an API which is consumed by the front end with Jersey 2.29.1 running on a Tomcat 9 server.
The front end offers a login which will store the authenticated user in a #SessionScoped AuthenticationBean. The API methods should only be consumable if the user is logged in on the front end.
#Path("/service")
public class ApiService {
#GET
#Path("/data")
#Produces(MediaType.APPLICATION_JSON)
public String loadData() {
final AuthenticationBean authBean = CDI.current().select(AuthenticationBean.class).get();
if (authBean != null && authBean.isLoggedIn()) {
// Do business logic
}
else {
throw new WebApplicationException(Response.Status.UNAUTHORIZED);
}
}
}
On the JSF page the API is consumed with a 'basic' jQuery AJAX call $.ajax() and updates some visual content based on the JSON response. The URL which I pass to the AJAX call is "#{request.contextPath}/api/service/data".
Everything works fine as expected, until I disable the cookies in the browser. If the cookies are disabled, the session ID is added to the URL (instead of being stored in a session cookie) by the servlet engine. So if I do not explicitly append the session ID to the URL for the AJAX call I have no access to the current session in the API service method, e.g. "#{request.contextPath}/api/service/data;jsessionid=#{session.id}" and therefore I can not check whether the user is logged in or not.
My question is now if I do have to add the jsessionid manually for every AJAX request or is there any other 'clean' possibility to pass the session ID to the service methods? Or do I have to access the session in any other way in the API service?

... explicitly append the session ID to the URL ...
The HttpServletResponse#encodeURL() does exactly that task, see also javadoc (emphasis mine):
Encodes the specified URL by including the session ID, or, if encoding is not needed, returns the URL unchanged. The implementation of this method includes the logic to determine whether the session ID needs to be encoded in the URL. For example, if the browser supports cookies, or session tracking is turned off, URL encoding is unnecessary.
So, basically:
#{request.contextPath}#{response.encodeURL('/api/service/data')}
The same method is by the way delegated by ExternalContext#encodeResourceURL().
Return the input URL, after performing any rewriting needed to ensure that it will correctly identify an addressable resource in the current application.
Jakarta Servlet: This must be the value returned by the javax.servlet.http.HttpServletResponse method encodeURL(url).
So, technically you can also do
#{request.contextPath}#{facesContext.externalContext.encodeResourceURL('/api/service/data')}
But this is a bit less convenient to type down. Moreover, using #{request.contextPath} already indicates that you're using JSF on top of servlets, not portlets, so using #{response} should be perfectly fine. You might want to make it yet shorter by defining a custom utility method in an application scoped bean. E.g.
#{functions.encodeURL('/api/service/data')}
#Named #ApplicationScoped
public class Functions {
public String encodeURL(String uri) {
ExternalContext ec = FacesContext.getCurrentInstance().getExternalContext();
return ec.getRequestContextPath() + ec.encodeResourceURL(uri);
}
}

This 'Answer' is not an answer to your real question but an explanation on why this is in no way JSF related (at the end there is a hint though to an answer)
JSF is an api and Jersey is an implementation of an api (JAX-RS). They effectively are complementary technologies and have no relation to one another in any way. It's the same like asking can I use JPA and Jersey in one application. Your actual questions have like stated no relation to JSF whatsover. JSF uses the session mechanism provided by the servlet engine, just like jax-rs does.
The second thing that is unclear is what your definition of 'front-end' is and where you run what. JSF in the JavaEE 'stack' is a front-end technology. JSF has a server side part in which you declare the components(See What is the definition of "component" in JSF) and when the html is generated they have client side html/javascript/css counterparts which communicate with the server in a way that is specified in the JSF specs. JSF 'components' by themselves do nothing if not backed by 'glue logic' on the server that in turn calls services (See JSF Controller, Service and DAO)
It might by now be clear that
is it possible for a JSF component to have called a Jersey service by an AJAX call?
is 'vague' and most likely given by your lack of knowledge of what the JSF is/does and how Jersey fits in (Jersey should be 'rest' here or jax-rs if you mean the api), See also How to implement JAX-RS RESTful service in JSF framework
JSF Components' html part on the client side communicates with the server in a JSF specific way, so
Rest is not needed there to get the client-side to communicate with the server (superfluous)
Trying to get rest in there makes things overly (overly) complex and without a real advantage
If you are trying to get the JSF server side components to communicate via REST
The server side is for interaction with the client side by means of the JSF spec for it
They are backed by code that in turn calls services. In this you can call a rest service, database or whatever
If, like I suspect, you want a non-jsf related $.ajax to call some rest service
You totally can, but read How to implement JAX-RS RESTful service in JSF framework
You can do anything with it to update the client side html
You can even update the html generated by JSF components
When doing 3, do NOT expect them to still work al the time, JSF is not to blame here
So after all this, your session id problem is not a jsf related problem. You'd have the same problem when using plain jsp or even plain html that has triggered a session.
HINT
So a better title would have been "jsessionid in url not added to ajax call in jquery" I posted this one in a search engine and added site:stackoverflow.com: jsessionid in url not added to ajax call in jquery site:stackoverflow.com
One of the results is effectively a duplicate of your question:
Appending my jsession ID at every ajax call by Jquery

Related

JSF Security: bean method accessibilty

I have a basic question about JSF ManagedBeans for which I can't find a answer.
Suppose I have a bean MyBean with two methods method1 and method2 and a JSF page with a command link
<h:commandLink action="#{myBean.method1}">
</h:commandLink>
Is it possible for someone to analyse the source code of the page and call method2 instead of method1?
Answer
No, this is not possible by design.
Reasoning
Technically the client can only tell the server "The user clicked a html element with a certain id".
This event is then processed by JSF on the server-side, the component with the corresponding id is looked up and in this case the method "#{myBean.method1}" is executed.
As you can see, the client can not[!] tell the server what to do with this event.
Sources
JSF 2.2 Spec - 3.4 Event and Listener Model
Caveat
JSF is stateful and there are multiple ways to hold this state. The default is to hold state information server-side (e.g. in the users HttpSession).
Another option is to transfer (encrypted) state to and from the client. This is still conceptionally secure, but there *might* be bugs with client side state saving. Such a bug *could* be exploitable to do something like you described.
Yes, it is always possible to modify code (or markup-language) on the client-side. Your "action" will be called through some forms and/or Javascript-Methods - everything visible to experienced users.
But that's not an issue of JSF-2 only - this applies for every language which allows insights from the client side.
You shouldn't apply "security through obscurity" (https://en.wikipedia.org/wiki/Security_through_obscurity) but rather make sure, that you can handle this on the server-side.
If a user, who has access to two urls modifies url1 to url2 - that's fine, why not? (Could be bookmarked) - But YOU should take care of the modified request, if he is not allowed to access url2.

Is there a way to get the Base URL from an Application-scoped bean in JSF?

In my JSF Facelets pages I know I can just use
#{request.requestURL}
To get the full URL to build exportable links and the like. However I have an #ApplicationScoped bean where I need the base URL (i.e. "http://dnsname/app") for the purposes of reports and other data. Is there a standard way to access the web-app container to get this information?
Not until the first HTTP request is fired. The domain/host/base is namely configured completely independently from the web application (on the appserver and/or proxy server, if any). If it's not an option to lazily set it as an application wide variable during the 1st HTTP request in e.g. a servlet request listener or a servlet filter or perhaps in a lazy loading getter of that application scoped bean, then you'd need to configure it externally on the web application via e.g. system property, VM argument, properties file, etc.

POS payment from backing bean in JSF

I want to implement a POST call to a virtual POS for card payment proceessing. The POS is simply a server program given by the bank, and must be accesses through a form with the transaction's data. This can be done by composing a form, method post, and a set of hidden params, and then targeting the output to a new window (which is required, because from this point, the bank software takes control).
But, for security issues, I want to make the POST call from a backing bean.
Thus:
A button in my facelet calls a method in a RequestScoped bean. This bean has access to the transaction's data in session.
The bean makes some stuff.
Inside the method, I can open an URLConnection, add the params, and then call the bank's program (I assume it's a servlet).
Whatever the output of this servlet, it must be redirected to a new window in the client side. This new window will contain the authentication and real payment stuff, but it's the bank's software.
Which would be the correct pattern to implement that behavior by using JSF?
Thanks in advance.
I think I understand now, you want to integrate the bank servlet functionality in your JSF application but do not want to bother with PCI compliance.
A redirect is an HTTP server response code that specifies a url to perform a GET on so you can't redirect the response of a form POST.
You also don't want to programmatically pass this POST request to the bank servlet from the server side and render the received response because you have already initiated a seperate session with bank servlet than the session exists between the user and your web application. That is actually a serious security concern.
You can't start this Bank Servlet session through JSF unless the server is handling all HTTP communication with the bank servlet.
The way I would do this under your scenario is to use Javascript to open a new window with a regular HTML form, and set the action of the form to the bank servlet URL, and have the initial inputs of that form match the required parameter names for the HTTP post. When this HTML form is submitted, the SSL certificate is negotiated with the users browser and the POST response will be rendered without any involvement of your web application whatsoever. The client will maintain two seperate sessions, the session for your application as well as the session for the Bank Servlet.
This is the secure way to do this that doesn't put any PCI compliance requirements on your web application.

Accessing FacesContext in servlet

I'm working on a JSF (v1.2) application. In my application I need a generic servlet which could serve any resource (PDF, Images, Excel, etc). My idea is to ask the caller to send the required information so that I can find out the correct delegator class using some configurations.
This delegator class will take care of serving the correct resource.
For example this is the request url
http://example.com/servlet?delegatorid=abcd
My Servlet code is something like this.
protected void doGet(HttpServletRequest request, HttpServletResponse response){
String delegatorID=request.getParameter("delegatorid");
//Get the configuration from Configuration table
configuration=getConfiguration(delegatorID);
//invoke the method of the delegator class based on this configuration
Object result=invokeMethod(configuration);
//write the response to the stream
}
My question is what is the best way to do this in a JSF project?
Should I completely avoid JSF dependency in this operation? I can find the delegator method and class and invoke it using reflection. Will there be any potential restrictions in future if I avoid JSF dependency. [One problem which I can think about is, in one of the code, I need to get the user information from session. I'm doing this through FacesContext. Since FacesContext is not available, it will fail, I should have another option to get the session.
If I have to introduce JSF dependency, how do I get the FacesContext
here? As far as I know, only the beans that are stored in
application scope can be accessed here. I don't want to do that. Is there any other way of getting it?
Instead of using a servlet, can I do this by invoking a ManagedBean
method directly using the URL? This will give me FacesContext. I
think I need to have a dummy JSP page for the managed bean method to
get invoked.
Could you please let me your thoughts on this?
The FacesContext (and ExternalContext) is just a facade over HttpServletRequest, HttpServletResponse, HttpSession, ServletContext, etcetara along with some JSF specifics which you don't need at all in a plain vanilla servlet. The ExternalContext#getSessionMap() is nothing more than an abstract mapping of HttpSession#get/setAttribute().
In a plain vanilla servlet, the session is just available by request.getSession() and the application by getServletContext() the usual way. See also among others this related question: Get JSF managed bean by name in any Servlet related class.
You can also just refactor code which needs to be shared by JSF and Servlet into an utility method which doesn't have any dependencies on javax.faces.* nor javax.servlet.* classes (or at most only javax.servlet.*) and finally let the callers each pass the necessary information through.

socialauth - can you use same session across a redirect?

I'm trying to use socialauth to login with google, facebook et al (I'll assume google here) and have a question about how it works. I'm using JSF 2 without Seam. The basic idea is that you:
make a few API calls indicating that you want to login with google.
make another API call which returns a URL for google.
supply a result URL which will be used by google to redirect back to your site.
redirect to the google URL.
then google will either immediately redirect back to your site or first ask for login details.
My confusion is about linking together the data from the outbound and inbound sides. In the getting started page (linked above) they suggest this:
Outbound
SocialAuthManager manager = new SocialAuthManager();
String successUrl = "http://my.domain.com/socialauthd/successAction.xhtml";
String url = manager.getAuthenticationUrl(id, successUrl);
// Store in session
session.setAttribute("authManager", manager);
Inbound
// get the auth provider manager from session
SocialAuthManager manager = (SocialAuthManager)session.getAttribute("authManager");
The problem I have is that I don't see how this can work, and it doesn't in testing. They suggest storing a reference to an instance of SocialAuthManager in the session, however when the request is received from google a new session is created. It doesn't have the JSESSIONID cookie and so isn't part of the session that sent the request to google in the first place.
To work around this I got a unique per-request id from the socialauth api (openid.assoc_handle - it's sent as a query param), put it in a concurrentHashMap in an app scoped bean, and retrieve the reference in a preRenderView listener in the completion page (successUrl - badly named in the example as it is called either way).
This all seems like a lot of hassle for something that isn't included in the documentation. I've tried this with #RequestScoped CDI beans, although I usually use CODI #ViewAccessScoped. With CODI I've tried adding the windowId to the success URL, and also adding the JSESSIONID cookie to the redirect, but neither approaches work. I don't think the bean scope is relevant but the more information the better.
I could dive into the spring, seam and struts examples but for a pure EE 6 developer it's a lot of overhead, and with a better understanding of this issue I can produce a simple, working, JSF only example which I will make available to the socialauth team for use on google code.
Am I missing something obvious or does this just have to be complicated? and if so why did they document an approach that simply doesn't work?
Edit: I think that the successUrl may be named more appropriately than I thought, because in testing with Yahoo I realise that you won't be redirected back to your own site unless correct login details are provided. I expect this is the same for all providers. I have added some comments regarding this solution to the socialauth site, and also to an issue I logged about this problem (neither of which have received any response from anyone involved in the socialauth project).
Include the jsessionid path parameter in the callback URL.
String successUrl = "http://my.domain.com/socialauthd/successAction.xhtml"
+ ";jsessionid=" + session.getId();
Note that this is not specific to JSF API, but to Servlet API (chapter 7.1.3, URL rewriting).

Resources