Functional Weave
Code in Python

Suites · auth

User accounts and authentication

Sign-up and login forms, password policy and hashing, login lockout, and signed access tokens (JWT) read from the Authorization header.

34 capabilities (12 core, 22 optional) and 12 gaps, in build order. Put each line in the file that calls it, then fune build.

1. Sign-up and login forms

Check what people type into the account forms, the same way in the browser and the API.

auth.validate-registration core
Validates a sign-up form (email, password, name) in one call, with a message per field.
Validate a sign-up form (email, password, name) in one call, with a message per field, in the browser and the API alike.
from fune.auth.validate_registration import validate_registration  # auth.validate-registration@^1
auth.validate-login core
Checks a login form and gives the normalised email to look the account up by.
Check a login form (email, password): the normalised email to look up, or a message for each field to fix.
from fune.auth.validate_login import validate_login  # auth.validate-login@^1
auth.normalise-email core
Trims an email and lower-cases its domain, so one address is one account.
Trim an email address and lower-case its domain for storage and lookup, or null if it is not a plausible address.
from fune.auth.normalise_email import normalise_email  # auth.normalise-email@^1
auth.validate-password-change optional
Checks a change-password form: current password given and correct, new one meeting the policy and different.
Check a change-password form: current password given and correct, new one meeting the policy and different.
from fune.auth.validate_password_change import validate_password_change  # auth.validate-password-change@^1

2. Passwords

Decide what passwords are acceptable and store them safely.

