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

Overview

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
    
    question 
    opened by nestedsoftware 12
  • 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?

    question 
    opened by MartinThoma 11
  • 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

    opened by brendanlong 9
  • 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

    question 
    opened by lysektomas 8
  • 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?

    enhancement 
    opened by gemerden 8
  • 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.

    opened by cliffmcc 8
  • 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.

    question 
    opened by kasanitej 7
  • 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.)

    question 
    opened by Querela 7
  • 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.

    question 
    opened by Querela 7
  • 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

    question 
    opened by marianakhoul 7
  • 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.)

    opened by kennytm 7
  • 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)
    
    question 
    opened by ikreb7 1
  • 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.

    question 
    opened by duhd1993 3
  • 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

    question 
    opened by tabebqena 4
  • 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.

    question 
    opened by kovacivo 1
  • auth.login_required does not work correctly on flask-socketio connect handler

    auth.login_required does not work correctly on flask-socketio connect handler

    I noticed that using @auth.login_required doesn't work when used on a socketio connect handler. It prevents the function from running, which makes it appear that it works, but it does not actually prevent the connection. This means that unauthenticated clients will be connected, and will receive events. Example:

    @socketio.on('connect')
    @auth.login_required
    def connect():
      # this handler does not run, but the client is still connected
      app.logger.info('connected')
    

    I believe that this is due to the fact that the connection rejection in http handlers and in socketio handlers does not work the same way - socketio connect handlers are supposed to return False to reject a connection, while http handlers send a status code etc. I resorted to using the following approach, which appears to work:

    @socketio.on('connect')
    @auth.login_required(optional=True)
    def connect():
        if not auth.current_user():
            app.logger.info('rejected client')
            # This works - reject clients by returning False.
            return False
        app.logger.info(f'client connected, current_user = {auth.current_user()}')
    

    A full working example is going to be a bit of a pain to write since it requires both a server and a client, but I can do that if it helps.

    On a side note, my understanding is that only the connect handler needs to be protected by authentication, and all the other socketio handlers are protected implicitly since clients can't connect. Is this correct?

    enhancement 
    opened by aisotton 7
Owner
Miguel Grinberg
Miguel Grinberg
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
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
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
Mock authentication API that acceccpts email and password and returns authentication result.

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

Herman Shpryhau 1 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

Raymond Penners 7.7k Jan 1, 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
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

Simple JWT 3.3k Jan 1, 2023
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

José Padilla 3.2k Dec 31, 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

Styria Digital Development 178 Jan 2, 2023
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

Jazzband 3.2k Dec 29, 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

Jazzband 3.2k Dec 28, 2022
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

Flask Extensions 181 Jan 1, 2023
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

Flask Extensions 132 Feb 10, 2021
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

Flask Extensions 181 Jan 1, 2023
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

Nyoman Pradipta Dewantara 468 Jan 1, 2023
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

sandhika 0 Aug 3, 2022
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
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

Anurag Chaudhury 49 Dec 23, 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

Anurag Chaudhury 39 Jan 23, 2021