Skip to main content

Password authentication for Plain.

Project description

plain.passwords

Password hashing, validation, and authentication views for Plain.

Overview

You can hash and verify passwords using the hash_password and check_password functions:

from plain.passwords.hashers import hash_password, check_password

# Hash a password for storage
hashed = hash_password("my-secret-password")
# Returns something like: pbkdf2_sha256$720000$abc123...$xyz789...

# Verify a password against a hash
is_valid = check_password("my-secret-password", hashed)
# Returns True

For user authentication, you can use the built-in views. Add PasswordLoginView to your URLs:

# app/urls.py
from plain.urls import path
from plain.passwords.views import PasswordLoginView

urlpatterns = [
    path("login/", PasswordLoginView, name="login"),
]

Password hashing

Passwords are hashed using PBKDF2 with SHA256 by default. The hash_password function generates a secure hash:

from plain.passwords.hashers import hash_password

hashed = hash_password("user-password")

The check_password function verifies a password against a stored hash. It also handles automatic hash upgrades when the hashing algorithm changes:

from plain.passwords.hashers import check_password

def setter(new_hash):
    # Called when the hash needs to be upgraded
    user.password = new_hash
    user.save()

is_valid = check_password("user-password", stored_hash, setter=setter)

You can configure which hashers are available via the PASSWORD_HASHERS setting. The first hasher in the list is used for new passwords:

# app/settings.py
PASSWORD_HASHERS = [
    "plain.passwords.hashers.PBKDF2PasswordHasher",
]

To create a custom hasher, subclass BasePasswordHasher and implement the required methods.

Password validation

Three validators are included for checking password strength:

from plain.passwords.validators import (
    MinimumLengthValidator,
    CommonPasswordValidator,
    NumericPasswordValidator,
)
from plain.exceptions import ValidationError

validators = [
    MinimumLengthValidator(min_length=10),
    CommonPasswordValidator(),
    NumericPasswordValidator(),
]

password = "test"
for validator in validators:
    try:
        validator(password)
    except ValidationError as e:
        print(e.message)

PasswordField

PasswordField is a model field that automatically hashes passwords before saving. It includes all three validators by default:

from plain import models
from plain.passwords.models import PasswordField

@models.register_model
class User(models.Model):
    email = models.EmailField(unique=True)
    password = PasswordField()

When you assign a raw password, it gets hashed automatically on save:

user = User(email="user@example.com", password="my-password")
user.save()
# user.password is now a hash like: pbkdf2_sha256$720000$...

For better type checking support, you can import from plain.passwords.types:

from plain.passwords.types import PasswordField

Views

All views are designed to work with plain.auth for session management.

Login

PasswordLoginView handles email/password authentication:

from plain.urls import path
from plain.passwords.views import PasswordLoginView

urlpatterns = [
    path("login/", PasswordLoginView, name="login"),
]

You can customize the success URL:

class MyLoginView(PasswordLoginView):
    success_url = "/dashboard/"

Signup

PasswordSignupView creates new users with email and password:

from plain.urls import path
from plain.passwords.views import PasswordSignupView

urlpatterns = [
    path("signup/", PasswordSignupView, name="signup"),
]

Password change

PasswordChangeView lets authenticated users change their password by entering their current password:

from plain.urls import path
from plain.passwords.views import PasswordChangeView

urlpatterns = [
    path("password/change/", PasswordChangeView, name="password_change"),
]

Password reset

Password reset requires two views and an email template. PasswordForgotView sends the reset email, and PasswordResetView handles the token and new password:

from plain.urls import path
from plain.passwords.views import PasswordForgotView, PasswordResetView

class MyPasswordForgotView(PasswordForgotView):
    reset_confirm_url_name = "password_reset"
    success_url = "/login/"

class MyPasswordResetView(PasswordResetView):
    success_url = "/login/"

