User authentication with webapp2 on Google App Engine

Google App Engine for Python ships with the capability to manage user accounts without the need of any additional library. This functionality is, however, insufficiently documented. This post will be structured as a step-by-step tutorial addressing user registration, login, password reset and a few other details.

The webapp2 framework on Google App Engine for Python 2.7 is definitely a step forward from the original webapp.
Despite the increase in flexibility and functionality, however, there are a few items that are still more laborious than in other frameworks. The most notable aspect is user account management.

Unsurprisingly, since it is meant to run on Google’s App Engine, using Google Accounts with webapp2 takes one line of code. OpenID authentication, while still defined experimental, is almost trivial to implement as well. There are some open source projects like SimpleAuth that attempt to offer a standard and unified API to handle signing in with Google, OAuth and OpenID accounts.

While it generally makes sense to offer support for authentication through popular services – it decreases friction for new users to try a service – in some cases users may prefer having a special login to access your application.

As experience teaches us, managing passwords securely is not a trivial task, and users legitimately expect application developers to take all the necessary measures to protect their passwords.

Since this is a use case that has to be considered countless time, there is significant value in using library functions to handle user accounts.

Here is how to do that using the functionalities embedded in the webapp2_extras package that is distributed with all standard installations of App Engine for Python 2.7.

Basics and prerequisites

Our main interface when dealing with authentication is webapp2_extras.auth. This module leverages the rest of webapp2’s infrastructure to offer us a simpler way to manage user authentication.

In particular, it relies on

  • webapp2_extras.security to handle password hashing (so that passwords are never stored in clear text) and random string generation;
  • webapp2_extras.sessions to identify requests coming from the same user as part of a client-server conversation.

Since we are implementing our own user database, we will need to define a custom model class to represent users in our application. The auth framework described here works under the assumption that this model class defines the following instance method:

  • get_id(self)

and the following class methods:

  • get_by_auth_token(cls, user_id, token)
  • get_by_auth_password(cls, auth_id, password)
  • create_auth_token(cls, user_id)
  • delete_auth_token(cls, user_id, token)

The specific role of each of those methods is described here.

In addition to that, if you are interested in using the code for password reset provided as part of this tutorial, you will also need to implement the following class method:

get_by_auth_token(cls, user_id, token, subject='auth')

For each request, our application will then be able to do the following:

  1. read a cookie to figure out whether the current belongs to an existing session,
  2. if a cookie is found, read an authentication from it and load the corresponding session (if present) from the session store backend,
  3. loads some cached user information from the session.

We will be able to tune some details of the process via configuration options.

Extending the default User model

webapp2 already contains a reference User model for Google App Engine that uses NDB for storage. If you are willing to use that, you will find it good enough for most of the needs you may have.
In particular,

  • it is an Expando model – it can include properties that were not specified as part of the class definition but are added at run-time, so it will not be a problem if your application needs to store some specific user information. New properties are indexed by default, so queries should still be fast.
  • it allows you to set uniqueness constraints – while NDB does not support unique properties by default, webapp2’s User model uses a custom mechanism to allow to allow that.

Oddly enough – since the User class is clearly well designed – the reference implementation does not offer any method to update the password for a given user once it has been set. Because of that, it is a good idea to extend the User model to add a
set_password method that will have the responsibility to securely hash passwords using the security module.
You can find an example User model implementation below: it will contain the password setter and the get_by_auth_token class method (modeled after the one already provided by webapp2) that has been introduced in the previous section.

import time
import webapp2_extras.appengine.auth.models

from google.appengine.ext import ndb

from webapp2_extras import security

class User(webapp2_extras.appengine.auth.models.User):
  def set_password(self, raw_password):
    """Sets the password for the current user

    :param raw_password:
        The raw password which will be hashed and stored
    """
    self.password = security.generate_password_hash(raw_password, length=12)

  @classmethod
  def get_by_auth_token(cls, user_id, token, subject='auth'):
    """Returns a user object based on a user ID and token.

    :param user_id:
        The user_id of the requesting user.
    :param token:
        The token string to be verified.
    :returns:
        A tuple ``(User, timestamp)``, with a user object and
        the token timestamp, or ``(None, None)`` if both were not found.
    """
    token_key = cls.token_model.get_key(user_id, subject, token)
    user_key = ndb.Key(cls, user_id)
    # Use get_multi() to save a RPC call.
    valid_token, user = ndb.get_multi([token_key, user_key])
    if valid_token and user:
        timestamp = int(time.mktime(valid_token.created.timetuple()))
        return user, timestamp

    return None, None

Setting up the configuration

With what we have seen so far, we are now able to configure our application. We will need to set some properties as follows:

config = {
  'webapp2_extras.auth': {
    'user_model': 'models.User',
    'user_attributes': ['name']
  },
  'webapp2_extras.sessions': {
    'secret_key': 'YOUR_SECRET_KEY'
  }
}

In particular,

  • user_model is the name of the custom User model class we described earlier,
  • user_attributes is a list of attributes in the User model that will be cached in the session. Ideally, frequently accessed properties should be stored here. The full User model will be accessible by querying the datastore.
  • secret_key is the key used to secure the hash signature calculation for session cookies.

Creating a base handler class

Before writing the actual handlers that will implement the business logic to sign up and authenticate users we will group some utility functions in a base handler class, which will be extended by all the following handler classes.

