Flask: share sessions between domain.com and username.domain.com

EDIT: Later, after reading your full question I noticed the original answer is not what you're looking for.

I've left the original at the bottom of this answer for Googlers, but the revised version is below.

Cookies are automatically sent to subdomains on a domain (in most modern browsers the domain name must contain a period (indicating a TLD) for this behavior to occur). The authentication will need to happen as a pre-processor, and your session will need to be managed from a centralised source. Let's walk through it.

To confirm, I'll proceed assuming (from what you've told me) your setup is as follows:

  • SERVER 1:
    • Flask app for domain.com
  • SERVER 2:
    • Flask app for user profiles at username.domain.com

A problem that first must be overcome is storing the sessions in a location that is accessible to both servers. Since by default sessions are stored on disk (and both servers obviously don't share the same hard drive), we'll need to do some modifications to both the existing setup and the new Flask app for user profiles.

Step one is to choose where to store your sessions, a database powered by a DBMS such as MySQL, Postgres, etc. is a common choice, but people also often choose to put them somewhere more ephemeral such as Memcachd or Redis for example.

The short version for choosing between these two starkly different systems breaks down to the following:

Database

  • Databases are readily available
  • It's likely you already have a database implemented
  • Developers usually have a pre-existing knowledge of their chosen database

Memory (Redis/Memchachd/etc.)

  • Considerably faster
  • Systems often offer basic self-management of data
  • Doesn't incur extra load on existing database

You can find some examples database sessions in flask here and here.

While Redis would be more difficult to setup depending on each users level of experience, it would be the option I recommend. You can see an example of doing this here.

The rest I think is covered in the original answer, part of which demonstrates the matching of username to database record (the larger code block).

Old solution for a single Flask app

Firstly, you'll have to setup Flask to handle subdomains, this is as easy as specifying a new variable name in your config file. For example, if your domain was example.com you would append the following to your Flask configuration.

SERVER_NAME = "example.com"

You can read more about this option here.

Something quick here to note is that this will be extremely difficult (if not impossible) to test if you're just working off of localhost. As mentioned above, browsers often won't bother to send cookies to subdomains of a domain without dots in the name (a TLD). Localhost also isn't set up to allow subdomains by default in many operating systems. There are ways to do this like defining your own DNS entries that you can look into (/etc/hosts on *UNIX, %system32%/etc/hosts on Windows).

Once you've got your config ready, you'll need to define a Blueprint for a subdomain wildard.

This is done pretty easily:

from flask import Blueprint
from flask.ext.login import current_user

# Create our Blueprint
deep_blue = Blueprint("subdomain_routes", __name__, subdomain="<username>")

# Define our route
@deep_blue.route('/')
def user_index(username):

    if not current_user.is_authenticated():
        # The user needs to log in
        return "Please log in"
    elif username != current_user.username:
        # This is not the correct user.
        return "Unauthorized"

    # It's the right user!
    return "Welcome back!"

The trick here is to make sure the __repr__ for your user object includes a username key. For eg...

class User(db.Model):

    username = db.Column(db.String)


    def __repr__(self):
       return "<User {self.id}, username={self.username}>".format(self=self)

Something to note though is the problem that arises when a username contains special characters (a space, @, ?, etc.) that don't work in a URL. For this you'll need to either enforce restrictions on the username, or properly escape the name first and unescape it when validating it.

If you've got any questions or requests, please ask. Did this during my coffee break so it was a bit rushed.