Appengine user service +GWT not logging out - google-app-engine

I have an appengine app with a GWT frontend. I am using appengine's user service to authenticate with google accounts. My problem is when logging out from the GWT frontend the user is not completely logged out. The user is shown the login page, however when you click to login again with google a google account, it goes straight to the app without going to the google login page. I am not using any custom login/pass fields here, strictly appengine user service.
I am guessing this has something to do with HTTP sessions and basic authentication, however I have not been able to log out entirely.
Here is the Login/out service on the server:
import javax.servlet.http.HttpSession;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;
#SuppressWarnings("serial")
public class LoginServiceImpl extends RemoteServiceServlet implements
LoginService {
public final static String CHANNEL_ID = "channel_id";
#Override
public UserAccountDTO getLoggedInUserDTO() {
UserAccountDTO userDTO;
HttpSession session = getThreadLocalRequest().getSession();
UserAccount u = LoginHelper.getLoggedInUser(session, null);
if (u == null)
return null;
userDTO = UserAccount.toDTO(u);
UserService userService = UserServiceFactory.getUserService();
userDTO.setLogoutURL(userService.createLogoutURL(requestUri));
return userDTO;
}
#Override
public void logout() throws NotLoggedInException {
getThreadLocalRequest().getSession().invalidate();
throw new NotLoggedInException("Logged out");
}
}
On the GWT client side I am using this code to logout:
Window.Location.assign(currentUserDTO.getLogoutURL());
When I click the logout link on my app (which runs the code above), nothing changes. However If I reload the page I am sent to my app's login page. When I click to login with my google account it goes straight into my app without asking for google credentials. This is tells me the user was logged out from my appengine app, however the the user is still somehow logged in to his google account in the browser (I'm assuming an auth token stored as a cookie?). I need to have my users completely logged out of there google account so the next visitor to the site is asked for google credentials.

Ideally to LogOut from GAE/Google I would use logOutUrl coming out of userService. For example
UserService userService = UserServiceFactory.getUserService();
logOutURL = userService.createLogoutURL(baseURL);
logOutURL is where I would redirect window to, to log out from Google
Also check a small servlet I have written to login and logout at: http://cloudspring-demo.appspot.com/html/csAuth.html
You can simply copy this servlet in appropriate servlet and after adding mapping in web.xml, you can simply invoke it to test out.

Related

How to determine if user has logged out on another page

I am running a Google App Engine project where everytime the user takes an action I want to check to see if the user is 1)logged in 2)an admin. This is the code I have for the appuser:
class AppUser
{
private UserService userService;
private User user;
public AppUser()
{
userService = UserServiceFactory.getUserService();
user = userService.getCurrentUser();
}
public IsAdministrator()
{
if(IsLoggedIn())
{
return userService.IsUserAdmin();
}
return false;
}
public IsLoggedIn()
{
return user == null;
}
}
When I log out with my app this works fine. However, if I log out on another page (like on google calendars or something) the app still thinks I'm logged in. Is there another better way to check if the user is still logged in?
Also I know that this can be done with security-constraint in the web.xml however that will not work in this case as I need to take certain actions if the user has logged off.
I am using App Engine SDK 1.7 and GWT SDK 2.4
Two ways to notify app about user logging out:
Synchronously - server actively notifies client (browser) about log-out. Use Channels API to send push notification to client. There is a GWT wrapper.
Asynchronously - server notifies client about log-out when client makes communication to server, i.e. in every RPC call add authentication check. If user id logged-out, raise an exception, which can be handled by GWT.
I ran into this today, though it was worse: I'd logged out as user A (from a Google Sites page), and logged in as user B, but my GAE app still thought I was logged in as user A. Argh.
The reason for this is that there are two cookies involved, one for tracking which Google user is logged into Google, and another for tracking which GAE application user is logged into my GAE application. Recall that a GAE could be using any federated authentication service, not just Google's. My application has no access to the google.com cookies, so I can't directly check whether user A is still logged in (or which user is currently logged in).
Unfortunately, I've not yet found a straight forward "federated logOUT" mechanism, though it is possible that Google Identity Toolkit can be used for detecting that the expected user is no longer logged in.
I found others discussing this issue:
How to manage multiple accounts login and logout in different browser pages?
UserService retrieves wrong user after logout
Update
I came up with a solution that works for my application, where I have a page that redirects the user, a student, to his or her classroom's home page. Since this is accessed infrequently by any one student (a few times a day), but which needs to know which student is logged in, I took the following approach which works for me:
User goes to page A, which clears the ACSID and SACSID cookies, and redirects to Google for the user to login.
User is probably already logged in, so Google (with several redirects) updates the ACSID and SACSID cookies to the currently logged in user, and redirects back to my application at page B.
Page B finally takes action on behalf of the logged in user, "confident" that the correct user is logged in (to the extent that pages are confident). ;-)
Here's a code sketch of the approach:
# My BaseHandler has a clear_cookie
class LoginAndRedirectHandler(base_handler.BaseHandler):
def get(self):
self.clear_cookie('ACSID')
self.clear_cookie('SACSID')
self.clear_cookie('dev_appserver_login')
if 'continue' in self.request.params and \
self.request.params['continue'].startswith('/'):
url = self.request.params['continue']
else:
# Whatever your page is that needs an up to date logged in user
url = users.create_login_url('/PageB')
if isinstance(url, unicode):
url = url.encode('utf8')
logging.info('Redirecting to ' + url)
self.redirect(url)
return
The reason I said infrequently above is that this process is expensive in time, with at least 4 or 5 redirects involved.