This will ensure that all handlers will inherit a set of useful utility functions and properties to access user data and infrastructure classes, but also ensures that all session data is properly saved on each request (see dispatch).

class BaseHandler(webapp2.RequestHandler):
  @webapp2.cached_property
  def auth(self):
    """Shortcut to access the auth instance as a property."""
    return auth.get_auth()

  @webapp2.cached_property
  def user_info(self):
    """Shortcut to access a subset of the user attributes that are stored
    in the session.

    The list of attributes to store in the session is specified in
      config['webapp2_extras.auth']['user_attributes'].
    :returns
      A dictionary with most user information
    """
    return self.auth.get_user_by_session()

  @webapp2.cached_property
  def user(self):
    """Shortcut to access the current logged in user.

    Unlike user_info, it fetches information from the persistence layer and
    returns an instance of the underlying model.

    :returns
      The instance of the user model associated to the logged in user.
    """
    u = self.user_info
    return self.user_model.get_by_id(u['user_id']) if u else None

  @webapp2.cached_property
  def user_model(self):
    """Returns the implementation of the user model.

    It is consistent with config['webapp2_extras.auth']['user_model'], if set.
    """    
    return self.auth.store.user_model

  @webapp2.cached_property
  def session(self):
      """Shortcut to access the current session."""
      return self.session_store.get_session(backend="datastore")

  def render_template(self, view_filename, params={}):
    user = self.user_info
    params['user'] = user
    path = os.path.join(os.path.dirname(__file__), 'views', view_filename)
    self.response.out.write(template.render(path, params))

  def display_message(self, message):
    """Utility function to display a template with a simple message."""
    params = {
      'message': message
    }
    self.render_template('message.html', params)

  # this is needed for webapp2 sessions to work
  def dispatch(self):
      # Get a session store for this request.
      self.session_store = sessions.get_store(request=self.request)

      try:
          # Dispatch the request.
          webapp2.RequestHandler.dispatch(self)
      finally:
          # Save all sessions.
          self.session_store.save_sessions(self.response)

Note – You may want redefine the view-related methods to something different if you use a different templating engine.

Registration: create new users

If we are using the model class discussed in the previous sections, in order to create a new User we just need to call the create_user method.

class SignupHandler(BaseHandler):
  def get(self):
    self.render_template('signup.html')

  def post(self):
    user_name = self.request.get('username')
    email = self.request.get('email')
    name = self.request.get('name')
    password = self.request.get('password')
    last_name = self.request.get('lastname')

    unique_properties = ['email_address']
    user_data = self.user_model.create_user(user_name,
      unique_properties,
      email_address=email, name=name, password_raw=password,
      last_name=last_name, verified=False)
    if not user_data[0]: #user_data is a tuple
      self.display_message('Unable to create user for email %s because of \
        duplicate keys %s' % (user_name, user_data[1]))
      return

    user = user_data[1]
    user_id = user.get_id()

    token = self.user_model.create_signup_token(user_id)

    verification_url = self.uri_for('verification', type='v', user_id=user_id,
      signup_token=token, _full=True)

    msg = 'Send an email to user in order to verify their address. \
          They will be able to do so by visiting  <a href="{url}">{url}</a>'

    self.display_message(msg.format(url=verification_url))

create_user will accept the following parameters:

  • an authentication id: the token (such as a username or an email address) users will use to identify themselves when trying to access our application. While users can have multiple authentication ids, only one is allowed at creation.
  • a list of unique properties (in this case, email_address): if this is specified, webapp2 will not allow us to create new users if others share the same values for the properties in this list.
  • name/value pairs that are going to be set as properties of the resulting User model. If you want to add your own fields, this where to do it. If a parameter called password_raw is present, it will be assumed to be the user password that will be used for authentication; webapp2 will hash it and store the hash the password field: we do not want to store passwords in clear text, do we?

We also create a signup token and associate it to the newly created account: we will use to confirm the email address that has been provided during registration.

Note that we are accessing the model as self.user_model rather than calling it directly, so that we are free to change which implementation to use by updating the application configuration.

Login and logout

If users are created as in the previous session, login is quite simple: the get_user_by_password method can be used to retrieve a user by their credentials. In addition to the user credentials, the method accepts some additional parameters. The one we care about (and the only one we use here) is remember: when set to True, the cookie used to identify the session is saved as persistent and the browser will keep it even after the user will close its window.

class LoginHandler(BaseHandler):
  def get(self):
    self._serve_page()

  def post(self):
    username = self.request.get('username')
    password = self.request.get('password')
    try:
      u = self.auth.get_user_by_password(username, password, remember=True)
      self.redirect(self.uri_for('home'))
    except (InvalidAuthIdError, InvalidPasswordError) as e:
      logging.info('Login failed for user %s because of %s', username, type(e))
      self._serve_page(True)

  def _serve_page(self, failed=False):
    username = self.request.get('username')
    params = {
      'username': username,
      'failed': failed
    }
    self.render_template('login.html', params)

The implementation above renders the login form when the request comes via GET and processes the credentials upon POST. When authentication fails it renders the login form and passes the username to the template so that the corresponding field can be pre-filled.

Implementing logout is even simpler: it is sufficient to get rid of the user session.

class LogoutHandler(BaseHandler):
  def get(self):
    self.auth.unset_session()
    self.redirect(self.uri_for('home'))