urlpatterns = [
    path("password/forgot/", MyPasswordForgotView, name="password_forgot"),
    path("password/reset/", MyPasswordResetView, name="password_reset"),
]

You need to create a password_reset email template for plain.email. The template receives email, user, and url in its context.

Forms

Several forms are available for building custom authentication flows:

FAQs

How do I customize the login form?

Subclass PasswordLoginForm and set form_class on your view:

from plain.passwords.forms import PasswordLoginForm
from plain.passwords.views import PasswordLoginView

class MyLoginForm(PasswordLoginForm):
    # Add custom fields or validation
    pass

class MyLoginView(PasswordLoginView):
    form_class = MyLoginForm

How do I customize password validation?

Pass custom validators to PasswordField:

from plain.passwords.models import PasswordField
from plain.passwords.validators import MinimumLengthValidator

password = PasswordField(validators=[
    MinimumLengthValidator(min_length=12),
])

How do I use a different hashing algorithm?

Add your hasher to PASSWORD_HASHERS. The first one is used for new passwords:

PASSWORD_HASHERS = [
    "myapp.hashers.Argon2PasswordHasher",
    "plain.passwords.hashers.PBKDF2PasswordHasher",  # For existing passwords
]

How long are password reset tokens valid?

By default, tokens expire after 1 hour. Override reset_token_max_age on PasswordResetView to change this:

class MyPasswordResetView(PasswordResetView):
    reset_token_max_age = 60 * 60 * 24  # 24 hours

Installation

Install the package from PyPI:

uv add plain.passwords

Add the password field to your User model:

# app/models.py
from plain import models
from plain.passwords.models import PasswordField

@models.register_model
class User(models.Model):
    email = models.EmailField(unique=True)
    password = PasswordField()

Add login and logout views to your URLs:

# app/urls.py
from plain.urls import path
from plain.auth.views import LogoutView
from plain.passwords.views import PasswordLoginView

urlpatterns = [
    path("login/", PasswordLoginView, name="login"),
    path("logout/", LogoutView, name="logout"),
]

Create templates for your views. For the login view, create templates/passwords/passwordlogin.html:

{% extends "base.html" %}

{% block content %}
<form method="post">
    {{ csrf_input }}
    {{ form.as_elements }}
    <button type="submit">Log in</button>
</form>
{% endblock %}

For password resets, install plain.email and create a reset email template.

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

plain_passwords-0.22.0.tar.gz (101.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

plain_passwords-0.22.0-py3-none-any.whl (104.8 kB view details)

Uploaded Python 3

File details

Details for the file plain_passwords-0.22.0.tar.gz.

File metadata

  • Download URL: plain_passwords-0.22.0.tar.gz
  • Upload date:
  • Size: 101.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.9.25 {"installer":{"name":"uv","version":"0.9.25","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for plain_passwords-0.22.0.tar.gz
Algorithm Hash digest
SHA256 ac48ebdc2510cef7ead0eafbe10f2fd7f1a68de0de79148b725da772e391d1e1
MD5 91f437fcdcf7c1d0ddbfdfeaa1939cc0
BLAKE2b-256 e64859facc8adfdeed869b0dcce3ad82495da5e5363ca32f62d439ce7d44ea33

See more details on using hashes here.

File details

Details for the file plain_passwords-0.22.0-py3-none-any.whl.

File metadata

  • Download URL: plain_passwords-0.22.0-py3-none-any.whl
  • Upload date:
  • Size: 104.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.9.25 {"installer":{"name":"uv","version":"0.9.25","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for plain_passwords-0.22.0-py3-none-any.whl
Algorithm Hash digest
SHA256 fd526404a4fed7b9a364178650191e43cbf1a16d0cc84e44f42d090a9c221e57
MD5 3984317090dec9d2029fa67e614e2e92
BLAKE2b-256 0b478de3d79919580de70809e2b0165508f54332dd6f08502e2f3bf41e2e091b

See more details on using hashes here.

Supported by

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