how to create password protected web pages in google app engine

I am developing a website using jsp-servlet in google app engine. I want to secure some web pages from all the users that visit my site. So is there any way to password protect my web pages. I know it is easily done by htaccess in apache. Can htaccess work in google app engine? if Yes, please specify the process.
You can take advantage of the App Engine Users API. This allows users to log in to your app using their Google account. If you want to control who can get into what parts of your app, you could check the logged-in user's ID against a list of allowed users in your data store when they make a request to your servlet.
Edit:
You're not going to find a method exactly like using .htaccess files -- that's just not how App Engine works. You have code in your servlets that are responsible for rendering pages. In this code, you'll need to add a check to see if the user has access, but only for the pages that you'd like to check.
Here's a code sample, which I hope might clarify things. This is a slightly-modified version of the code at the link I sent you.
public class MySecretServlet extends HttpServlet {
public void doGet(HttpServletRequest req, HttpServletResponse resp)
throws IOException {
UserService userService = UserServiceFactory.getUserService();
resp.setContentType("text/html");
if (req.getPathInfo().equals("/secret_page") {
if (req.getUserPrincipal() != null &&
req.getUserPrincipal().getUserId().equals("admin-id")) {
// render your protected page here
} else {
resp.getWriter().println("<p>Please <a href=\"" +
userService.createLoginURL(thisURL) +
"\">sign in</a>.</p>");
}
} else {
// render your unprotected content here
}
}
}
Alternatively, you can use the security constraint features in your web.xml file. You can find the documentation for those here. This is less flexible, though, as you can only change access rights between "everyone" and "admin-only".

Authentication on Google App Engine (web page)

I would like to require the user to use a login/password when accessing some parts of my GAE (Google App Engine) application. I want to limit the access to the set of predefined users, e.g. on the page, where new data can be uploaded to the Datastore.
I found there are 3 ways on GAE how this can be solved, but it looks like only the last one (the most complicated) could be a solution for my issue:
Use a deployment descriptor (<security-constraint> in web.xml). This is not nice, since those users have access, even as viewers to the GAE application's admin page, where they can see the billing history, browse Datastore, logs, etc. and this is something I have to avoid.
Use the federated login (Open ID) (https://developers.google.com/appengine/articles/openid) and allow users to use their Google, Yahoo! or other identities to login. It is not a solution, since I want to limit the access to a small set of users (max. 5) and not allow everybody to use the application.
The other option is to create simple custom login page accessible only via HTTPS and let the user send the username & password in a POST request (it can be plain since we have secure https connection) to a servlet, on the servlet generate some session identifier with specified validity and make it part of every subsequent request. It also needs to check each time the user sends a GET or POST request if the session identifier exists and does not expire meanwhile manually.
Any other/better proposals on how to maintain administrator accounts? Can HttpSession help with the last variant?
Kind Regards,
STeN
I suggest the standard Google login page. Use something like this in you authentication controller (Java + Jersey Framework which is of course not necessary):
#Path("/login")
public Response login(#Context UriInfo ui) throws Exception {
UserService userService = UserServiceFactory.getUserService();
com.google.appengine.api.users.User user = userService.getCurrentUser();
Response response;
if (user == null) {
URI uri = new URI(userService.createLoginURL(ui.getBaseUri().toString()));
response = Response.seeOther(uri).build();
} else {
URI uri = new URI("/");
response = Response.seeOther(uri).build();
}
return response;
}
#GET
#Path("/logout")
public Response logout(#Context UriInfo ui) throws Exception {
UserService userService = UserServiceFactory.getUserService();
com.google.appengine.api.users.User user = userService.getCurrentUser();
Response response;
if (user == null) {
URI uri = new URI("/");
response = Response.seeOther(uri).build();
} else {
URI uri = new URI(userService.createLogoutURL(ui.getBaseUri().toString()));
response = Response.seeOther(uri).build();
}
return response;
}
The login method redirects you app to the Google login page if the user is missin (essentially not logged in). The logout method will logout the user.
Hope this helps
I use a combination of 2 and 3. I allow all users to login, but then I limit actions to particular e-mail addresses. These could be hard coded or (better) in the datastore and memcache (so that you don't have to query the datastore on every request). You could also cache this data in static variables in Java if you want to - just be aware that you might need to kill instances off manually if you change the users who have access. If, like me, you rarely / never change access then this shouldn't be a problem.
Allowing all users to login really gives them no access in my app - they see the admin pages but they're empty apart from a message saying "You do not have access to any of these administrative options".
Some notes:
AFAIK your assumption is not correct. Login to application has nothing to do with admin pages permission. You need to explicitly add users via "Permissions" page for them to have access to admin pages.
You can still check user properties (email) after user logs in with OpenID and deny them access.
This is of course doable. The natural way to track users are sessions. Google for examples.
In both cases 2. ' 3. it's advisable to have a servlet filter that checks session if there is user logged in and deny access (return 404) if user is not logged in.
Pay attention to the 3rd solution: instead of passing username & password my webapp asks username & apiSecret (generated automatically at the first login), so you can invalidate (and re-generate) quickly the apiSecret if something goes wrong.
There is another option: OAuth (https://developers.google.com/appengine/docs/java/oauth/).
Here is my piece of code (UserAccount is my class to represent a User; User is "com.google.appengine.api.users.User", retrieveUser(..) is the function to retrieve my UserAccount from a logged "User"):
public UserAccount getUserLogged(HttpServletRequest request) {
try {
User loggedUser = oauthService.getCurrentUser();
if(loggedUser!=null) {
return super.userAB.retrieveUser(loggedUser);
}
} catch (OAuthRequestException e) {
return null;
}
return null;
}

"The page you requested is invalid" when trying to sign into Google account in Google App Engine

I have an application deployed on GAE. I use JSF for my pages. I use Google Account API in order to have my users log-in.
The code for login URL creation is:
HttpServletRequest req = (HttpServletRequest)FacesContext.getCurrentInstance().getExternalContext().getRequest();
UserService userService = UserServiceFactory.getUserService();
String loginURL = userService.createLoginURL(req.getRequestURI());
The loginURL that the code creates is:
https://www.google.com/accounts/ServiceLogin?service=ah&passive=true&continue=https://appengine.google.com/_ah/conflogin%3Fcontinue%3D<my application home JSF page>&ltmpl=gm&shdf=ChALEgZhaG5hbWUaBFRlc3QMEgJhaCIUG4bRrGRUVeCNGKu07NKfR4J9Wp8oATIUhxCkjIdiyeVwHWveU9q2Qm1jhqc
Most of the time it works ok but in some scenarios (e.g. logout from all my Google Account logged in pages, close the browser, open it again and try login) I don't reach the sign-in page but rather an error page with the message: "The page you requested is invalid".
Any idea?

How to force a user to re-enter login password using PAPE extension with max_auth_age=0

I am using OpenID with GAE (Experimental - Federated Login). I don't
get prompted for user/password when I hit logout and then hit login
back again. Can someone please let me know what the problem is?
I use the following code to create my login url's:
UserService userService = UserServiceFactory.getUserService();
HashSet<String> attributeRequestSet = new HashSet<String>();
attributeRequestSet.add("openid.ns.pape=http://specs.openid.net/
extensions/pape/1.0");
attributeRequestSet.add("openid.pape.max_auth_age=0");
String loginUrl = userService.createLoginURL(destinationUrl,
"gmail.com", "gmail.com", attributeRequestSet);
and code for my logout url's:
UserService userService = UserServiceFactory.getUserService();
User user = userService.getCurrentUser();
String logoutUrl = userService.createLogoutURL(destinationUrl,
user.getAuthDomain())
As long as you are logged in to your Google account it will not request you to input username/password.
If you logout of your app and then login again, you are redirected to Google auth page, but since you are still logged into Google it just immediately redirects you back to your app.
The OpenID feature you are requesting (openid.pape.max_auth_age=0) is not yet supported: see Unsupported Features

Resources