Email confirmation and password reset

Signup tokens are one of the undocumented features of webapp2 (and they may possibly subject to change), but they can be quite handy when implementing a flow to confirming email addresses or recover passwords.

As mentioned before, the webapp2 uses authentication tokens to identify users after they logged in: they are meant to be securely shared by a client and the server and exchanged when a client needs to prove its identity.

As you probably imagine, this mechanism can be generalized to handle email confirmations and password resets: when websites send us an activation link after a registration, the URL usually contain their equivalent of signup tokens.

webapp2 sets a subject property for each of the tokens it generates, so the only difference between auth token and signup token is the value for that property. So, why do we want to use signup tokens?

Setting a different value for that property allows us to partition tokens by their purpose: we can then implement useful features as deleting all the password reset tokens that have not been used in 48 hours.

Here is a sample verification handler that is able to process email verification links:

class VerificationHandler(BaseHandler):
  def get(self, *args, **kwargs):
    user = None
    user_id = kwargs['user_id']
    signup_token = kwargs['signup_token']
    verification_type = kwargs['type']

    # it should be something more concise like
    # self.auth.get_user_by_token(user_id, signup_token
    # unfortunately the auth interface does not (yet) allow to manipulate
    # signup tokens concisely
    user, ts = self.user_model.get_by_auth_token(int(user_id), signup_token,
      'signup')

    if not user:
      logging.info('Could not find any user with id "%s" signup token "%s"',
        user_id, signup_token)
      self.abort(404)

    # store user data in the session
    self.auth.set_session(self.auth.store.user_to_dict(user), remember=True)

    if verification_type == 'v':
      # remove signup token, we don't want users to come back with an old link
      self.user_model.delete_signup_token(user.get_id(), signup_token)

      if not user.verified:
        user.verified = True
        user.put()

      self.display_message('User email address has been verified.')
      return
    elif verification_type == 'p':
      # supply user to the page
      params = {
        'user': user,
        'token': signup_token
      }
      self.render_template('resetpassword.html', params)
    else:
      logging.info('verification type not supported')
      self.abort(404)

This handler is meant to be used with a route using a template that matches URLs like /v/USERID-TOKEN. You can configure it as follows (please refer to the sample code at the end of this article for the full routes configuration):

webapp2.Route('/<type:v|p>/<user_id:\d+>-<signup_token:.+>',
  handler=VerificationHandler, name='verification')

Two minor notes on this item:

  1. For increased security, we may require users to enter their password before authenticating them.
  2. Ideally, we may want to use a different subject for email confirmation and password reset tokens.

Ensure users are logged in

Now that everything else is in place, we can decide whether users are allowed to access certain resources depending on their logged in state.
The following decorator can be used to annotate handler methods that require users to be logged in.

def user_required(handler):
  """
    Decorator that checks if there's a user associated with the current session.
    Will also fail if there's no session present.
  """
  def check_login(self, *args, **kwargs):
    auth = self.auth
    if not auth.get_user_by_session():
      self.redirect(self.uri_for('login'), abort=True)
    else:
      return handler(self, *args, **kwargs)

  return check_login

Just placing @user_required, as in the following example, before those methods will ensure that anonymous users will be directed to a login page when attempting to go through the annotated handler method.

class AuthenticatedHandler(BaseHandler):
  @user_required
  def get(self):
    self.render_template('authenticated.html')

Finishing touches

Before actually using this in code in production, there is at least one task we should take care of: calls that send passwords (like login, signup, password reset) should be using https.

This is quite easy to do and the documentation is quite straightforward: just follow the instructions here and you will be all set.

Your app.yaml file should include the following once you are done:

handlers:
- url: /signup
  script: main.app
  secure: always

- url: /login
  script: main.app
  secure: always

- url: /forgot
  script: main.app
  secure: always

- url: .*
  script: main.app

libraries:
- name: webapp2
  version: "2.5.1"

Of course, we will also need some views to be able to use this application. While this is the typical task that is left as an exercise to the reader, the example implementation you will find in the following section will contain a fully working application you can play with.

Reference code

You can find a ready to use application skeleton on GitHub. Feel free to play with it to experiment the full flow described in this post, use it to bootstrap your project and to improve on it. Just post a comment if you have any question or suggestion.


If you are interested in learning more on App Engine, you may want to check out Programming Google App Engine (Kindle edition)

About these ads

