Simple extension that provides Basic, Digest and Token HTTP authentication for Flask routes

Flask-HTTPAuth

Build status codecov

Simple extension that provides Basic and Digest HTTP authentication for Flask routes.

Installation

The easiest way to install this is through pip.

pip install Flask-HTTPAuth

Basic authentication example

from flask import Flask
from flask_httpauth import HTTPBasicAuth
from werkzeug.security import generate_password_hash, check_password_hash

app = Flask(__name__)
auth = HTTPBasicAuth()

users = {
    "john": generate_password_hash("hello"),
    "susan": generate_password_hash("bye")
}

@auth.verify_password
def verify_password(username, password):
    if username in users and \
            check_password_hash(users.get(username), password):
        return username

@app.route('/')
@auth.login_required
def index():
    return "Hello, %s!" % auth.current_user()

if __name__ == '__main__':
    app.run()

Note: See the documentation for more complex examples that involve password hashing and custom verification callbacks.

Digest authentication example

from flask import Flask
from flask_httpauth import HTTPDigestAuth

app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret key here'
auth = HTTPDigestAuth()

users = {
    "john": "hello",
    "susan": "bye"
}

@auth.get_password
def get_pw(username):
    if username in users:
        return users.get(username)
    return None

@app.route('/')
@auth.login_required
def index():
    return "Hello, %s!" % auth.username()

if __name__ == '__main__':
    app.run()

Resources