auth.password-policy core
Checks a new password against NIST SP 800-63B-4 by default: length, common passwords, name and email.
Check a new password against a named policy (NIST SP 800-63B-4 by default): length, common passwords, name and email.
from fune.auth.password_policy import password_policy, check_password  # auth.password-policy@^1
auth.password-hash core
Hashes passwords as PBKDF2-SHA256 for storage, verifies in constant time, and flags hashes to upgrade.
Hash a password for storage as pbkdf2_sha256$iterations$salt$hash, verify one in constant time, and spot weak hashes.
from fune.auth.password_hash import hash_password, verify_password, password_needs_rehash  # auth.password-hash@^1
crypto.pbkdf2-sha256 optional
The PBKDF2 key derivation underneath, if you need it directly.
PBKDF2 with HMAC-SHA256 (RFC 8018): derive a key from a password and salt, slowly, fast enough for a login request.
from fune.crypto.pbkdf2_sha256 import pbkdf2_sha256  # crypto.pbkdf2-sha256@^1
crypto.constant-time-equal optional
Compares MACs and hashes in constant time, for your own token or signature checks.
Compare two byte strings in time that does not depend on where they differ, for checking MACs and hashes.
from fune.crypto.constant_time_equal import constant_time_equal  # crypto.constant-time-equal@^1
  • breached-passwords Checking passwords against a breach corpus (Have I Been Pwned's range API) is network work; the policy's common-password list is built in.

3. Login protection

Slow down password guessing.

auth.login-throttle core
Allows a login attempt or locks the account out from its recent failures, with the seconds until retry.
Allow a login attempt or lock the account out, from its recent failed attempts, with the seconds until it may retry.
from fune.auth.login_throttle import login_throttle  # auth.login-throttle@^1
http.retry-after optional
Reads a Retry-After header, for a client that backs off when locked out or rate limited.
Seconds to wait from an HTTP Retry-After header (delay-seconds or an HTTP date), or null if missing or malformed.
from fune.http.retry_after import retry_after_seconds  # http.retry-after@^1
  • ip-rate-limiting Rate limiting by IP address across accounts, with its shared counter store (Redis, a database).

4. Tokens

Issue and check signed tokens for an API or a single-page app.

auth.access-token core
Issues HS256 access tokens, reads them from the Authorization header, and refuses revoked or superseded ones.
Issue an API's HS256 access token, read one from an Authorization header, and refuse revoked or superseded ones.
from fune.auth.access_token import issue_access_token, read_access_token, confirm_access_token  # auth.access-token@^1
auth.bearer-token core
Takes the token out of an "Authorization: Bearer ..." header, or null.
The token from an HTTP Authorization header of the form "Bearer <token>" (RFC 6750), or null if it has none.
from fune.auth.bearer_token import parse_bearer_token  # auth.bearer-token@^1
auth.jwt optional
Signs, verifies and decodes HS256 JWTs with exp/nbf/iat checks, for tokens of your own shape.
Sign, verify and decode HS256 JSON Web Tokens (RFC 7519): signature, algorithm and exp/nbf/iat checked with leeway.
from fune.auth.jwt import sign_jwt, verify_jwt, decode_jwt  # auth.jwt@^1
crypto.hmac-sha256 optional
HMAC-SHA256, for signing webhooks, links or cookies.
HMAC-SHA256 of a message under a secret key (RFC 2104, RFC 4231), in pure code that also runs in the browser.
from fune.crypto.hmac_sha256 import hmac_sha256  # crypto.hmac-sha256@^1
crypto.sha256 optional
SHA-256 digests, for storing API keys or reset tokens hashed.
The SHA-256 digest of a byte string (FIPS 180-4), in pure code that also runs in the browser.
from fune.crypto.sha256 import sha256  # crypto.sha256@^1
encoding.base64 optional
Base64 and base64url, for encoding tokens and keys.
Bytes to base64 and base64url text and back (RFC 4648), with strict decoding, identically in every language.
from fune.encoding.base64 import base64_encode, base64_decode, base64_url_encode, base64_url_decode  # encoding.base64@^1
time.countdown optional
Seconds left until a token expires and a timer text, for "session expires in 4:05".
Seconds left until a Unix time, whether it has passed, and a timer text like 4:05, for tokens or retry waits.
from fune.time.countdown import countdown  # time.countdown@^1
  • sessions Server-side sessions and cookies: storing them, Secure/HttpOnly/SameSite flags, CSRF tokens and expiry are yours.
  • asymmetric-jwt RS256/ES256 tokens and JWKS key sets (for third-party identity providers): auth.jwt is HS256 only.

5. Forms in React

Accessible account pages in a React front end (TypeScript only).

react.form.password-field optional
A password input with Show/Hide and a live checklist of the password policy.
A password input with a Show/Hide button and an optional live checklist of a password policy (GOV.UK password input).
Not available in Python: typescript only
react.form.text-field optional
Labelled email and name inputs with hints and errors.
A labelled single-line text input with hint, error message, autocomplete, prefix and suffix (GOV.UK text input).
Not available in Python: typescript only
react.form.error-summary optional
The "There is a problem" box linking to each field in error.
A "There is a problem" box linking to each field in error, focused on arrival, built from a validator's fields (GOV.UK).
Not available in Python: typescript only
form.state optional
Form values, touched fields and validator errors as a reducer for useReducer.
A form's state as a pure reducer: values, touched fields, validator errors and the submit, for useReducer.
Not available in Python: typescript only

6. Contact details from validation-basics

Check and normalise a person's contact details before storing them.

validation.email core
Is an email address plausible, by a documented subset of RFC 5322.
Is this a plausible email address? A documented, pragmatic subset of RFC 5322, not the full grammar.
from fune.validation.email import is_email  # validation.email@^1
validation.phone-e164 core
Normalises a phone number to +44… E.164 for a default country.
Normalise a phone number to E.164 (+447700900123) using a default country's trunk and dialling prefixes.
from fune.validation.phone_e164 import validate_phone_e164  # validation.phone-e164@^2
validation.uk-postcode core
Validates a UK postcode and puts it in canonical form.
Validate a UK postcode and normalise it to canonical upper case with one space before the inward code.
from fune.validation.uk_postcode import validate_uk_postcode  # validation.uk-postcode@^2
text.normalise-name optional
Tidies a typed name: spacing and title case with Mc, O' and hyphen rules.
Tidy a personal name: trim, collapse whitespace and title-case, with Mc, Mac, O', hyphen and van/de rules.
from fune.text.normalise_name import normalise_name  # text.normalise-name@^1

7. Bank details from validation-basics

Check payee and customer bank details before money moves.

validation.iban core
Checks an IBAN's mod-97 checksum and its country's length.
Check an IBAN against the ISO 13616 mod-97-10 checksum and the registered length for its country.
from fune.validation.iban import is_iban  # validation.iban@^1
validation.bic optional
Checks a SWIFT/BIC code's format and splits it.
Check the format of a SWIFT/BIC code (8 or 11 characters) and split it into its parts.
from fune.validation.bic import validate_bic  # validation.bic@^1
validation.uk-sort-code-account optional
Checks a UK sort code and account number by the Vocalink modulus rules, against a table you supply.
Check a UK sort code and account number with the Vocalink/Pay.UK modulus rules, against a table you supply.
from fune.validation.uk_sort_code_account import validate_uk_sort_code_account  # validation.uk-sort-code-account@^3
validation.uk-modulus-table optional
Parses Vocalink's VALACDOS and SCSUBTAB files into that table.
Parse Vocalink's VALACDOS.txt and SCSUBTAB.txt into the table UK sort code modulus checking needs.
from fune.validation.uk_modulus_table import parse_uk_modulus_table  # validation.uk-modulus-table@^1

8. Identifiers and check digits from validation-basics

Catch mistyped reference numbers by their check digits.

validation.luhn optional
Luhn mod-10 check, for card numbers and many reference numbers.
Check a digit string against the Luhn mod-10 checksum used by payment cards and many identifiers.
from fune.validation.luhn import is_luhn  # validation.luhn@^1
validation.uk-company-number optional
Checks a Companies House number's format and prefix.
Check a Companies House company number's format and prefix (SC, NI, OC, LP...) and zero-pad it to eight characters.
from fune.validation.uk_company_number import validate_uk_company_number  # validation.uk-company-number@^2
validation.lei optional
Checks a Legal Entity Identifier's check digits.
Check a 20-character Legal Entity Identifier against its ISO 17442 / ISO 7064 MOD 97-10 check digits.
from fune.validation.lei import validate_lei  # validation.lei@^1
validation.gtin optional
Checks an EAN/UPC/GTIN barcode number.
Check an EAN-8, UPC-A, EAN-13 or GTIN-14 barcode number against the GS1 mod-10 check digit.
from fune.validation.gtin import validate_gtin  # validation.gtin@^1

9. Displaying sensitive values from validation-basics

Show stored identifiers without exposing them.

text.mask optional
Masks all but the last digits of a card, account or phone number.
Mask all but the last n characters of a card, account or phone number, optionally keeping separators.
from fune.text.mask import mask  # text.mask@^1

Gaps

What this kind of app usually needs that Functional Weave does not have yet: write these yourself, or use a service.

  • email-verification Sending verification and password-reset emails, and storing their one-time tokens with expiry.
  • oauth-social-login OAuth 2.0 / OpenID Connect sign-in with Google, Microsoft, GitHub and the like.
  • mfa Second factors: TOTP authenticator codes, SMS codes, passkeys/WebAuthn and recovery codes.
  • roles-permissions Roles, permissions and organisation membership: who may do what is yours to model.
  • user-storage The users table, unique email index, migrations and account deletion.
  • address-lookup Turning a postcode into a list of addresses (Royal Mail PAF or a lookup API) is network work and not in caps.
  • email-deliverability Whether a mailbox exists (MX lookups, a confirmation email): validation.email checks the syntax only.
  • vocalink-data The Vocalink modulus tables themselves: download them from Pay.UK under its terms; caps parses them but does not ship them.