Skip to main content

The Flask HTTP Digest Authentication project.

Project description

Description

Flask-DigestAuth is an HTTP Digest Authentication implementation for Flask applications. It authenticates the user for the protected views.

HTTP Digest Authentication is specified in RFC 2617.

Refer to the full Flask-DigestAuth readthedocs documentation.

Why HTTP Digest Authentication?

HTTP Digest Authentication has the advantage that it does not send thee actual password to the server, which greatly enhances the security. It uses the challenge-response authentication scheme. The client returns the response calculated from the challenge and the password, but not the original password.

Log in forms has the advantage of freedom, in the senses of both the visual design and the actual implementation. You may implement your own challenge-response log in form, but then you are reinventing the wheels. If a pretty log in form is not critical to your project, HTTP Digest Authentication should be a good choice.

Flask-DigestAuth works with Flask-Login. Log in protection can be separated with the authentication mechanism. You can create protected Flask modules without knowing the actual authentication mechanisms.

Installation

You can install Flask-DigestAuth with pip:

pip install Flask-DigestAuth

You may also install the latest source from the Flask-DigestAuth GitHub repository.

pip install git+https://github.com/imacat/flask-digestauth.git

Setting the Password

The password hash of the HTTP Digest Authentication is composed of the realm, the username, and the password. Example for setting the password:

from flask_digest_auth import make_password_hash

user.password = make_password_hash(realm, username, password)

The username is part of the hash. If the user changes their username, you need to ask their password, to generate and store the new password hash.

Flask-DigestAuth Alone

Flask-DigestAuth can authenticate the users alone.

Simple Applications with Flask-DigestAuth Alone

In your my_app.py:

from flask import Flask, request, redirect
from flask_digest_auth import DigestAuth

app: flask = Flask(__name__)
... (Configure the Flask application) ...

auth: DigestAuth = DigestAuth(realm="Admin")
auth.init_app(app)

@auth.register_get_password
def get_password_hash(username: str) -> t.Optional[str]:
    ... (Load the password hash) ...

@auth.register_get_user
def get_user(username: str) -> t.Optional[t.Any]:
    ... (Load the user) ...

@app.get("/admin")
@auth.login_required
def admin():
    return f"Hello, {g.user.username}!"

@app.post("/logout")
@auth.login_required
def logout():
    auth.logout()
    return redirect(request.form.get("next"))

Larger Applications with create_app() with Flask-DigestAuth Alone

In your my_app/__init__.py:

from flask import Flask
from flask_digest_auth import DigestAuth

auth: DigestAuth = DigestAuth()

def create_app(test_config = None) -> Flask:
    app: flask = Flask(__name__)
    ... (Configure the Flask application) ...

    auth.realm = app.config["REALM"]
    auth.init_app(app)

    @auth.register_get_password
    def get_password_hash(username: str) -> t.Optional[str]:
        ... (Load the password hash) ...

    @auth.register_get_user
    def get_user(username: str) -> t.Optional[t.Any]:
        ... (Load the user) ...

    return app

In your my_app/views.py:

from my_app import auth
from flask import Flask, Blueprint, request, redirect

bp = Blueprint("admin", __name__, url_prefix="/admin")

@bp.get("/admin")
@auth.login_required
def admin():
    return f"Hello, {g.user.username}!"

@app.post("/logout")
@auth.login_required
def logout():
    auth.logout()
    return redirect(request.form.get("next"))

def init_app(app: Flask) -> None:
    app.register_blueprint(bp)

Flask-Login Integration

Flask-DigestAuth works with Flask-Login. You can write a Flask module that requires log in, without specifying how to log in. The application can use either HTTP Digest Authentication, or the log in forms, as needed.

To use Flask-Login with Flask-DigestAuth, login_manager.init_app(app) must be called before auth.init_app(app).

The currently logged-in user can be retrieved at flask_login.current_user, if any.

The views only depend on Flask-Login, but not the Flask-DigestAuth. You can change the actual authentication mechanism without changing the views.

Simple Applications with Flask-Login Integration

In your my_app.py:

import flask_login
from flask import Flask, request, redirect
from flask_digest_auth import DigestAuth

app: flask = Flask(__name__)
... (Configure the Flask application) ...

login_manager: flask_login.LoginManager = flask_login.LoginManager()
login_manager.init_app(app)

@login_manager.user_loader
def load_user(user_id: str) -> t.Optional[User]:
    ... (Load the user with the username) ...

auth: DigestAuth = DigestAuth(realm="Admin")
auth.init_app(app)

@auth.register_get_password
def get_password_hash(username: str) -> t.Optional[str]:
    ... (Load the password hash) ...

@app.get("/admin")
@flask_login.login_required
def admin():
    return f"Hello, {flask_login.current_user.get_id()}!"