Comments
  • inject current user into decorator callbacks

    inject current user into decorator callbacks

    I have recently started using flask-httpauth on a project. Something that jumped out at me is setting the current user into the flask g thread-local object via something like g.current_user = user inside verify_password and verify_token.

    Would it make sense to create a new decorator, something like @auth.current_user, which takes a callback that returns the current user? This callback can then be used to inject the current user as a parameter into the callbacks for the other flask-httpauth decorators, e.g. @auth.verify_password as well as @auth.login_required.

    I think it would be nice to be able to just receive the current user as a parameter and remove the need to use flask's g object.

    Below is a simplified example just to illustrate the idea:

    def login_required(f):    
        user = "jane" # in reality obtain the user from the callback
    
        @wraps(f) 
        def __decorated_function(*args, **kwargs):    
            new_args = (*args, user) # inject user into parameters to `f`
            f(*new_args, **kwargs)
        
        return __decorated_function
    
  • Type annotations / type hints / stub files for mypy

    Type annotations / type hints / stub files for mypy

    Are there plans to add type annotations or stub files for mypy to flask-httpauth?

    As you seem to want to support Python 2, I guess stub files would be the way to go. Would you be open to a PR?

  • Use constant time string comparisons

    Use constant time string comparisons

    This uses constant time string comparisons everywhere that looked plausibly like it might matter:

    • Plaintext password comparison
    • Digest nonce comparison
    • Digest hash comparison

    Fixes #82

  • Concurent Basic auth

    Concurent Basic auth

    Hi, is it possible to make auth concuretly for two users?

    I am using your default Basic authentication example.

    Example: User 1 opened page, basic auth prompt shows up. He didnt write anything and has opened prompt. User 2 wants to open page, but he can't. Loading bar is still loading and BasicAuth prompt is not showing up.

    WSGI app is not serving any requests.

    Is it possible to make this http basic auth non blocking?

    Thanks, Tomas

  • adds role based access control

    adds role based access control

    For my own project i have added a simple way to add role based access control to BasicAuth:

    class HTTPRoleAuth(flask_httpauth.HTTPBasicAuth):
        def __init__(self, scheme=None, realm=None):
            super().__init__(scheme, realm)
            self.get_user_roles_callback = None
    
        def get_user_roles(self, f):
            """ user roles are the roles the user has """
            self.get_user_roles_callback = f
            return f
    
        def roles_required(self, *endpoint_roles):
            """ endpoint roles are the roles (str) the user has to have to get access to the (decorated) endpoint """
            def decorator(f):
                @wraps(f)
                def decorated(*args, **kwargs):
                    """ basically the login_required decorated but with a check of 'authorize' """
                    auth = self.get_auth()
                    if request.method != 'OPTIONS':  # pragma: no cover
                        password = self.get_auth_password(auth)
    
                        if not (self.authenticate(auth, password) and
                                self.authorize(auth, endpoint_roles)):
                            request.data  # empty the stream
                            return self.auth_error_callback()
    
                    return f(*args, **kwargs)
                return decorated
            return decorator
    
        def authorize(self, auth, endpoint_roles):
            """ if any of the roles of the user correspond to the endpoint roles; the user gets access """
            if not auth.username:
                return False
            user_roles = self.get_user_roles_callback(auth.username)
            return any(role in endpoint_roles for role in user_roles)
    

    used as in:

        @route('/try_roles/<data>', methods=['GET'])
        @auth.roles_required("super", "security")
        def try_login_with_roles(self, data):
            """ roles test url """
            return {"success": True, "data": data}
    

    compared to:

        @route('/try_login/<data>', methods=['GET'])
        @auth.login_required
        def try_login(self, data):
            """ login test url """
            return {"success": True, "data": data}
    

    with roles_required a replacement for login_required with the additional requirement that the user has one of the roles (obtained through the get_user_roles callback) set by the decorator.

    Is this something to add to the repo?

  • Support for hashed passwords?

    Support for hashed passwords?

    Have you considered changing this module to support hashed passwords? A simple function that mutated the HTTP provided password before it was compared against the password obtained from @auth.get_password would do the trick. Perhaps a decorator called @auth.hash_password?

    This would let you use Basic Auth over SSL and provide much stronger hashing than what's built into HTTP Digest, and prevent your service from storing plaintext passwords in the DB.

  • HTTPDigestAuth @get_password authentication needs improvements

    HTTPDigestAuth @get_password authentication needs improvements

    So "get_password" is expecting a raw password. We do store passwords in SHA256 and we even dont know how to retrieve a actual password from that hash value. So the function can be improved to accomodate that.

  • How to protect

    How to protect "hidden" endpoints/routes

    How can I protect arbitrary routes in my webapp with HTTPAuth? In particular, using flask-apispec (swagger) I just initialize the extension and optionally provide setting on what the routes are, e. g.:

    from flask_httpauth import HTTPBasicAuth
    from flask_apispec.extension import FlaskApiSpec
    from werkzeug.security import check_password_hash
    
    auth = HTTPBasicAuth()
    docs = FlaskApiSpec()
    
    def register_extensions(app):
        # ...
    
        @auth.verify_password
        def verify_password(username, password):  # pylint: disable=unused-variable
            users = app.config["AUTH_USERS"]
            for user in users:
                if user["name"] != username:
                    continue
                if check_password_hash(user["password"], password):
                    return user
            return False
    
        @auth.get_user_roles
        def get_user_roles(user):  # pylint: disable=unused-variable
            return user["roles"]
    
        # ...
    
        app.config.update(
            {
                "APISPEC_SPEC": ...,
                "APISPEC_SWAGGER_URL": "/swagger/",
                "APISPEC_SWAGGER_UI_URL": "/swagger-ui/",
            }
        )
        docs.init_app(app)
    
        # ...
    

    Flask-HTTPAuth only provides decorators for me, so how can I protect those routes that are not declared by me? (Besides only enabling the apispec extension in Development/Testing environments.)

  • HTTP Method-based role authentiation

    HTTP Method-based role authentiation

    In my REST API webapp, I wanted to provide read-only access as well as read-and-write access using different roles for different HTTP methods, and both protected by HTTPBasicAuth.

    My current (working) solution is:

    # logging just to trace calls
    from functools import wraps
    
    from flask import Blueprint
    from flask import current_app
    from flask import request
    
    from ..app import auth
    
    bp_api = Blueprint("api", __name__)
    
    def login_required_method_filter(role, methods=None):
        def filter_method_decorator(f):
            @wraps(f)
            def decorated_function(*args, **kwargs):
                current_app.logger.warning(
                    "request? - %s - %s of %s - %s",
                    request.endpoint,
                    request.method,
                    methods,
                    role,
                )
                if methods and request.method not in methods:
                    # ignore and continue with request
                    return None
    
                f_authd = auth.login_required(role=role)(f)
                return f_authd(*args, **kwargs)
    
            return decorated_function
    
        return filter_method_decorator
    
    
    @bp_api.before_request
    @login_required_method_filter(role="api-rw", methods=("POST", "DELETE", "PUT"))
    def before_request_rw():
        """Protect write API routes"""
        current_app.logger.warning(
            "before_request_rw - %s - %s", request.endpoint, request.method
        )
    
    
    @bp_api.before_request
    @login_required_method_filter(role="api-ro", methods=("GET", "HEAD", "OPTIONS"))
    def before_request_ro():
        """Protect API routes"""
        # user = auth.current_user()
        # roles = user["roles"]
        # if request.method in ("GET", "HEAD") ...
        current_app.logger.warning(
            "before_request_ro - %s - %s", request.endpoint, request.method
        )
    
    # api methods, using MethodViews or MethodResource from flask_apispec
    
    # app httpauth configs
    "AUTH_USERS": [
        {
            "name": "api-ro",
            "password": generate_password_hash("api-ro"),
            "roles": ["api-ro"],
        },
        {
            "name": "api-rw",
            "password": generate_password_hash("api-rw"),
            "roles": ["api-rw", "api-ro"],
        },
    ]
    

    The idea is that the authentiation with roles only checks if the correct HTTP method is used, else the method is unprotected. But with covering all HTTP methods, one check will work and protect using the given role. So the main issue is when not all HTTP methods are covered with handlers it could potentially let a request through. (methods=None is the catch-all to protect against this)

    In my tests it works reliably. The only problem for me is currently (in my browser) how to disable old tokens, e. g. if I want to switch between read+write and read-only ...

    Maybe this is a use-case that is interesting for others or can even be included into this library.

  • Authentication Help

    Authentication Help

    Hello,

    I am trying to authenticate my API.

    some background information: I am using the HTTPBasicAuth() function (if you recommend anything else for this situation I do not mind changing the code)

    Below is my app.py script

    users={}
    secrets = "./secrets.json" 
    with open(secrets, 'r') as c:
        secrets_json = json.load(c)
    
    user = secrets_json['API']['user']
    password = secrets_json['API']['password']
    users[user] = generate_password_hash(password)
    
    app = Flask(__name__)
    auth = HTTPBasicAuth()
    
    @auth.verify_password
    def verify_password(username, password):
        if username in users:
              return check_password_hash(users.get(username), password)
        return False
    
    @app.route("/",methods=["GET"])
    @auth.login_required
    def main():
        return "Hello World"
    
    @app.route("/annotate/",methods=["GET"])
    @auth.login_required
    def annotate():
    #Annotate functions.....
    
    if __name__ == "__main__":
        http_server = WSGIServer(('0.0.0.0',5000), app)
        http_server.serve_forever()
    
    

    When I am using: curl -u Maria http://localhost:5000/ It asks for authentication, but if I call the annotate function using: curl -u Maria http://localhost:5000/annotate/gene=BRAF&protein_change=V600E&variant_type=MISSENSE

    It doesn't ask for authentication.

    I also want it to ask for authentication when using a web browser. When I ran it the first time, it asked through the browser but that was it. I was reading through your other issues and comments and you said it asks only once, but how can I make it ask every time?

    Thank you very much for any help you can provide! Maria Nakhoul

  • HTTPDigestAuth raise TypeError when get_password() returns None.

    HTTPDigestAuth raise TypeError when get_password() returns None.

    If we implement @auth.get_password as described in the doc:

    @auth.get_password
    def get_pw(username):
        if username in users:
            return users[username]
        return None
    

    When the user logs in with an invalid user name, the script with raise an error:

      File ".../env/lib/python3.3/site-packages/flask_httpauth.py", line 52, in decorated
        if not self.authenticate(auth, password):
      File ".../env/lib/python3.3/site-packages/flask_httpauth.py", line 108, in authenticate
        a1 = auth.username + ":" + auth.realm + ":" + password
    TypeError: Can't convert 'NoneType' object to str implicitly
    

    (BTW the test suite does not catch this because it simply makes every invalid user's password being 'other'. This is totally wrong, and should return some non-string to indicate unconditional rejection.)

  • Feature request: Support application factories

    Feature request: Support application factories

    It would be nice if you could support application factories. Then would be this possible:

    from flask_httpauth import HTTPBasicAuth
    
    auth = HTTPBasicAuth()
    
    def create_app():
        app = Flask(__name__)
        auth.init_app(app)
    
  • Chrome is asking for password every time

    Chrome is asking for password every time

    Hello, I'm using Basic auth as provided in the tutorial example. the entry looks like

    @app.route('/', methods=['GET', 'POST', 'OPTIONS', 'PUT', 'DELETE', 'HEAD', 'PATCH'])
    @app.route('/<path:input_path>', methods=['GET', 'POST', 'OPTIONS', 'PUT', 'DELETE', 'HEAD', 'PATCH'])
    @auth.login_required
    def enter(input_path='/'):
    

    Interestingly, firefox seems to remember the login information correctly and only asks for it once.

  • Type of g.flask_httpauth_user

    Type of g.flask_httpauth_user

    After successful authentication, What is the type of g.flask_httpauth_user that is added by the @login_required decorator?

    I expect to find user object, But As I can understand from the source code, It is always str or None. In the case of string it will be the username. If my note is true, It will is better to change this behavior to store user object. If this will break the backword compatibility, I suggest to add optional user loader callback, that will be called after successful login.

    The current behavior break the example mentioned :

    `@bp.route('/tokens', methods=['POST'])
     @basic_auth.login_required
    def get_token():
        token = basic_auth.current_user().get_token()
        db.session.commit()
        return jsonify({'token': token})` 
    

    basic_auth.current_user() return the g.flask_httpauth_user which has no method named get_token()

    Thank you.

    Version: 4.4.0

  • custom htpasswd file for specific tenant passed as argument's endpoint

    custom htpasswd file for specific tenant passed as argument's endpoint

    Hello Miguel,

    I have a use case where for some REST endpoints I want to use the HTTP Basic Auth but let authentication run against custom htpasswd file. For example, for endpoint http://localhost:5000/api/someAPIcall I want to pass specific tenant and authentication will run against his/her htpasswd file - look up via file system in specific directory. My goal is to separate different tenants (customers) and not to use just one big common htpasswd file for all of them.

    Unfortunately, I am not that deep into Python and Flask, tough digging currently on everyday basis. Looking at the functions and wondering how to do that.

Flask JWT Router is a Python library that adds authorised routes to a Flask app.
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'

Mar 26, 2022
User Authentication in Flask using Flask-Login
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

May 20, 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

May 13, 2022
Mock authentication API that acceccpts email and password and returns authentication result.

Mock authentication API that acceccpts email and password and returns authentication result.

Feb 11, 2022
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

May 22, 2022
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

May 19, 2022
A JSON Web Token authentication plugin for the Django REST Framework.

Simple JWT Abstract Simple JWT is a JSON Web Token authentication plugin for the Django REST Framework. For full documentation, visit django-rest-fram

May 23, 2022
JSON Web Token Authentication support for Django REST Framework

REST framework JWT Auth Notice This project is currently unmaintained. Check #484 for more details and suggested alternatives. JSON Web Token Authenti

May 8, 2022
JSON Web Token Authentication support for Django REST Framework

REST framework JWT Auth JSON Web Token Authentication support for Django REST Framework Overview This package provides JSON Web Token Authentication s

May 18, 2022
A JSON Web Token authentication plugin for the Django REST Framework.

Simple JWT Abstract Simple JWT is a JSON Web Token authentication plugin for the Django REST Framework. For full documentation, visit django-rest-fram

May 18, 2022
A JSON Web Token authentication plugin for the Django REST Framework.

Simple JWT Abstract Simple JWT is a JSON Web Token authentication plugin for the Django REST Framework. For full documentation, visit django-rest-fram

May 20, 2022
Simple Login - Login Extension for Flask - maintainer @cuducos
Simple Login - Login Extension for Flask - maintainer @cuducos

Login Extension for Flask The simplest way to add login to flask! Top Contributors Add yourself, send a PR! How it works First install it from PyPI. p

May 18, 2022
Simple Login - Login Extension for Flask - maintainer @cuducos
Simple Login - Login Extension for Flask - maintainer @cuducos

Login Extension for Flask The simplest way to add login to flask! Top Contributors Add yourself, send a PR! How it works First install it from PyPI. p

Feb 10, 2021
Simple Login - Login Extension for Flask - maintainer @cuducos
Simple Login - Login Extension for Flask - maintainer @cuducos

Login Extension for Flask The simplest way to add login to flask! How it works First, install it from PyPI: $ pip install flask_simplelogin Then, use

May 11, 2022
FastAPI extension that provides JWT Auth support (secure, easy to use, and lightweight)

FastAPI JWT Auth Documentation: https://indominusbyte.github.io/fastapi-jwt-auth Source Code: https://github.com/IndominusByte/fastapi-jwt-auth Featur

May 16, 2022
Boilerplate/Starter Project for building RESTful APIs using Flask, SQLite, JWT authentication.

auth-phyton Boilerplate/Starter Project for building RESTful APIs using Flask, SQLite, JWT authentication. Setup Step #1 - Install dependencies $ pip

Dec 26, 2021
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

Apr 22, 2022
A flask extension for managing permissions and scopes

Flask-Pundit A simple flask extension to organize resource authorization and scoping. This extension is heavily inspired by the ruby Pundit library. I

May 4, 2022
A flask extension for managing permissions and scopes

Flask-Pundit A simple flask extension to organize resource authorization and scoping. This extension is heavily inspired by the ruby Pundit library. I

Jan 23, 2021