Skip to content

A very simple Flask application that demonstrates how to integrate OpenID connect into user session management.

License

Notifications You must be signed in to change notification settings

shihanng/flask-login-example

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

28 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

simple-login

The aim of this document is to provide a simple guide on how to create a web application that requires user to login via OpenID connect to view a specific page. The web application is built with Python 3 and the Flask. The user login session is managed with the help of Flask-Login and Flask-Session. See setup.py for the dependencies.

Start Development Server

Let's make sure that everything is running as promised.

1. Create a virtual environment and install the dependencies.
python3 -mvenv env
pip install --editable .
2. Setup the following environment variables. GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET can be obtained from Google's Credentials page, see section "Obtain OAuth 2.0 credentials" in this guide.
export FLASK_ENV=development
export GOOGLE_CLIENT_ID=
export GOOGLE_CLIENT_SECRET=
3-a. Using the flask run command
export FLASK_APP=src/simple_login/app.py
flask run
3-b. Using the setup.py's entry_points
app
4. Visit localhost:5000 to confirm the app is running.

Process with the "login from here" and complete the login. Then visit localhost:5000/secret We should be able to see

Only logged in user can see this.

The Essentials

As the entry_points in setup.py indicates, the application starts from ./src/simple_login/app.py.

Configurations

The configuations of the application are stored in the environment. This is one of the practices recommended by the Twelve-Factor App.

GOOGLE_CLIENT_ID = os.environ.get("GOOGLE_CLIENT_ID", default="")

To load the configurations into our application, we use,

app.config.from_object(__name__)

which loads only the uppercase attributes of the module/class. To access the configurations within the application, we do

current_app.config["GOOGLE_CLIENT_ID"]

Check out the official documentation to learn more about configuration handling.

Sessions

The default Flask's session stores the values in cookies not on the server side by default. This means that we should not store anything sensitive in the default cookie. For server side session, we can use the extension Flask-Session Here in our example we setup the application to store the session in our local filesystem.

SESSION_TYPE = 'filesystem'

Session(app)

Then use the flask.session object which works like an ordinary dict to access the values:

session['state'] = state  # Store
v = session['state']      # Retrieve

Managing Logins

We can manage user login with the Flask-Login library which store active user's ID in the session that we've setup above.

login_manager = LoginManager()
login_manager.init_app(app)

In addition to the setup above, we also need to provide a our user class.

class User(object):

and a user_loader callback which will return either the user of a given user_id from the database, or None if the Id is invalid:

@login_manager.user_loader
def load_user(user_id) -> Optional[User]:
    app.logger.debug('looking for user %s', user_id)
    u = users.get(user_id, None)
    app.logger.debug('id is %s', id)
    if not id:
        return None
    return u

For the sake of simplicity, our database is just a simple dict within the application.

users: Dict[str, User] = {}

Walk-through

  1. When a user visits localhost:5000, Flask will render the index page using the templates/index.html that lead the user to localhost:5000/login.

  2. The login process starts with creating an anti-forgery state token, and nonce for replay protection. Both values are stored in the server-side session and the client (browser) holds the session ID in cookies.

  3. Then an authentication request is sent to Google and the user will be redirect to the Google consent page.

  4. The response after the consent is given will be received by the /callback endpoint. At this point we should verify that the value of the state from Google matches the one we've stored in the session.

  5. Using the code parameter from the response, a POST request is made to Google for exchanging the access token and ID token.

  6. The id_token (a JWT) field should be found in the successful response to the POST request. After base64 decode the id_token we should verify the nonce field and remove the nonce from our session. Then we create the our application User based on the id obtained from the sub field, store it in our users DB, and login the user with Flask-Login: login_user(u).

About

A very simple Flask application that demonstrates how to integrate OpenID connect into user session management.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published