@app.post("/logout")
@flask_login.login_required
def logout():
    auth.logout()
    # Do not call flask_login.logout_user()
    return redirect(request.form.get("next"))

Larger Applications with create_app() with Flask-Login Integration

In your my_app/__init__.py:

from flask import Flask
from flask_digest_auth import DigestAuth
from flask_login import LoginManager

auth: DigestAuth = DigestAuth()

def create_app(test_config = None) -> Flask:
    app: flask = Flask(__name__)
    ... (Configure the Flask application) ...

    login_manager: LoginManager = LoginManager()
    login_manager.init_app(app)

    @login_manager.user_loader
    def load_user(user_id: str) -> t.Optional[User]:
        ... (Load the user with the username) ...

    auth.realm = app.config["REALM"]
    auth.init_app(app)

    @auth.register_get_password
    def get_password_hash(username: str) -> t.Optional[str]:
        ... (Load the password hash) ...

    return app

In your my_app/views.py:

import flask_login
from flask import Flask, Blueprint, request, redirect
from my_app import auth

bp = Blueprint("admin", __name__, url_prefix="/admin")

@bp.get("/admin")
@flask_login.login_required
def admin():
    return f"Hello, {flask_login.current_user.get_id()}!"

@app.post("/logout")
@flask_login.login_required
def logout():
    auth.logout()
    # Do not call flask_login.logout_user()
    return redirect(request.form.get("next"))

def init_app(app: Flask) -> None:
    app.register_blueprint(bp)

The views only depend on Flask-Login, but not the actual authentication mechanism. You can change the actual authentication mechanism without changing the views.

Session Integration

Flask-DigestAuth features session integration. The user log in is remembered in the session. The authentication information is not requested again. This is different to the practice of the HTTP Digest Authentication, but is convenient for the log in accounting.

Log In Bookkeeping

You can register a callback to run when the user logs in, for ex., logging the log in event, adding the log in counter, etc.

@auth.register_on_login
def on_login(user: User) -> None:
    user.visits = user.visits + 1

Log Out

Flask-DigestAuth supports log out. The user will be prompted for the new username and password.

Test Client

Flask-DigestAuth comes with a test client that supports HTTP digest authentication.

A unittest Test Case

from flask import Flask
from flask_digest_auth import Client
from flask_testing import TestCase
from my_app import create_app

class MyTestCase(TestCase):

    def create_app(self):
        app: Flask = create_app({
            "SECRET_KEY": token_urlsafe(32),
            "TESTING": True
        })
        app.test_client_class = Client
        return app

    def test_admin(self):
        response = self.client.get("/admin")
        self.assertEqual(response.status_code, 401)
        response = self.client.get(
            "/admin", digest_auth=(USERNAME, PASSWORD))
        self.assertEqual(response.status_code, 200)

A pytest Test

import pytest
from flask import Flask
from flask_digest_auth import Client
from my_app import create_app

@pytest.fixture()
def app():
    app: Flask = create_app({
        "SECRET_KEY": token_urlsafe(32),
        "TESTING": True
    })
    app.test_client_class = Client
    yield app

@pytest.fixture()
def client(app):
    return app.test_client()

def test_admin(app: Flask, client: Client):
    with app.app_context():
        response = client.get("/admin")
        assert response.status_code == 401
        response = client.get(
            "/admin", digest_auth=(USERNAME, PASSWORD))
        assert response.status_code == 200

Authors

imacat
2022/11/23

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

Flask-DigestAuth-0.4.0.tar.gz (20.6 kB view details)

Uploaded Source

Built Distribution

Flask_DigestAuth-0.4.0-py3-none-any.whl (16.8 kB view details)

Uploaded Python 3

File details

Details for the file Flask-DigestAuth-0.4.0.tar.gz.

File metadata

  • Download URL: Flask-DigestAuth-0.4.0.tar.gz
  • Upload date:
  • Size: 20.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.2 CPython/3.10.7

File hashes

Hashes for Flask-DigestAuth-0.4.0.tar.gz
Algorithm Hash digest
SHA256 3a549dc6d93407beded27a01a48fb908dc151d7bc455532dbea77f00155ee2e4
MD5 5f6928af92b5e34238f5b11ad988086a
BLAKE2b-256 62a26986d80acca83e25cd27d91b0290ca30687e02692823dce00298852f961e

See more details on using hashes here.

File details

Details for the file Flask_DigestAuth-0.4.0-py3-none-any.whl.

File metadata

File hashes

Hashes for Flask_DigestAuth-0.4.0-py3-none-any.whl
Algorithm Hash digest
SHA256 e1f736882b5d03de1c5655374901e5d564101b0a32f1f3b1cc80e1d071c21c62
MD5 da0e120fd62cb3333c289697c5e6df4a
BLAKE2b-256 054bb59acad75e3d01143c077e9f812be220d2a077cdb71919187703e8898fbb

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page