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
Customizable User Authorization & User Management: Register, Confirm, Login, Change username/password, Forgot password and more.

Flask-User v1.0 Attention: Flask-User v1.0 is a Production/Stable version. The previous version is Flask-User v0.6. User Authentication and Management

Ling Thio 997 Jan 6, 2023
Customizable User Authorization & User Management: Register, Confirm, Login, Change username/password, Forgot password and more.

Flask-User v1.0 Attention: Flask-User v1.0 is a Production/Stable version. The previous version is Flask-User v0.6. User Authentication and Management

Ling Thio 916 Feb 15, 2021
User Authentication in Flask using Flask-Login

User-Authentication-in-Flask Set up & Installation. 1 .Clone/Fork the git repo and create an environment Windows git clone https://github.com/Dev-Elie

ONDIEK ELIJAH OCHIENG 31 Dec 11, 2022
This script helps you log in to your LMS account and enter the currently running session

This script helps you log in to your LMS account and enter the currently running session, all in a second

Ali Ebrahimi 5 Sep 1, 2022
This program automatically logs you into a Zoom session at your alloted time

This program automatically logs you into a Zoom session at your alloted time. Optionally you can choose to have end the session at your allotted time.

null 9 Sep 19, 2022
Flask JWT Router is a Python library that adds authorised routes to a Flask app.

Read the docs: Flask-JWT-Router Flask JWT Router Flask JWT Router is a Python library that adds authorised routes to a Flask app. Both basic & Google'

Joe Gasewicz 52 Jan 3, 2023
Integrated set of Django applications addressing authentication, registration, account management as well as 3rd party (social) account authentication.

Welcome to django-allauth! Integrated set of Django applications addressing authentication, registration, account management as well as 3rd party (soc

Raymond Penners 7.7k Jan 1, 2023
Ready-to-use and customizable users management for FastAPI

FastAPI Users Ready-to-use and customizable users management for FastAPI Documentation: https://frankie567.github.io/fastapi-users/ Source Code: https

François Voron 2.4k Jan 4, 2023
Integrated set of Django applications addressing authentication, registration, account management as well as 3rd party (social) account authentication.

Welcome to django-allauth! Integrated set of Django applications addressing authentication, registration, account management as well as 3rd party (soc

Raymond Penners 7.7k Jan 3, 2023
User-related REST API based on the awesome Django REST Framework

Django REST Registration User registration REST API, based on Django REST Framework. Documentation Full documentation for the project is available at

Andrzej Pragacz 399 Jan 3, 2023
A host-guest based app in which host can CREATE the room. and guest can join room with room code and vote for song to skip. User is authenticated using Spotify API

A host-guest based app in which host can CREATE the room. and guest can join room with room code and vote for song to skip. User is authenticated using Spotify API

Aman Raj 5 May 10, 2022
:couple: 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 9, 2023
Django-registration (redux) provides user registration functionality for Django websites.

Description: Django-registration provides user registration functionality for Django websites. maintainers: Macropin, DiCato, and joshblum contributor

Andrew Cutler 920 Jan 8, 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 2, 2023
A secure authentication module to validate user credentials in a Streamlit application.

Streamlit-Authenticator A secure authentication module to validate user credentials in a Streamlit application. Installation Streamlit-Authenticator i

M Khorasani 336 Dec 31, 2022
examify-io is an online examination system that offers automatic grading , exam statistics , proctoring and programming tests , multiple user roles

examify-io is an online examination system that offers automatic grading , exam statistics , proctoring and programming tests , multiple user roles ( Examiner , Supervisor , Student )

Ameer Nasser 4 Oct 28, 2021
Spotify User Token Generator Template

Spotify User Token Generator Template Quick Start $ pip3 install -r requirements

Arda Soyer 1 Feb 1, 2022
An open source Flask extension that provides JWT support (with batteries included)!

Flask-JWT-Extended Features Flask-JWT-Extended not only adds support for using JSON Web Tokens (JWT) to Flask for protecting views, but also many help

Landon Gilbert-Bland 1.4k Jan 4, 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 5, 2023