Functional Weave
Code in TypeScript

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.
import { validateRegistration } from "#fune/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.
import { validateLogin } from "#fune/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.
import { normaliseEmail } from "#fune/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.
import { validatePasswordChange } from "#fune/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.
import { passwordPolicy, checkPassword } from "#fune/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.
import { hashPassword, verifyPassword, passwordNeedsRehash } from "#fune/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.
import { pbkdf2Sha256 } from "#fune/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.
import { constantTimeEqual } from "#fune/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.
import { loginThrottle } from "#fune/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.
import { retryAfterSeconds } from "#fune/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.
import { issueAccessToken, readAccessToken, confirmAccessToken } from "#fune/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.
import { parseBearerToken } from "#fune/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.
import { signJwt, verifyJwt, decodeJwt } from "#fune/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.
import { hmacSha256 } from "#fune/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.
import { sha256 } from "#fune/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.
import { base64Encode, base64Decode, base64UrlEncode, base64UrlDecode } from "#fune/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.
import { countdown } from "#fune/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).
import { passwordChecklist, PasswordField } from "#fune/react.form.password-field@^1";
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).
import { TextField } from "#fune/react.form.text-field@^1";
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).
import { errorSummaryItems, ErrorSummary } from "#fune/react.form.error-summary@^1";
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.
import { initialFormState, formReducer, visibleErrors } from "#fune/form.state@^1";

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.
import { isEmail } from "#fune/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.
import { validatePhoneE164 } from "#fune/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.
import { validateUkPostcode } from "#fune/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.
import { normaliseName } from "#fune/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.
import { isIban } from "#fune/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.
import { validateBic } from "#fune/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.
import { validateUkSortCodeAccount } from "#fune/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.
import { parseUkModulusTable } from "#fune/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.
import { isLuhn } from "#fune/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.
import { validateUkCompanyNumber } from "#fune/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.
import { validateLei } from "#fune/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.
import { validateGtin } from "#fune/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.
import { mask } from "#fune/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.