115 thoughts on “User authentication with webapp2 on Google App Engine

    • Hi,
      Can you please make a tutorial for
      1. custom authentication using cloud endpoints as well.
      2. CSRF prevention with the above tutorial.
      P.S This tutorial is great. It’s very helpful indeed.

  1. hi! I’m new with GAE and I created a app with webapp2 authentication but when a update it to production don’t work and I don’t have any idea what the problem, I think that a need to setup something in my acount but I’m not sure :(

  2. Many thanks. Very helpful post.

    Maybe you want to mention to first-time users of webapp2 to add it to the libraries in app.yaml, for example:
    libraries:
    – name: webapp2
    version: 2.5.1

  3. My Mistake – You have BaseHandler in your GitHub code.

    One thing I’m considering is setting auth_fail_action: unauthorized
    in app.yaml. Then setting a custom error handler.
    app.error_handlers[401] = handle_401

  4. Has anyone had an issue with this where login/logout actions won’t take effect until either python is restarted, a file is edited, or the dev server is restarted? Seems to be some sort of caching that I can’t find.

    Any help would be appreciated, thanks.

  5. I figured out my problem with the values not changing, it was in my own code in the custom render function I had made.

    Thanks for this write-up, it is a huge help!

  6. Thank you all for the comments.
    Robert, I did remove the link to browse users, as there is no reliable way to get that information without hardcoding it.

    Also, if you actually perform the app config change you mentioned might consider sending a pull request with that.

  7. I’m not using HTTPS, (apart from for when they enter their password).
    That means tokens can be stolen. A hacker will be able to access most of the website and most of a users account, however to perform important operations, they will be redirected to https://*.appspot.com and need to re-enter their password.
    Not being secure isn’t ideal but saves 9$ a month on SSL certificates – what do you think?

  8. The webapp2 implementation of authentication, even the one based on username and password, relies on cookie storing session. this session is either a store itself or a key to the server-side store (depeding on the session “backend” option).
    So auth_token is never passed to the client and client is not expected to pass it back to be able to access resources.

    What if I was wringing an API based service, similar, for example, to app.net (ADN).
    The clients to my API are not browsers – but mobile apps.
    My understanding is that if I used webapp2 auth, the client apps would have to simulate browser, that is storing secure cookie as if it was a token.
    ADN authentication is based on returning token explicityly in the body of the response, and having client submit the token either in Bearer header, query string or body of the POST request. I would love to do the same. The implementation however would not use any of the goodness bestowed upon us by webapp2.

    My questions
    1. Did I describe the situation correctly?
    2. What would you do if you were writing API only servce?

  9. Hi Michael,
    yes, you did describe the situation perfectly.

    I believe Google Cloud Endpoints would be the perfect solution for your need as they would handle both exposing an API on the server side and generating code for the client side. Endpoints support authenticated calls by leveraging OAuth, which would be compatible with your needs.

    On the other hand, as the documentation states,

    Experimental!
    Google Cloud Endpoints is an experimental, innovative, and rapidly changing new feature for Google App Engine. Unfortunately, being on the bleeding edge means that we may make backwards-incompatible changes to Google Cloud Endpoints. We will inform the community when this feature is no longer experimental.

    So if you go with that, you might need to make some changes on your app while the API evolves.

    A more stable alternative (at least in the short term) that would require a bit more work would be to implement an OAuth provider in your server application (perhaps using an existing library like appengine_oauth_provider).

    Does this answer your question?

  10. Thanks Alessandro
    I would like to use GAE. I would love to have an example. Do you have a simple app like that? There is gae-boilerplate but they use session. I shall check out your suggestions.

  11. Unfortunately I do not have any example available, but I think the library I had linked above does not depend on Django: it is based on one that used to be, but the maintainer had replaced the django calls with the GAE equivalents.

    Another alternative worth checking out is this overview of OAuth for Python.

  12. I have managed somehow to make it work with webapp2 User, Auth and Session.
    Session store is still used to keep token. Session cookie is ignored, I don’t know how to avoid it being sent. Token is extracted from the header. Once this project’s rush time is over in about one month I hope to distill what I did into a clear example

  13. Hi Alessandro,

    First, thank you very much for this great work…

    It would be great if you can create an “admin” page where you can see all the accounts and the ability to remove them if necessary.

    Again, congratulations, and I hope you will consider my comments.

    Regards,
    Vincent

    PS : Sorry for my english

  14. Great article, thanks.

    In my application time to time users can not login immediately after registration.
    I’m calling self.auth.get_user_by_password(email, password) after model.User.create_user(email, password_raw=password).
    Time to time I got InvalidAuthIdError.

    You’ve never experienced this?

    • I encountered the same problem as Dmitry. What I did is.. copy a line of LoginHandler code at the end of SignupHandler, hoping it will auto log the user in after registration..

      user_id = properties.get_id()
      user = self.auth.get_user_by_password(username, password)
      self.redirect(‘/feeds’, abort=True)

      And I got InvalidAuthIdError… I tried to trace back and found get_by_auth_id() function does not find the user…even I pass the correct auth_id, which is the username..

      The second try is to use self.auth.set_session() function, to store user info in the session. But in my feeds function (@user_required), it still couldn’t find any user info…

      Do you know why is it so? Or do you mind giving an example about auto logged-in immediately after registration scenario…

      Nevertheless, this is a fantastic tutorial! Thanks a lot!

  15. Thank you folks,
    Michael, I am not sure you can prevent the cookie from being sent, once you have it. If you’ll share the example once you have it, I might try having a look.
    Vincent, I did not create an admin view because I tried to keep this project a skeleton that people could build upon. If you don’t believe AppEngine datastore admin is enough for your purposes and want more, you can fork the project and contribute an admin section.
    Dmitry, I have never experienced that issue: can you find anything relevant in the logs?

  16. First of all thanks for this great article. I’ve followed it and also changed some bits to fit my page.

    I’ve run into a problem when I try to let users change its properties (name, surnames,…).

    My problem is that user_info is a cached property so, when I update a user values, old values keep showing on my page. I can check through the admin console that the values have changed on db, but the values on user_info are the old ones.

    I’ve tried several things (like removing user_info to force to be calculated again), but the problem remains. I guess that user_info is calculated using another cached property which stills use old values.

    Any tip on solving this issue?

    Thanks in advance

  17. hi has anyone being able to query all users from the datastore? I want to check for information of User (SELECT * FROM User) i had an error of Kind not found User

  18. I’ve found the solution to my problem. I guess I wasn’t on my best time.

    If anybody has problems updating user data on a session, here is how I solved it. Having updated user data on ‘data':

    currdata = self.auth.get_session_data(pop=True)
    currdata.update(data)
    self.auth.set_session(currdata, token=currdata['token'], token_ts=currdata['token_ts'],
    cache_ts=currdata['cache_ts'], remember=currdata['remember'])

    @Kelvin, I guess that if you have this kind of error is because you have not created (and put) any instance of User

  19. Great job Otger!

    Kelvin, it would be a lot easier to help you if you could share a snippet of the code you are using. Maybe you can try posting the question on StackOverflow or pasting it in a gist?

    Robert, I have been playing with Endpoints and they are definitely interesting. One thing you may want to explore, however, is that if you rely on cookies for authentication, you may have trouble if you try to authenticate clients that do not run in the browser (e.g. mobile apps)

  20. This is great, thank you. Perhaps I’m missing something but where are you inserting the config options that you mention? In which file? Is this part of the model, or in the .yaml file?

  21. One minor thing. If you are gonna use https, the url for this connection should be mapping before the regular expression “.*” in the yaml.xml.

    Otherwise, when you enter a url, this always match first with this reg expression and the https will never be applied for the connection.

  22. I want to send the auth token and validate on the server side and get the response as valid or not valid through JQUERY AJAX. Please let me know how to implement on the server side.
    I have put your code under accounts.example.com and now i want to allow download at http://www.example.com by validating the auth token through javascript. please let me know how to do it.

  23. Thx for this great post.
    You also found out the best way to check if a user is admin that conforms to the standard app engine accounts? Is it enough to override the is_current_user_admin() function?

    • That would work, but bear in mind that admin status is managed outside the scope of the code in this article: admins have to be signed in with their Google accounts too, for this to work.

  24. Hi bahgat,
    first of all thanks a alot for posting this.
    But Bahgat,
    could you please let me know why filter function is not working with you code even i tried by creating one more entity class Agent_Details(db.Model) but inside this also filter function is not working and apart from this how can i retrieve all attribute store in User attribute.

    Please reply for the same ASAP because i was trying these operation since one week but due to this problems am not able to proceed.

    • Hello Rishi,
      it is very hard to help you without seeing your code. However, what filter function are you having issues with?

      In general, I would suggest you to ask questions like this on stackoverflow.com: there is an active community of users there who might be able to help you on a timely fashion.

  25. Great stuff.
    Thanks for spending the time to put this together. I can’t even imagine how many hours I would have wasted getting going with this stuff if I didn’t find your blog. I’m pleased to say I know have my site working with both Google accounts and proprietary accounts based mostly on the implementation you have here.
    I have a similar question to tschundeee above. I want to continue to allow app.yaml to dictate whether or not people can get to pages based on whether or not they are logged in. This seems to have fallen out when people use a non-google account to login. Any thoughts on how to get this login_required in app.yaml to recognize when people are logged in?
    i.e.:
    – url: /powerusers/.*
    script: somescript.app
    login: required

    Thanks!

    • Thank you Dave,
      I am glad this helped, that is one of the reasons why I wrote this article in the first place :).

      As you already figured out, the login: required option works with the built-in authentication options that GAE provides you with.
      If you still want to handle that with both Google accounts and your own internal accounts, you can perhaps consider altering the behavior of the user_required to verify that the user is logged in with a Google account.

  26. Hi Alessandro, thanks for this great example, i learned a lot with it in this world of GAE.

    I understand almost all the code and i make it work big part of it, but when i call the “user_info()” method i get this error in console “TypeError: ‘dict’ object is not callable”

    After the user is logged in i have this in the controller that require the user under authentication:
    class Home(BaseHandler):
    @user_required
    def get(self):

    # get the data of the user in session
    user = self.user_info() # in this line i get the error

    template_values = {}
    template = JINJA_ENVIRONMENT.get_template(‘templates/panel/index.html’)
    self.response.write( template.render( template_values ) )

    Am I calling the data session wrong?

    Sorry if i wrote something wrong but i don’t know much english, i hope that you understand my question. Thanks for your help

  27. Pingback: Webapp2 List (and/or kill) All Sessions for User (Appengine Python) | BlogoSfera

  28. Thanks for the great post! This really helped me understand the complex stuff of login en authentication. Saved me a lot of time.
    I cloned the source from github, and made some modifications to the login part:
    was: always go to home page after login
    what I wanted: after login, go to page from where login was invoked.

    So if he user is not logged in, clicks ‘secure’ link: the login screen appears. After successful login, user is forwarded to the page he/she wanted to access in the first place.

    If anyone is interested, it’s a fork of the original demo on github.

  29. As a newb to all this, I have a question about the verification procedure in your code:
    During signup, the user record is created, but not actually put() into the datastore. In your code, this only happens after the verification is complete, as far as I can tell.
    So where are all the user-details (last name etc) stored between signup and verification?
    Is the complete user data encrypted into the verification token? Or does webapp2 store the user-info into the datastore/session/cookie when the create_user is called?

  30. Thanks for this, Alessandro – so helpful.

    But now I want to delete a user. I have an ‘admin’ page with a list of all users, and ‘delete’ links and appropriate handlers. I delete the user entity and redirect back to the admin page – but the list is unchanged unless I refresh the page again. I assume that this is happening because I’m too quick to regenerate the user list, and because users have no parent, I can’t do an ancestor query and be assured of updated info when I get back to the page.

    I can pass in a key as the parent when I call create_user, which seems to work properly… but when I try to verify the new user, get_by_auth_token fails even though the user ID and token are properly formed in the verification URL, etc. Any ideas why this should be?

    thanks
    Aaron

    • Hello Aaron,
      it is hard to tell without being able to see the code, but is there any chance the admin page is cached by the client?
      You can test whether that is happening by having the Chrome Developer Tools (Network tab) when you test your webapp and seeing whether the status for second call to the admin page is 304 or something like that.

    • Not caching, but looks like a timing issue. If I do this:

      a) get list of all users
      b) delete one
      c) get list of all users

      I get the deleted user in step c, but if I wait a couple of seconds first, it’s gone. But, I’ve found that while I see this in AppLauncher on my mac, it doesn’t happen when I deploy to the cloud. I can live with that, but I wish I understood it better.

      Thanks again,
      Aaron

    • Hi Aaron,

      Can you please share the handler to delete an User? I am a newbie with pyton / GAE programming. Thanks;

    • Hi Edward,
      one way to do that would be to delete all the tokens associated with a specific user.
      Webapp2 does not offer any public API to do that, but you can extend the code in webapp2_extras to include that functionality.
      See the code here for more details.

  31. Alessandro Bahgat I love the way you explained. Finally I was able to understand the whole process.

    The only thing that I found is that “create_user” uses by default “security.generate_password_hash” which uses by default sha1 to hash passwords. As long as sha1 has been prove to be unsecure, There is any way enforce a more secure algorithm to hash passwords?

    I was thinking in bcrypt (but the pure python version because gae don’t suport c) finded here: https://github.com/erlichmen/py-bcrypt

    What do you think? How can I change just the “create_user” part to use a more secure algorithm?
    (mmm … also checking passwords on login must be changed)

    • Hello Jandro,
      you can hash passwords using any algorithm of your choice and specify them to create_user using the password parameter (instead of the password_raw variant, which will use sha1, as you pointed out).

      Then, you can override get_by_auth_password in your user model, which is used to verify passwords at login.

      That said, switching to py-bcrypt would certainly slow down your application and consume more resources. You might want to read this thread to make sure the benefit is worth the cost in your case.

  32. Pingback: Google App Engine Password | iphatak4s

  33. Hello and Happy new year at all :)

    Quick question:
    If I print the result from user_info, I receive that: {‘token_ts': 1388754331, ‘name': u’Olivier’, ‘cache_ts': 1388754331, ‘token': u’8ca40khnqOzIr5dbDhsQFV’, ‘remember': 1, ‘user_id': 5825281323433984}, but I want other information’s like email_address and last_name.

    Apparently It’s possible to retrieve extra information with user_attributes, but if I customize my user_attributes like that: ‘user_attributes': ['name', 'email_address'],
    I receive an error: Invalid user data: [5825281323433984, 1, u'8ca40khnqOzIr5dbDhsQFV', 1388754331, 1388754331, u'Olivier']. Expected attributes: ['user_id', 'remember', 'token', 'token_ts', 'cache_ts', 'name', 'email_address'].

    Can you provide me a solution or comments to solved that ?

    Thanks in advance.
    olituks

    • Sorry, I found the problem, I’m not perform a logoff after change my code and old datas still in cache… now if a refresh the page and perform a login, I receive all datas I expected.

  34. Hi Alessandro,

    My system based on your explanations here is running well – thanks again.

    Now, after a few months, with about 50 users in the system, I note that I have hundreds of auth tokens, many older than the default 3 weeks. Are they supposed to disappear on their own? Am I supposed to delete them myself occasionally?

    thanks
    Aaron

    • Hi Aaron,

      I also have old tokens. It seems that old tokens are deleted when new tokens are created for the same user, i.e. the user needs to login again. The same should be true for all other tokens (signup, password reset,…) You could add a cron-job that is running daily or weekly to delete all the old tokens that have been missed for some reason.

      Ani

    • Thanks, Ani. The confusing part for me is that a single user might have 20+ auth tokens. The system can be set to remember the user by storing the auth in a cookie, so maybe new auth tokens are being created as old ones in the cookies expire, then aren’t deleted..?

      A

    • I have looked into it and can confirm. So, there might be some kind of “leak” where the auth module is missing to catch and delete expired tokens although it seems to delete tokens under certain conditions, e.g. in webapp2 v2.5.2 in validate_token() and unset_session() in auth.AuthStore.

      However, even if the auth module would not miss to delete these tokens, the app would still need a cron-job to do the clean-up of expired tokens. There are cases (for example non-consumed tokens, non-returning users) where webapp2 has no chance to catch them, unless webapp2 would query for expired tokens whenever a new token is created. I think a custom cron-job is much cheaper and faster :)

      The query in the cron-job handler could be like this (adjust and import path to your custom User class):

      “`python
      import datetime
      from google.appengine.ext import ndb
      expiredTokens = User.token_model.query(User.token_model.created 0:
      keys = expiredTokens.fetch(100, keys_only=True)
      ndb.delete_multi(keys)
      “`

      The value in the timedelta() assumes that any token expires ~3 months after it has been created. Then deletes them in bulks of 100.

      About cron-jobs for Python apps: https://developers.google.com/appengine/docs/python/config/cron

  35. Great job @anihatzis,
    you managed to reply even before I read the comment!

    @Aaron, anihatzis is right: unfortunately webapp2 has mechanism for deleting tokens when they are supposed to expire as no code is called when that condition triggers.
    I would recommend to follow the suggestion to have a cron job to take care of that, especially if you use signup tokens, as those pose a security risk if they stick around too long.

  36. Re: get_user_by_session(), on current GAE, not working when client is htmlunit

    Dear Sir,

    Thanks to you, I’m up and running with user accounts.

    Why would htmlunit not work with this stuff?

    More specifically, the following works, when the client is a Chrome browser:

    def check_login(self, *args, **kwargs):
    auth = self.auth
    if not auth.get_user_by_session():
    logging.info(‘%User not logged in. Redirecting’)
    self.redirect(self.uri_for(‘login’), abort=True) . . .
    _____________________________________________________

    It does not work when the client is htmlunit (Java program running on Eclipse), using the GAE production server. (auth.get_user_by_session() is None, even when the user should be logged in)

    It does not work when the client is htmlunit, using the current GAE development server.

    It DOES work when the client is htmlunit, using the old GAE development server (${GOOGLE_APP_ENGINE}/old_dev_appserver.py).

    Any suggestions on how to investigate this would be most appreciated.

    Thanking you again,

    Jerome

    • Hello Jerome,
      it definitely looks like something has changed in the GAE development server, rather than being it a bug in webapp2 per se. Can you see anything in the logs?

      I would suggest debugging get_user_by_session on the new server to see whether this is a genuine bug and report a bug on the appropriate component.

    • Alessandro,

      After updating with the latest version of htmlunit, the problem disappeared.

      With best regards,

      Jerome

  37. First off thanks for this awesome guide! I’ve followed it but now I’m hitting a snag on implementation. When my user (myself right now) tries to validate their account I’m getting the following error:
    user, ts = self.user_model.get_by_auth_token(user_id, signup_token, ‘signup’)
    TypeError: get_by_auth_token() takes exactly 3 arguments (4 given)

    Can anyone help troubleshoot this?

  38. thank you so much it is very helpful, actually i want to have more than one entity kind of users, for example:
    entity kind : user1
    entity kind: user2
    entity kind: user3
    so now i have 3 kinds of users ! maybe user1 for the team leaders , user2 for senior developer and user3 junior developers. how could i implement this by your code ? thank you in advance :)

    • I strongly recommend to avoid specific User kinds for application roles. It would be much better if you create a specific AppRole class with objects like “Team Leader”, “Senior Dev”, “Junior Dev”, and then link your User objects with the app-roles. If a user is promoted or demoted you just update the app-role key reference, you don’t need to delete the User object and than create a new one (with all the problems that comes with this approach, especially if you store comments, commits etc. with the user as parent). Alternatively, you could design something more powerful and abstract (e.g. ACL with many different permissions and scopes).

  39. if i want to inherit blobstore_handlers.BlobstoreUploadHandler and not just Webapp2.ReaquestHandler, how could i use basehandler ?

    • Since Blobstore handlers serve a very specific purpose, I think you can just implement the methods you need on your handler(s) that need to manipulate objects with the blobstore API and have the others extend BaseHandler.
      Webapp2 is quite versatile and should allow you to use different handler types, as long as you keep them separate.

      Hope that helps!

    • I had the same issue when I wanted to use BlobStore for serving images from my custom domain and with the same authentication. Found this solution: http://stackoverflow.com/a/20342757/1549523 Maybe you have more luck than me However, I wasn’t able to implement it successfully, so I’ve decided to use Google Cloud Storage client library instead of BlobStore. GCS let me use my custom request handler with no issues. Implementation was quite smooth. And I have even build the resize feature of BlobStore using images.resize() from google.appengine.api – also see http://stackoverflow.com/q/22082244/1549523

  40. Hi Alessandro,

    Really thank you for this tutorial and your source code. It helps me a lot to kickstart my project’s backend. I have one question though, is there any way that we can get a UserToken by user_id ? The webapp2’s module seems to only have create_token not get_token.

    I am trying to implement facebook authentication. So once user is confirmed to be facebook user and match with our datastore’s user, i will return him/her a ‘auth’ token. Client side (js/ios app) saves this token and any api call from this user will use this token.

    Of course, I can create the token every time, but i end up with a lot of unused tokens in datastore. When I try to query UserToken.query(user==user_id), it causes error: ‘unindexed property user’.

    • Try `UserToken.query(UserToken.user==user_id)` – you might also want to add UserToken.subject to the filters, e.g. UserToken.subject==”auth”

    • @anihatzis: I did try that but that returns an error ‘unindexed property “user”‘. So i went to the source, it really declares as indexed = false

      class UserToken(model.Model):

      user = model.StringProperty(required=True, indexed=False)

      Unless, I override this class as a new UserToken then it can be done. But it got to have a better way I think … Just to get any auth_token from a particular User. Is there any reason (security ?) why webapp2 does not provide this out of the box ?

    • @natewerewolf Sorry, my mistake. UserToken.user is unindexed. The classmethod `UserToken.get()` does not use a query with user filter. If user + subject + token is given, the method constructs the key and gets the token from datastore. The query is used if only subject and token is given (both are indexed). I think you could try either `UserToken.get(user=user_id, subject=”auth”, token=token_from_http)` or `UserToken.get(subject=”auth”, token=token_from_http)` to manually get the token. But I’m not sure why you actually want to get the user token? If I want to get the authenticated user, I use e.g. `myuser = reqhandler.user` provided by webapp2

    • @anihatzis: I was trying to implement authorized REST API calls (currently using Google Endpoint for this purpose). So in those calls, I cannot get user from session like the normal flow. What I can do is attach user_id and user_auth_token on each call from client. On server, I will take these information and verify if the calls are, in fact, authorized.
      So that’s why if the user successfully logged in, server need to manually get the user_auth_token string and return to client. Does this flow make sense ? Or should I try another approach ?

    • Makes sense to me. I have done something similar with the Channel API. Have you been able to successfully get the user-tokens? And actually, I would be interested to learn how you pass user_id and token through Endpoints, if you don’t mind to tell.

    • Hi, so I tried implementing it and added code example here: http://stackoverflow.com/a/22495862/1149484
      Still want some opinions on the security side though.

      To clarify, I was not able to get the existing UserToken. However, I think it might make sense to reuse the UserToken only if my Custom Auth Token (here is Facebook Access Token) is still valid. So I created own Auth Token based on Facebook Acccess token, and re-get it whenever client pass an access_token (if it’s new, then we re-create auth token, of course).

      With this approach, if the facebook’s access_token does not change, my auth token returned will also not change. If they log out, we can remove the auth token like previously. So we won’t end up with a million of tokens… You can look at the link, especially on ‘Implementation of User.get_by_facebook_id’ part and tell me what you think.

  41. Pingback: base handler class ,webapp2_extra.auth | Stackforum.com

  42. Thanks so much for sharing the code. It is wonderfull. I am testing it in a development enviornment and find that all users created are gone after my machine is reboot. Is this supposed to be the case?
    James

    • Hi James,
      that happens occasionally with the development server, as it clears the datastore. If you deploy your application on appspot.com, that won’t happen.

  43. HI Alessandro,

    Thanks a lot for the awesome tutorial! This is exactly what I was looking for. Could you please help me with one small thing, I am very new to webapp2 and this might be very stupid but I’m stuck for some time now.

    I do not know how to access the stored information for a logged in user. What is the command to use to lets say fetch the last name of the user. I tried user.last_name() , user['last_name'] but nothing seems to work and couldn’t find it in the documentation. Could you please help.

    • Hi Pawan,
      the User implementation provided by webapp2 is very basic, and it does not store names for users unless you extend it.
      You can probably extend the custom user class mentioned in the article (here) to add any property you need to handle.

      Good luck!

  44. Hi Alessandro,

    Firstly, this code is absolutely awesome. It has been very helpful on my project.

    I noticed that once a user signs up, they are able to login even if they have not clicked the verification url. Is that the correct user experience. I see nothing in your code that checks for verified=true. Am I missing something?

  45. Thank you Jason,
    I am glad this helps.

    You are right, the sample code allows users with unverified email addresses to log in.
    I chose to leave that behavior to be customized for the application you are developing, since different apps implement different verifications (e.g. some won’t allow unverified users to login, while others will allow them to do so, but present them with a note encouraging them to verify their address).

    Cheers!

  46. Hi, great code there!
    I’m new with webapp2, I have to ask, how can I implement some roles for the authentication process? How to distinguish admins with normal users?

  47. Hi Alessandro. This is great; thank you! I am fairly new to Python + GAE in general.
    Q: I have two ndb datastore kinds (two database). One for group A, one for B as in class GroupA(ndb.model) and class GroupB(ndb.model), both slightly different fields. Like “teachers” and “students” on a single website. How can I handle such session with your examples? What must I change? Any help is greatly appreciated!

    • Hi there,
      I would consider having Teacher and Student classes extend the custom User model in this post and adding the properties you need for each user type to the corresponding class.

    • I have the same question. Can webapp2 be configured to use the extended classes, Teacher and Student? Since User is an expando, is extending recommended? I’m struggling to write decorators that discriminate one user type from another with the expando. I do not even see logging working in a decorator. It would be great to see an example of using decorators to discriminate user types. Thanks for any suggestions!

    • I got my decorator working. I had a syntax error or two, and a runtime error where I was returning the handler without the function arguments. I am using the expando properties to discriminate different user classes.

  48. This will help you to do a search for the inevitable swing back of school
    bus their stated goals. Not only are these independent
    school bus contractors no later than December 30!
    Allowing the case, the living area. As each candidate arrives, the best.
    Don’t let school bus this water you require, as well as show samples of their trade.

  49. Hi,
    Great information, thanks.
    My question may be a bit simple but here goes… My application has many .html pages in addition to main.html. Do I need to include a check that the user is logged in in the ‘get’ handler of each page and redirect to ‘main’ if they are not? My concern is that the user should not be able to bypass the login.html page if they know the other page’s link.
    Thanks
    David

    • Yes, you need to add verification if user is logged in to every request handler (where it makes sense for your app). The same applies if your app implements access control, for example, only logged in users with a certain permission P1 can execute request handler R1.

What do you think? Leave a Reply:

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s