socialauth - can you use same session across a redirect? - jsf

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).

Related

What is the safest method to make session?

So I have few things to say I don't want to use cookies so things like express-session doesn't come as option.
I use nodejs with express with no front-end JavaScript and mysql as database. I don't really know how to do it so I would like to hear your opinion.
I already tried to search on internet.
When dealing with regular web pages, there are only four places in a request to store information that would identify a session.
Cookie sent with each request
Custom header on each request
Query parameter with each request
In the path of the URL
You've ruled out the cookie.
The custom header could work for programmatic requests and is regularly used by Javascript code with various types of tokens. But, if you need a web browser to maintain or send the session on its own, then custom headers are out too.
That leaves query parameters or in the path of the URL. These both have the same issues. You would create a sessionID and then attach something like ?sessionID=92347987 to every single request that your web page makes to your server. There are some server-side frameworks that do sessions this way (most have been retired in favor of cookies). This has all sorts of issues (which is why it isn't used very often any more). Here are some of the downsides:
You have to dynamically generate every single link in a web page so that it will include the right sessionID as part of the link so if the user clicks on it, the resulting http request will have the right sessionID included.
All browser caching has to be disabled or bypassed because you don't want the browser to use cached web pages that might contain the wrong sessionID.
User bookmarks basically don't work because they end up bookmarking a URL with a sessionID in it that won't last forever.
The user sees sessionID=xxxx in all their URLs.
Network infrastructure that log the URLs of requests will include the sessionID (because it's in the URL). This is considered a security risk.
All that said and with those tradeoffs, it can be made to work, but it is not considered the "safest" way to do it.

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

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

Access without Logging in

Im using GWT, GAE to make a web app.
I looked at a bunch of tutorials regarding implementing a login system but most of those tutorials implement it so it's mandatory to login to access the web app. How would I go about making it so that anyone can access the app but if they want to use account specific functionality, they they have the option of signing up for an account.
There are two parts to it.
First, in your client code you check if a user is logged in. If so, you allow access to the "closed" parts of the app. If not, you show a link/button to login and hide tabs/views that are accessible to authorized users.
Second, in your server code you specify which requests do not require authentication and which do require it. This is necessary if a user somehow figures out how to send a request without using your client code.
For example, in my code some requests have checkSession() called at the very beginning. If no authentication object is found for this user in session, this method throws LoginException to the client. If the authentication object is present, the request continues to execute normally and returns requested data to the client.
Further to Andrei's answer, if you want a framework to manage the sessions for you, you can use GWT-Platform, which has an excellent Gatekeeper feature.
I use it for mine and I have a LoggedInGatekeeper class. Simply add #UseGatekeeper(LoggedInGatekeeper.class) to the constructor of each presenter proxy and it checks if the user is logged in. If you want anyone to be able to access that page simply annotate with #NoGatekeeper. Easy!
It takes a bit of setting up but it's a great MVP framework. There are maven archetypes and samples etc.
Hope this helps.

How to probe for authorized access on the client side

So far I have mainly been using a single asp.net app metaphor where the razor pages are served together with the data from the same app, so I can protect certain controller actions for the ui and data controller actions using the same security.
My new app has a completely independent api web site (using servicestack) and a different asp.net UI app that consumes the api. Right now the two apps sit on the same server, but I want to support the ability for anybody to write a UI app that consumes my data, so the app could sit anywhere and could be a php app all I care.
The new UI uses razor and MVC but it is really a fully client side app, which requests data from the api site.
So, the problem is right there. I am used to automatically redirecting a page from the server side to the login when someone hasn't logged in yet. My UI's server side has no concept of the login situation of the api web site.
The best I can do right now is, at the begging of ANY UI page's load, to do a lightweight ajax call to the api web site, to get the current user info. If it's null, I do a client side document.location.href = .
This works, but has issues, primarily it causes a lot of client side busy ui stuff. An initial version of the UI loads empty (no data), then an awkward redirect to the login page happens. The client side app becomes chatty - every page that loads does an additional call to the api site. The server side redirect is clean because the first page you see is either the UI page that you already have access to or the login page.
My first question is, what is the best practice to do this kind of stuff? My second question is, is there a client side cookie on my UI page that deterministically tells me I am logged in to the api site? I inspected the cookies on a UI page before and after the login that sets the security to the api site and the cookies seem to be the same. My third question is - is there some kind of security actionfilter I can write for my UI mvc site, which somehow magically determines from the cookies of the request, whether the UI is currently logged in to the api site and if it is, it lets the request serve the page, if not, it does the sever side redirect to the login page.
Thanks
Edit:
Scott - thanks so much for the detailed explanation. One clarification - i use mvc only to really serve my client side html. I use all knockoutjs and Ajax calls to servicestack to render the data. My question is really about how to react to Ajax calls that return security exceptions and how to avoid presenting an empty html ui because the user is not logged in. My login page authenticates directly from html to ss bypassing the mvc part. It's not an spa where I can keep a single client side login state that applies to all views of the spa. There are multiple cshtml pages that all need to probe for login in order to not load empty and redirect to the login page...
So the MVC just serves the blank template, that includes the knockout js that will call the API to populate it? I believe this flow shows how your current pages are testing for a session using a lightweight ajax call to the api.
The problem with that approach as you have noted is that it has overhead, and a noticeable delay if there isn't a session.
Solution:
You can't test for the ss-id cookie in your JavaScript client application because of the origin difference. Knowing this cookie exists would give you an indication of whether a user might have a valid session. But seeing you can't access it, you have to work around this. When your login page successfully creates a session by calling the API, you should have the success method create a cookie that denotes that you have a session. Such as a hasSession cookie.
You can check for this existence of this cookie on each page load. It doesn't involve a server trip to verify it. If that cookie has the same expiration policy as the ServiceStack API cookie, then it should stay in sync.
The initial cshtml page state should hide the unpopulated page form contact using CSS, and show a loading indicator until the data is loaded from the API.
When the page first loads it should check if the hasSession cookie exists? If it doesn't then it shouldn't make any API calls, and should redirect immediately login.
How would I know that I can invoke ajax calls and succeed without making a test call?
You should just assume you have a session ss-id cookie if you have the hasSession cookie as you must have logged in successfully to get it. So make you call for the page data. If you get data back from the call and not a 401 exception then populate the form, and display it by altering the CSS.
If you got a 401 redirect to the login screen, and delete the hasSession cookie. The user won't have seen a blank unpopulated form because the CSS prevented this. They get a loading indicator while waiting, a perfectly reasonable state.
The 401 Authorization error should only occur once, and redirect to login, and that shouldn't even happen if your hasSession and the ss-id cookie expiration remain in sync.
I am just confused why you are trying to change the ServiceStack attributes now, subclassing [Authorize]. You shouldn't need to change the behaviour of the API.

j_security_check not redirecting to welcome page - successful login event listener?

For ages I've been puzzled about why after login I sometimes don't directed to the application welcome page. I've finally figured it out (years after everyone else):
I login successfully via j_security_check and go to the welcome page
wait for session timeout
click on h:link which sends a GET request
because it's a GET and not a POST my custom ViewExpiredException
handler doesn't kick in
container security redirects to the login page because the session
has timed out. Because of the session timeout+container security the
get request (from h:link) isn't seen by the application, in either a phase listener
or filter.
I successfully login again
j_security_check redirects me to the page which triggered the
authentication, in this case the target of the GET request.
The last bit I'd not understood, I assumed it would always go to the welcome page.
My problem is that my current design requires that after login I always show the welcome page. The welcome page has a preRenderView event which sets up some context information in a session scoped bean after login and increments a few counters etc...
This context information is required by backing bean code for other pages, and presently if I don't go through the welcome page first there'll be an exception.
In terms of fixing it I've looked at the following options:
Ideally there'd be an #PostLogin method that could be called, which would cleanly solve all my problems. I use JSF (Mojarra) with Myfaces CODI but I don't see anything which does what I want.
I could add some more code to my filter, but I need to persist some data (i.e. login count), it doesn't look like a nice option. Maybe I'm wrong.
I make all the preRenderView methods of potential targets of j_security_check (pages called with GET) handle the case where they are called directly from j_seecurity_check. I can see this being what I have to do but it seems like a lot of hassle.
Write a Server Authentication Module for glassfish to override j_security_check behavior.
How is this normally handled? I've started hitting this problem after moving to GETs for simple navigation cases after years of abusing POSTs, and the custom exception handler doesn't work. If anyone has any guidance on this issue I'd appreciate it, at least I know what's going on now. Hopefully I've missed something obvious!
Thanks
O/S
Ideally there'd be an #PostLogin method that could be called, which would cleanly solve all my problems. I use JSF (Mojarra) with Myfaces CODI but I don't see anything which does what I want.
There is indeed no such thing.
I could add some more code to my filter, but I need to persist some data (i.e. login count), it doesn't look like a nice option. Maybe I'm wrong.
That would indeed be the "easiest" way. Basically:
UserPrincipal user = request.getUserPrincipal();
HttpSession session = request.getSession();
if (user != null && session.getAttribute("user") == null) {
session.setAttribute("user", user);
// First-time login. You can do your intercepting thing here.
response.sendRedirect(request.getContextPath() + "/welcome.xhtml");
}
I make all the preRenderView methods of potential targets of j_security_check (pages called with GET) handle the case where they are called directly from j_seecurity_check. I can see this being what I have to do but it seems like a lot of hassle.
That's not DRY.
Write a Server Authentication Module for glassfish to override j_security_check behavior.
Can't answer that as I've never done that.

Resources