Flask user session management.

Overview

Flask-Login

Tests coverage Software License

Flask-Login provides user session management for Flask. It handles the common tasks of logging in, logging out, and remembering your users' sessions over extended periods of time.

Flask-Login is not bound to any particular database system or permissions model. The only requirement is that your user objects implement a few methods, and that you provide a callback to the extension capable of loading users from their ID.

Installation

Install the extension with pip:

$ pip install flask-login

Usage

Once installed, the Flask-Login is easy to use. Let's walk through setting up a basic application. Also please note that this is a very basic guide: we will be taking shortcuts here that you should never take in a real application.

To begin we'll set up a Flask app:

import flask

app = flask.Flask(__name__)
app.secret_key = 'super secret string'  # Change this!

Flask-Login works via a login manager. To kick things off, we'll set up the login manager by instantiating it and telling it about our Flask app:

import flask_login

login_manager = flask_login.LoginManager()

login_manager.init_app(app)

To keep things simple we're going to use a dictionary to represent a database of users. In a real application, this would be an actual persistence layer. However it's important to point out this is a feature of Flask-Login: it doesn't care how your data is stored so long as you tell it how to retrieve it!

# Our mock database.
users = {'[email protected]': {'password': 'secret'}}

We also need to tell Flask-Login how to load a user from a Flask request and from its session. To do this we need to define our user object, a user_loader callback, and a request_loader callback.

class User(flask_login.UserMixin):
    pass


@login_manager.user_loader
def user_loader(email):
    if email not in users:
        return

    user = User()
    user.id = email
    return user


@login_manager.request_loader
def request_loader(request):
    email = request.form.get('email')
    if email not in users:
        return

    user = User()
    user.id = email

    # DO NOT ever store passwords in plaintext and always compare password
    # hashes using constant-time comparison!
    user.is_authenticated = request.form['password'] == users[email]['password']

    return user

Now we're ready to define our views. We can start with a login view, which will populate the session with authentication bits. After that we can define a view that requires authentication.

@app.route('/login', methods=['GET', 'POST'])
def login():
    if flask.request.method == 'GET':
        return '''
               <form action='login' method='POST'>
                <input type='text' name='email' id='email' placeholder='email'/>
                <input type='password' name='password' id='password' placeholder='password'/>
                <input type='submit' name='submit'/>
               </form>
               '''

    email = flask.request.form['email']
    if flask.request.form['password'] == users[email]['password']:
        user = User()
        user.id = email
        flask_login.login_user(user)
        return flask.redirect(flask.url_for('protected'))

    return 'Bad login'


@app.route('/protected')
@flask_login.login_required
def protected():
    return 'Logged in as: ' + flask_login.current_user.id

Finally we can define a view to clear the session and log users out:

@app.route('/logout')
def logout():
    flask_login.logout_user()
    return 'Logged out'

We now have a basic working application that makes use of session-based authentication. To round things off, we should provide a callback for login failures:

@login_manager.unauthorized_handler
def unauthorized_handler():
    return 'Unauthorized'

Complete documentation for Flask-Login is available on ReadTheDocs.

Contributing

We welcome contributions! If you would like to hack on Flask-Login, please follow these steps:

  1. Fork this repository
  2. Make your changes
  3. Install the requirements in dev-requirements.txt
  4. Submit a pull request after running make check (ensure it does not error!)

Please give us adequate time to review your submission. Thanks!

Comments
  • current_user instance is of LocalProxy type

    current_user instance is of LocalProxy type

    Hi, when using current_user instance I was having a weird bug with MongoEngine, then I decided to debug and when checking:

    ipdb> type(current_user)
    <class 'werkzeug.local.LocalProxy'>
    

    That's not desired if I will use it directly. MongoEngine complains when using that instance during query/update, etc... Is there any way to unwrap the proxy? Right now I have to query to the DB to get the real object.

    Thanks

    opened by eka 35
  • Broken compatibility with Werkzeug 0.9 and Flask 0.10

    Broken compatibility with Werkzeug 0.9 and Flask 0.10

    The current release version does not work well with Flask 0.10 and the current code in Flask-Login in master does not work with Werkzeug 0.9.

    The two changes that break it:

    • sessions in flask can no longer contain binary data
    • headers are now unicode.
    Bug 
    opened by mitsuhiko 20
  • Drop Python 2.7 support

    Drop Python 2.7 support

    Fixes #508.

    This removes all support for and references to Python 2.7, including in the test matrix. Most changes were performed through running 2to3 but some work was performed manually, such as imports and removing the _compat module. Running make clean reports all is well.

    opened by le717 19
  • Backwards compatible imports (_create_identifier)

    Backwards compatible imports (_create_identifier)

    Can we keep the global import namespace compatible with 0.3.x version for utils introduced in 088ac3cf5e0597d59224fcfb0536bea031a9ae17 ?

    __init__.py

    
    from utils import _create_identifier, ...
    
    __all__ = ('_create_identifier', ...)
    

    Thanks!

    -- Make sure these boxes are checked before submitting your issue--thank you!

    • [x] Ensure you are using the latest PyPI release.
    • [x] Read the CHANGES document thoroughly.
    • [x] Provide a clear and simple set of steps to reproduce your issue for others.
    opened by jirikuncar 19
  • Fix handling of X-Forwarded-For header

    Fix handling of X-Forwarded-For header

    The code that calculates a user's identifier uses the remote address or the contents of the X-Forwarded-For header doesn't account for the fact that the X-Forwarded-For header can contain a comma separated list of remote addresses, which is the case for scenarios in which 2+ proxy servers are involved in the request forwarding. This caveat causes Flask-Login to unexpectedly log users out when the combination of proxy servers that routed a previous request change.

    See http://tools.ietf.org/html/rfc7239 for more information about X-Forwarded-For standard.

    opened by sholsapp 19
  • Avoid touching the session unless something changed

    Avoid touching the session unless something changed

    Some session backends will try to detect changes made to the session dictionary and only save if the session was modified. The pop() call will set modified = True in such a backend. By checking if the "remember" key exists before we pop it we avoid this behaviour.

    opened by simonklee 15
  • Bump jinja2 from 2.10.3 to 2.11.3 in /requirements

    Bump jinja2 from 2.10.3 to 2.11.3 in /requirements

    Bumps jinja2 from 2.10.3 to 2.11.3.

    Release notes

    Sourced from jinja2's releases.

    2.11.3

    This contains a fix for a speed issue with the urlize filter. urlize is likely to be called on untrusted user input. For certain inputs some of the regular expressions used to parse the text could take a very long time due to backtracking. As part of the fix, the email matching became slightly stricter. The various speedups apply to urlize in general, not just the specific input cases.

    2.11.2

    2.11.1

    This fixes an issue in async environment when indexing the result of an attribute lookup, like {{ data.items[1:] }}.

    2.11.0

    This is the last version to support Python 2.7 and 3.5. The next version will be Jinja 3.0 and will support Python 3.6 and newer.

    Changelog

    Sourced from jinja2's changelog.

    Version 2.11.3

    Released 2021-01-31

    • Improve the speed of the urlize filter by reducing regex backtracking. Email matching requires a word character at the start of the domain part, and only word characters in the TLD. :pr:1343

    Version 2.11.2

    Released 2020-04-13

    • Fix a bug that caused callable objects with __getattr__, like :class:~unittest.mock.Mock to be treated as a :func:contextfunction. :issue:1145
    • Update wordcount filter to trigger :class:Undefined methods by wrapping the input in :func:soft_str. :pr:1160
    • Fix a hang when displaying tracebacks on Python 32-bit. :issue:1162
    • Showing an undefined error for an object that raises AttributeError on access doesn't cause a recursion error. :issue:1177
    • Revert changes to :class:~loaders.PackageLoader from 2.10 which removed the dependency on setuptools and pkg_resources, and added limited support for namespace packages. The changes caused issues when using Pytest. Due to the difficulty in supporting Python 2 and :pep:451 simultaneously, the changes are reverted until 3.0. :pr:1182
    • Fix line numbers in error messages when newlines are stripped. :pr:1178
    • The special namespace() assignment object in templates works in async environments. :issue:1180
    • Fix whitespace being removed before tags in the middle of lines when lstrip_blocks is enabled. :issue:1138
    • :class:~nativetypes.NativeEnvironment doesn't evaluate intermediate strings during rendering. This prevents early evaluation which could change the value of an expression. :issue:1186

    Version 2.11.1

    Released 2020-01-30

    • Fix a bug that prevented looking up a key after an attribute ({{ data.items[1:] }}) in an async template. :issue:1141

    ... (truncated)

    Commits

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) You can disable automated security fix PRs for this repo from the Security Alerts page.
    dependencies python 
    opened by dependabot[bot] 13
  • Exception: No user_loader has been installed for this LoginManager

    Exception: No user_loader has been installed for this LoginManager

    For apps with multiple User models, is there any way to alter the user_loader / load_user() to be able to distinguish between the models? The standard is

    @login_manager.user_loader
    def load_user(user_id):
        return User.get(user_id)
    

    I need to somehow discern which model the user_id passed belongs to and insert a condition to replace User.get() with the appropriate model.

    Bug 
    opened by NikolaJankovic 13
  • Flask WTF and session_protection = 'strong'

    Flask WTF and session_protection = 'strong'

    This https://github.com/maxcountryman/flask-login/commit/9c8f3ca7441fa0abb506093c7161b5aa4cad88df has created me a lot of problems using forms with csrf token enabled and session protection set to strong this is the issue that I was getting as well https://github.com/lepture/flask-wtf/issues/197 and unsetting the session_protection made it work.

    I can't figure out how I can make it work again using 'strong', could you please provide me some useful links or examples?

    opened by shipperizer 13
  • Support force-logout for active sessions

    Support force-logout for active sessions

    Right now you can only force-logout a remember-me token by using a custom token. It would be nice if there was a clean way to do this for active sessions, too.

    From a quick look at the code this could probably be done by making get_id return e.g. a tuple of userid, token and the user_loader handling this token accordingly (retrieving the user from the DB and checking if the tokens match). However, the documentation is quite clear about the user id having to be unicode and a second look at the code shows that the user id actually converted to a string at some point: data = encode_cookie(str(session['user_id'])) (actually, shouldn't this be unicode and not str?).

    Sure, I could use a custom string representation such as id:token but it feels dirty.

    Contributor Friendly Feature Request 
    opened by ThiefMaster 13
  • Re-enable localisation of flash messages after migration to Flask 0.10+

    Re-enable localisation of flash messages after migration to Flask 0.10+

    Flask 0.10+ makes it impossible to use login_manager.login_message = lazy_gettext('Please log in') (no more pickle), thus buggering localisation of flask-login's flash messages.

    Add a localize_callback attribute to LoginManager that, if present, will be called with login_message and needs_refresh_message and whose return value will be sent flash.

    opened by deanishe 13
  • Getting mixed results with flask-login, when using authenticated and anonymous clients

    Getting mixed results with flask-login, when using authenticated and anonymous clients

    Describe the bug I have a test that makes use of two fixtures. One provides an anonymous client, and another provides an authenticated client. Should I run the following code, the test fails and the user info returned is that of the authenticated user, although I am using the anonymous client.

    #Note: APIAwareFlaskLoginClient extends FlaskLoginClient to request a csrf prior to each post
    
    @pytest.fixture
    def app():
        app = create_app()
        app.test_client_class = APIAwareFlaskLoginClient
    
        with app.app_context():
            # reinit the database at each test
            db.drop_all()
            db.create_all()
    
            with Transaction(db) as session:
                session.add(Owner(email='a', name='b', password='c'))
    
        yield app
    
    @pytest.fixture
    def client(app):
        return app.test_client()
    
    @pytest.fixture
    def authenticated_client(app):
        client = app.test_client()
        client.post("/api/auth/login", json=dict(email='a', password='c', remember='1'))
        return client
    
    def test_anonymous_after_auth(client, authenticated_client):
        response = client.get('/api/auth/user')
        assert response.status_code == 401
    

    Should I remove the authenticated_client from the method signature, everything works as expected. Is this a bug, or I am doing something wrong?

    Environment (please complete the following information):

    • OS: Linux
    • Python 3.10.8
    • flask-login 0.6.2
    opened by flixman 0
  • regenerate & push latest ReadTheDocs documentation

    regenerate & push latest ReadTheDocs documentation

    Describe the bug The documentation on the ReadTheDocs website is out of date.

    There was a broken link that was fixed in 2019 (https://github.com/maxcountryman/flask-login/commit/dc6103ac), but the ReadTheDocs website still has the old link (https://flask-login.readthedocs.io/en/latest/#login-example).

    The latest docs should be regenerated/pushed to ReadTheDocs to resolve the bug.

    To Reproduce Steps to reproduce the behavior:

    1. Go to https://flask-login.readthedocs.io/en/latest/#login-example
    2. Click on 'this Flask Snippet' link
    3. See error

    Expected behavior Link should go to https://web.archive.org/web/20120517003641/http://flask.pocoo.org/snippets/62/

    Screenshots

    Desktop (please complete the following information):

    • OS: macOS
    • Browser: chrome
    • Version
    opened by pwillis-eiq 2
  • Potential regression in expected behavior with UserMixin.is_authenticated defaulting to is_active that was changed in 0.6.0

    Potential regression in expected behavior with UserMixin.is_authenticated defaulting to is_active that was changed in 0.6.0

    Version 0.6.0 has:

    • UserMixin.is_authenticated will return whatever is_active returns by default. This prevents inactive users from logging in. #486, #530

    Based on the comments in the above issues, the case for doing this was to prevent inactive users from logging in but an inactive user is not the same as a user who can't authenticate due to invalid credentials. These are much different things.

    In 0.5.0 you could do:

        @login_manager.user_loader
        def load_user(uid):
            user = user_model.query.get(uid)
    
            if not user.is_active():
                login_manager.login_message = 'This account has been disabled.'
                return None
    
            return user
    

    And then if an inactive user tried to login they would receive a custom flash message. This lets them know they have an account but they've been disabled.

    With 0.6.0 this code path doesn't seem to execute because the user gets blocked before they would be loaded so you end up with whatever message you would send to the user when their authentication failed. This is a regression in behavior.

    I thought a potential workaround in 0.6.0 would have been to add this to my user model (the default in 0.5.0):

        def is_authenticated(self):
            return True
    

    But this had no effect. I'm still not able to execute the user loader that would have presented a custom flash message.

    How can we get the old behavior back where end users of this library can handle inactive users after they've been authenticated?

    opened by nickjj 0
  • deprecate `__about__` module

    deprecate `__about__` module

    The only thing that might be relevant to runtime inspection is __version__, which is already exported. The rest is package metadata that should be in setup.cfg, which can be inspected with importlib.metadata if needed.

    opened by davidism 0
  • don't look at the `X-Forwarded-For` header

    don't look at the `X-Forwarded-For` header

    Flask-Login has no way to guarantee that this header is correct because it doesn't know how many proxies the app is or isn't deployed behind. Instead, it should always read request.remote_addr, and assume that the user has configured ProxyFix if necessary.

    opened by davidism 1
Releases(0.6.2)
  • 0.6.2(Jul 26, 2022)

  • 0.6.1(May 2, 2022)

  • 0.6.0(Mar 30, 2022)

    • Changes: https://github.com/maxcountryman/flask-login/blob/main/CHANGES.md#version-060

    This release sets new minimum versions of Python, Flask, and Werkzeug, and fixes compatibility with the latest versions of those.

    • Python >= 3.7
    • Flask >= 1.0.4, this will be bumped to reflect the latest supported release (2.1) in the future
    • Werkzeug >= 1.0.1, this will be bumped to reflect the latest supported release (2.1) in the future
    Source code(tar.gz)
    Source code(zip)
Owner
Max Countryman
Distributed systems, functional programming, cloud computing.
Max Countryman
An extension of django rest framework, providing a configurable password reset strategy

Django Rest Password Reset This python package provides a simple password reset strategy for django rest framework, where users can request password r

Anexia 363 Dec 24, 2022
Google Auth Python Library

Google Auth Python Library This library simplifies using Google's various server-to-server authentication mechanisms to access Google APIs. Installing

Google APIs 598 Jan 07, 2023
This app makes it extremely easy to build Django powered SPA's (Single Page App) or Mobile apps exposing all registration and authentication related functionality as CBV's (Class Base View) and REST (JSON)

Welcome to django-rest-auth Repository is unmaintained at the moment (on pause). More info can be found on this issue page: https://github.com/Tivix/d

Tivix 2.4k Jan 03, 2023
OAuth2 goodies for the Djangonauts!

Django OAuth Toolkit OAuth2 goodies for the Djangonauts! If you are facing one or more of the following: Your Django app exposes a web API you want to

Jazzband 2.7k Dec 31, 2022
Foundation Auth Proxy is an abstraction on Foundations' authentication layer and is used to authenticate requests to Atlas's REST API.

foundations-auth-proxy Setup By default the server runs on http://0.0.0.0:5558. This can be changed via the arguments. Arguments: '-H' or '--host': ho

Dessa - Open Source 2 Jul 03, 2020
Provide OAuth2 access to your app

django-oml Welcome to the documentation for django-oml! OML means Object Moderation Layer, the idea is to have a mixin model that allows you to modera

Caffeinehit 334 Jul 27, 2022
Authentication, JWT, and permission scoping for Sanic

Sanic JWT Sanic JWT adds authentication protection and endpoints to Sanic. It is both easy to get up and running, and extensible for the developer. It

Adam Hopkins 229 Jan 05, 2023
Easy and secure implementation of Azure AD for your FastAPI APIs 🔒 Single- and multi-tenant support.

Easy and secure implementation of Azure AD for your FastAPI APIs 🔒 Single- and multi-tenant support.

Intility 220 Jan 05, 2023
Multi-user accounts for Django projects

django-organizations Summary Groups and multi-user account management Author Ben Lopatin (http://benlopatin.com) Status Separate individual user ident

Ben Lopatin 1.1k Jan 02, 2023
A generic, spec-compliant, thorough implementation of the OAuth request-signing logic

OAuthLib - Python Framework for OAuth1 & OAuth2 *A generic, spec-compliant, thorough implementation of the OAuth request-signing logic for Python 3.5+

OAuthlib 2.5k Jan 01, 2023
This is a Python library for accessing resources protected by OAuth 2.0.

This is a client library for accessing resources protected by OAuth 2.0. Note: oauth2client is now deprecated. No more features will be added to the l

Google APIs 787 Dec 13, 2022
Python module for generating and verifying JSON Web Tokens

python-jwt Module for generating and verifying JSON Web Tokens. Note: From version 2.0.1 the namespace has changed from jwt to python_jwt, in order to

David Halls 210 Dec 24, 2022
Social auth made simple

Python Social Auth Python Social Auth is an easy-to-setup social authentication/registration mechanism with support for several frameworks and auth pr

Matías Aguirre 2.8k Dec 24, 2022
Authentication for Django Rest Framework

Dj-Rest-Auth Drop-in API endpoints for handling authentication securely in Django Rest Framework. Works especially well with SPAs (e.g React, Vue, Ang

Michael 1.1k Jan 03, 2023
Simple extension that provides Basic, Digest and Token HTTP authentication for Flask routes

Flask-HTTPAuth Simple extension that provides Basic and Digest HTTP authentication for Flask routes. Installation The easiest way to install this is t

Miguel Grinberg 1.1k Jan 05, 2023
Social auth made simple

Python Social Auth Python Social Auth is an easy-to-setup social authentication/registration mechanism with support for several frameworks and auth pr

Matías Aguirre 2.8k Dec 24, 2022
Implements authentication and authorization as FastAPI dependencies

FastAPI Security Implements authentication and authorization as dependencies in FastAPI. Features Authentication via JWT-based OAuth 2 access tokens a

Jacob Magnusson 111 Jan 07, 2023
Auth-Starters - Different APIs using Django & Flask & FastAPI to see Authentication Service how its work

Auth-Starters Different APIs using Django & Flask & FastAPI to see Authentication Service how its work, and how to use it. This Repository based on my

Yasser Tahiri 7 Apr 22, 2022
JWT authentication for Pyramid

JWT authentication for Pyramid This package implements an authentication policy for Pyramid that using JSON Web Tokens. This standard (RFC 7519) is of

Wichert Akkerman 73 Dec 03, 2021
Implementation of Supervised Contrastive Learning with AMP, EMA, SWA, and many other tricks

SupCon-Framework The repo is an implementation of Supervised Contrastive Learning. It's based on another implementation, but with several differencies

Ivan Panshin 132 Dec 14, 2022