{
  "id": "auth",
  "title": "User accounts and authentication",
  "summary": "Sign-up and login forms, password policy and hashing, login lockout, and signed access tokens (JWT) read from the Authorization header.",
  "synonyms": [
    "authentication",
    "auth",
    "login",
    "log in",
    "sign in",
    "signup",
    "sign up",
    "registration",
    "user accounts",
    "passwords",
    "password reset",
    "jwt",
    "access tokens",
    "bearer tokens",
    "api keys",
    "brute force protection"
  ],
  "apps": [
    "login system",
    "user account system",
    "sign-up and sign-in pages",
    "authentication service",
    "API with token auth",
    "membership site"
  ],
  "language": "rust",
  "includes": [
    "validation-basics"
  ],
  "includedBy": [],
  "caveats": [],
  "sections": [
    {
      "key": "forms",
      "title": "Sign-up and login forms",
      "purpose": "Check what people type into the account forms, the same way in the browser and the API.",
      "from": "auth",
      "members": [
        {
          "id": "auth.validate-registration",
          "tier": "core",
          "why": "Validates a sign-up form (email, password, name) in one call, with a message per field.",
          "section": "forms",
          "from": "auth",
          "version": "1.0.0",
          "summary": "Validate a sign-up form (email, password, name) in one call, with a message per field, in the browser and the API alike.",
          "status": "stable",
          "license": "NOASSERTION",
          "languages": [
            "typescript",
            "python",
            "rust"
          ],
          "available": true,
          "group": false,
          "functions": [
            "validateRegistration"
          ],
          "signatures": [
            "pub fn validate_registration(email: &str, password: &str, name: &str, policy: &PasswordPolicy) -> RegistrationCheck"
          ],
          "reference": "fune!(auth.validate-registration@^1);  // then call validate_registration(…)",
          "url": "http://www.functionalweave.com/functions/auth.validate-registration"
        },
        {
          "id": "auth.validate-login",
          "tier": "core",
          "why": "Checks a login form and gives the normalised email to look the account up by.",
          "section": "forms",
          "from": "auth",
          "version": "1.0.0",
          "summary": "Check a login form (email, password): the normalised email to look up, or a message for each field to fix.",
          "status": "stable",
          "license": "NOASSERTION",
          "languages": [
            "typescript",
            "python",
            "rust"
          ],
          "available": true,
          "group": false,
          "functions": [
            "validateLogin"
          ],
          "signatures": [
            "pub fn validate_login(email: &str, password: &str) -> LoginCheck"
          ],
          "reference": "fune!(auth.validate-login@^1);  // then call validate_login(…)",
          "url": "http://www.functionalweave.com/functions/auth.validate-login"
        },
        {
          "id": "auth.normalise-email",
          "tier": "core",
          "why": "Trims an email and lower-cases its domain, so one address is one account.",
          "section": "forms",
          "from": "auth",
          "version": "1.0.0",
          "summary": "Trim an email address and lower-case its domain for storage and lookup, or null if it is not a plausible address.",
          "status": "stable",
          "license": "NOASSERTION",
          "languages": [
            "typescript",
            "python",
            "rust"
          ],
          "available": true,
          "group": false,
          "functions": [
            "normaliseEmail"
          ],
          "signatures": [
            "pub fn normalise_email(value: &str) -> Option<String>"
          ],
          "reference": "fune!(auth.normalise-email@^1);  // then call normalise_email(…)",
          "url": "http://www.functionalweave.com/functions/auth.normalise-email"
        },
        {
          "id": "auth.validate-password-change",
          "tier": "optional",
          "why": "Checks a change-password form: current password given and correct, new one meeting the policy and different.",
          "section": "forms",
          "from": "auth",
          "version": "1.0.0",
          "summary": "Check a change-password form: current password given and correct, new one meeting the policy and different.",
          "status": "stable",
          "license": "NOASSERTION",
          "languages": [
            "typescript",
            "python",
            "rust"
          ],
          "available": true,
          "group": false,
          "functions": [
            "validatePasswordChange"
          ],
          "signatures": [
            "pub fn validate_password_change(current_password: &str, new_password: &str, current_password_matches: bool, email: &str, name: &str, policy: &PasswordPolicy) -> PasswordChangeCheck"
          ],
          "reference": "fune!(auth.validate-password-change@^1);  // then call validate_password_change(…)",
          "url": "http://www.functionalweave.com/functions/auth.validate-password-change"
        }
      ],
      "gaps": []
    },
    {
      "key": "passwords",
      "title": "Passwords",
      "purpose": "Decide what passwords are acceptable and store them safely.",
      "from": "auth",
      "members": [
        {
          "id": "auth.password-policy",
          "tier": "core",
          "why": "Checks a new password against NIST SP 800-63B-4 by default: length, common passwords, name and email.",
          "section": "passwords",
          "from": "auth",
          "version": "1.0.1",
          "summary": "Check a new password against a named policy (NIST SP 800-63B-4 by default): length, common passwords, name and email.",
          "status": "stable",
          "license": "NOASSERTION",
          "languages": [
            "typescript",
            "python",
            "rust"
          ],
          "available": true,
          "group": true,
          "functions": [
            "passwordPolicy",
            "checkPassword"
          ],
          "signatures": [
            "pub fn password_policy(name: &str) -> PasswordPolicy",
            "pub fn check_password(password: &str, email: Option<&str>, name: Option<&str>, policy: &PasswordPolicy) -> PasswordCheck"
          ],
          "reference": "fune!(auth.password-policy@^1);  // then call password_policy, check_password",
          "url": "http://www.functionalweave.com/functions/auth.password-policy"
        },
        {
          "id": "auth.password-hash",
          "tier": "core",
          "why": "Hashes passwords as PBKDF2-SHA256 for storage, verifies in constant time, and flags hashes to upgrade.",
          "section": "passwords",
          "from": "auth",
          "version": "1.0.0",
          "summary": "Hash a password for storage as pbkdf2_sha256$iterations$salt$hash, verify one in constant time, and spot weak hashes.",
          "status": "stable",
          "license": "NOASSERTION",
          "languages": [
            "typescript",
            "python",
            "rust"
          ],
          "available": true,
          "group": true,
          "functions": [
            "hashPassword",
            "verifyPassword",
            "passwordNeedsRehash"
          ],
          "signatures": [
            "pub fn hash_password(password: &str, salt: &[i64], iterations: i64) -> String",
            "pub fn verify_password(password: &str, stored: &str) -> bool",
            "pub fn password_needs_rehash(stored: &str, iterations: i64) -> bool"
          ],
          "reference": "fune!(auth.password-hash@^1);  // then call hash_password, verify_password, password_needs_rehash",
          "url": "http://www.functionalweave.com/functions/auth.password-hash"
        },
        {
          "id": "crypto.pbkdf2-sha256",
          "tier": "optional",
          "why": "The PBKDF2 key derivation underneath, if you need it directly.",
          "section": "passwords",
          "from": "auth",
          "version": "1.0.0",
          "summary": "PBKDF2 with HMAC-SHA256 (RFC 8018): derive a key from a password and salt, slowly, fast enough for a login request.",
          "status": "stable",
          "license": "NOASSERTION",
          "languages": [
            "typescript",
            "python",
            "rust"
          ],
          "available": true,
          "group": false,
          "functions": [
            "pbkdf2Sha256"
          ],
          "signatures": [
            "pub fn pbkdf2_sha256(password: &[i64], salt: &[i64], iterations: i64, key_length: i64) -> Vec<i64>"
          ],
          "reference": "fune!(crypto.pbkdf2-sha256@^1);  // then call pbkdf2_sha256(…)",
          "url": "http://www.functionalweave.com/functions/crypto.pbkdf2-sha256"
        },
        {
          "id": "crypto.constant-time-equal",
          "tier": "optional",
          "why": "Compares MACs and hashes in constant time, for your own token or signature checks.",
          "section": "passwords",
          "from": "auth",
          "version": "1.0.0",
          "summary": "Compare two byte strings in time that does not depend on where they differ, for checking MACs and hashes.",
          "status": "stable",
          "license": "NOASSERTION",
          "languages": [
            "typescript",
            "python",
            "rust"
          ],
          "available": true,
          "group": false,
          "functions": [
            "constantTimeEqual"
          ],
          "signatures": [
            "pub fn constant_time_equal(a: &[i64], b: &[i64]) -> bool"
          ],
          "reference": "fune!(crypto.constant-time-equal@^1);  // then call constant_time_equal(…)",
          "url": "http://www.functionalweave.com/functions/crypto.constant-time-equal"
        }
      ],
      "gaps": [
        {
          "key": "breached-passwords",
          "text": "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.",
          "section": "passwords",
          "from": "auth"
        }
      ]
    },
    {
      "key": "login",
      "title": "Login protection",
      "purpose": "Slow down password guessing.",
      "from": "auth",
      "members": [
        {
          "id": "auth.login-throttle",
          "tier": "core",
          "why": "Allows a login attempt or locks the account out from its recent failures, with the seconds until retry.",
          "section": "login",
          "from": "auth",
          "version": "1.0.0",
          "summary": "Allow a login attempt or lock the account out, from its recent failed attempts, with the seconds until it may retry.",
          "status": "stable",
          "license": "NOASSERTION",
          "languages": [
            "typescript",
            "python",
            "rust"
          ],
          "available": true,
          "group": false,
          "functions": [
            "loginThrottle"
          ],
          "signatures": [
            "pub fn login_throttle(failures: &[i64], now: i64, policy: &ThrottlePolicy) -> ThrottleDecision"
          ],
          "reference": "fune!(auth.login-throttle@^1);  // then call login_throttle(…)",
          "url": "http://www.functionalweave.com/functions/auth.login-throttle"
        },
        {
          "id": "http.retry-after",
          "tier": "optional",
          "why": "Reads a Retry-After header, for a client that backs off when locked out or rate limited.",
          "section": "login",
          "from": "auth",
          "version": "1.0.0",
          "summary": "Seconds to wait from an HTTP Retry-After header (delay-seconds or an HTTP date), or null if missing or malformed.",
          "status": "stable",
          "license": "NOASSERTION",
          "languages": [
            "typescript",
            "python",
            "rust"
          ],
          "available": true,
          "group": false,
          "functions": [
            "retryAfterSeconds"
          ],
          "signatures": [
            "pub fn retry_after_seconds(header: Option<&str>, now: i64) -> Option<i64>"
          ],
          "reference": "fune!(http.retry-after@^1);  // then call retry_after_seconds(…)",
          "url": "http://www.functionalweave.com/functions/http.retry-after"
        }
      ],
      "gaps": [
        {
          "key": "ip-rate-limiting",
          "text": "Rate limiting by IP address across accounts, with its shared counter store (Redis, a database).",
          "section": "login",
          "from": "auth"
        }
      ]
    },
    {
      "key": "tokens",
      "title": "Tokens",
      "purpose": "Issue and check signed tokens for an API or a single-page app.",
      "from": "auth",
      "members": [
        {
          "id": "auth.access-token",
          "tier": "core",
          "why": "Issues HS256 access tokens, reads them from the Authorization header, and refuses revoked or superseded ones.",
          "section": "tokens",
          "from": "auth",
          "version": "1.0.0",
          "summary": "Issue an API's HS256 access token, read one from an Authorization header, and refuse revoked or superseded ones.",
          "status": "stable",
          "license": "NOASSERTION",
          "languages": [
            "typescript",
            "python",
            "rust"
          ],
          "available": true,
          "group": true,
          "functions": [
            "issueAccessToken",
            "readAccessToken",
            "confirmAccessToken"
          ],
          "signatures": [
            "pub fn issue_access_token(subject: &str, name: &str, email: &str, token_version: i64, jti: &str, now: i64, ttl_seconds: i64, secret: &[i64]) -> AccessToken",
            "pub fn read_access_token(authorization: Option<&str>, secret: &[i64], now: i64, leeway_seconds: i64) -> AccessCheck",
            "pub fn confirm_access_token(check: &AccessCheck, current_token_version: Option<i64>, revoked: bool) -> AccessCheck"
          ],
          "reference": "fune!(auth.access-token@^1);  // then call issue_access_token, read_access_token, confirm_access_token",
          "url": "http://www.functionalweave.com/functions/auth.access-token"
        },
        {
          "id": "auth.bearer-token",
          "tier": "core",
          "why": "Takes the token out of an \"Authorization: Bearer ...\" header, or null.",
          "section": "tokens",
          "from": "auth",
          "version": "1.0.0",
          "summary": "The token from an HTTP Authorization header of the form \"Bearer <token>\" (RFC 6750), or null if it has none.",
          "status": "stable",
          "license": "NOASSERTION",
          "languages": [
            "typescript",
            "python",
            "rust"
          ],
          "available": true,
          "group": false,
          "functions": [
            "parseBearerToken"
          ],
          "signatures": [
            "pub fn parse_bearer_token(authorization: Option<&str>) -> Option<String>"
          ],
          "reference": "fune!(auth.bearer-token@^1);  // then call parse_bearer_token(…)",
          "url": "http://www.functionalweave.com/functions/auth.bearer-token"
        },
        {
          "id": "auth.jwt",
          "tier": "optional",
          "why": "Signs, verifies and decodes HS256 JWTs with exp/nbf/iat checks, for tokens of your own shape.",
          "section": "tokens",
          "from": "auth",
          "version": "1.0.0",
          "summary": "Sign, verify and decode HS256 JSON Web Tokens (RFC 7519): signature, algorithm and exp/nbf/iat checked with leeway.",
          "status": "stable",
          "license": "NOASSERTION",
          "languages": [
            "typescript",
            "python",
            "rust"
          ],
          "available": true,
          "group": true,
          "functions": [
            "signJwt",
            "verifyJwt",
            "decodeJwt"
          ],
          "signatures": [
            "pub fn sign_jwt(claims: &Value, secret: &[i64]) -> String",
            "pub fn verify_jwt(token: &str, secret: &[i64], now: i64, leeway_seconds: i64) -> JwtVerification",
            "pub fn decode_jwt(token: &str) -> Option<Value>"
          ],
          "reference": "fune!(auth.jwt@^1);  // then call sign_jwt, verify_jwt, decode_jwt",
          "url": "http://www.functionalweave.com/functions/auth.jwt"
        },
        {
          "id": "crypto.hmac-sha256",
          "tier": "optional",
          "why": "HMAC-SHA256, for signing webhooks, links or cookies.",
          "section": "tokens",
          "from": "auth",
          "version": "1.0.0",
          "summary": "HMAC-SHA256 of a message under a secret key (RFC 2104, RFC 4231), in pure code that also runs in the browser.",
          "status": "stable",
          "license": "NOASSERTION",
          "languages": [
            "typescript",
            "python",
            "rust"
          ],
          "available": true,
          "group": false,
          "functions": [
            "hmacSha256"
          ],
          "signatures": [
            "pub fn hmac_sha256(key: &[i64], message: &[i64]) -> Vec<i64>"
          ],
          "reference": "fune!(crypto.hmac-sha256@^1);  // then call hmac_sha256(…)",
          "url": "http://www.functionalweave.com/functions/crypto.hmac-sha256"
        },
        {
          "id": "crypto.sha256",
          "tier": "optional",
          "why": "SHA-256 digests, for storing API keys or reset tokens hashed.",
          "section": "tokens",
          "from": "auth",
          "version": "1.0.0",
          "summary": "The SHA-256 digest of a byte string (FIPS 180-4), in pure code that also runs in the browser.",
          "status": "stable",
          "license": "NOASSERTION",
          "languages": [
            "typescript",
            "python",
            "rust"
          ],
          "available": true,
          "group": false,
          "functions": [
            "sha256"
          ],
          "signatures": [
            "pub fn sha256(bytes: &[i64]) -> Vec<i64>"
          ],
          "reference": "fune!(crypto.sha256@^1);  // then call sha256(…)",
          "url": "http://www.functionalweave.com/functions/crypto.sha256"
        },
        {
          "id": "encoding.base64",
          "tier": "optional",
          "why": "Base64 and base64url, for encoding tokens and keys.",
          "section": "tokens",
          "from": "auth",
          "version": "1.0.0",
          "summary": "Bytes to base64 and base64url text and back (RFC 4648), with strict decoding, identically in every language.",
          "status": "stable",
          "license": "NOASSERTION",
          "languages": [
            "typescript",
            "python",
            "rust"
          ],
          "available": true,
          "group": true,
          "functions": [
            "base64Encode",
            "base64Decode",
            "base64UrlEncode",
            "base64UrlDecode"
          ],
          "signatures": [
            "pub fn base64_encode(bytes: &[i64]) -> String",
            "pub fn base64_decode(text: &str) -> Vec<i64>",
            "pub fn base64_url_encode(bytes: &[i64]) -> String",
            "pub fn base64_url_decode(text: &str) -> Vec<i64>"
          ],
          "reference": "fune!(encoding.base64@^1);  // then call base64_encode, base64_decode, base64_url_encode, base64_url_decode",
          "url": "http://www.functionalweave.com/functions/encoding.base64"
        },
        {
          "id": "time.countdown",
          "tier": "optional",
          "why": "Seconds left until a token expires and a timer text, for \"session expires in 4:05\".",
          "section": "tokens",
          "from": "auth",
          "version": "1.0.0",
          "summary": "Seconds left until a Unix time, whether it has passed, and a timer text like 4:05, for tokens or retry waits.",
          "status": "stable",
          "license": "NOASSERTION",
          "languages": [
            "typescript",
            "python",
            "rust"
          ],
          "available": true,
          "group": false,
          "functions": [
            "countdown"
          ],
          "signatures": [
            "pub fn countdown(until: i64, now: i64) -> Countdown"
          ],
          "reference": "fune!(time.countdown@^1);  // then call countdown(…)",
          "url": "http://www.functionalweave.com/functions/time.countdown"
        }
      ],
      "gaps": [
        {
          "key": "sessions",
          "text": "Server-side sessions and cookies: storing them, Secure/HttpOnly/SameSite flags, CSRF tokens and expiry are yours.",
          "section": "tokens",
          "from": "auth"
        },
        {
          "key": "asymmetric-jwt",
          "text": "RS256/ES256 tokens and JWKS key sets (for third-party identity providers): auth.jwt is HS256 only.",
          "section": "tokens",
          "from": "auth"
        }
      ]
    },
    {
      "key": "forms-ui",
      "title": "Forms in React",
      "purpose": "Accessible account pages in a React front end (TypeScript only).",
      "from": "auth",
      "members": [
        {
          "id": "react.form.password-field",
          "tier": "optional",
          "why": "A password input with Show/Hide and a live checklist of the password policy.",
          "section": "forms-ui",
          "from": "auth",
          "version": "1.0.1",
          "summary": "A password input with a Show/Hide button and an optional live checklist of a password policy (GOV.UK password input).",
          "status": "stable",
          "license": "NOASSERTION",
          "languages": [
            "typescript"
          ],
          "available": false,
          "framework": {
            "name": "react",
            "range": "^19"
          },
          "group": true,
          "functions": [
            "passwordChecklist",
            "PasswordField"
          ],
          "signatures": [],
          "reference": null,
          "url": "http://www.functionalweave.com/functions/react.form.password-field"
        },
        {
          "id": "react.form.text-field",
          "tier": "optional",
          "why": "Labelled email and name inputs with hints and errors.",
          "section": "forms-ui",
          "from": "auth",
          "version": "1.0.1",
          "summary": "A labelled single-line text input with hint, error message, autocomplete, prefix and suffix (GOV.UK text input).",
          "status": "stable",
          "license": "NOASSERTION",
          "languages": [
            "typescript"
          ],
          "available": false,
          "framework": {
            "name": "react",
            "range": "^19"
          },
          "group": false,
          "functions": [
            "TextField"
          ],
          "signatures": [],
          "reference": null,
          "url": "http://www.functionalweave.com/functions/react.form.text-field"
        },
        {
          "id": "react.form.error-summary",
          "tier": "optional",
          "why": "The \"There is a problem\" box linking to each field in error.",
          "section": "forms-ui",
          "from": "auth",
          "version": "1.0.1",
          "summary": "A \"There is a problem\" box linking to each field in error, focused on arrival, built from a validator's fields (GOV.UK).",
          "status": "stable",
          "license": "NOASSERTION",
          "languages": [
            "typescript"
          ],
          "available": false,
          "framework": {
            "name": "react",
            "range": "^19"
          },
          "group": true,
          "functions": [
            "errorSummaryItems",
            "ErrorSummary"
          ],
          "signatures": [],
          "reference": null,
          "url": "http://www.functionalweave.com/functions/react.form.error-summary"
        },
        {
          "id": "form.state",
          "tier": "optional",
          "why": "Form values, touched fields and validator errors as a reducer for useReducer.",
          "section": "forms-ui",
          "from": "auth",
          "version": "1.0.1",
          "summary": "A form's state as a pure reducer: values, touched fields, validator errors and the submit, for useReducer.",
          "status": "stable",
          "license": "NOASSERTION",
          "languages": [
            "typescript"
          ],
          "available": false,
          "group": true,
          "functions": [
            "initialFormState",
            "formReducer",
            "visibleErrors"
          ],
          "signatures": [],
          "reference": null,
          "url": "http://www.functionalweave.com/functions/form.state"
        }
      ],
      "gaps": []
    },
    {
      "key": "contact",
      "title": "Contact details",
      "purpose": "Check and normalise a person's contact details before storing them.",
      "from": "validation-basics",
      "members": [
        {
          "id": "validation.email",
          "tier": "core",
          "why": "Is an email address plausible, by a documented subset of RFC 5322.",
          "section": "contact",
          "from": "validation-basics",
          "version": "1.0.0",
          "summary": "Is this a plausible email address? A documented, pragmatic subset of RFC 5322, not the full grammar.",
          "status": "stable",
          "license": "NOASSERTION",
          "languages": [
            "typescript",
            "python",
            "rust"
          ],
          "available": true,
          "group": false,
          "functions": [
            "isEmail"
          ],
          "signatures": [
            "pub fn is_email(value: &str) -> bool"
          ],
          "reference": "fune!(validation.email@^1);  // then call is_email(…)",
          "url": "http://www.functionalweave.com/functions/validation.email"
        },
        {
          "id": "validation.phone-e164",
          "tier": "core",
          "why": "Normalises a phone number to +44… E.164 for a default country.",
          "section": "contact",
          "from": "validation-basics",
          "version": "2.0.0",
          "summary": "Normalise a phone number to E.164 (+447700900123) using a default country's trunk and dialling prefixes.",
          "status": "stable",
          "license": "NOASSERTION",
          "languages": [
            "typescript",
            "python",
            "rust"
          ],
          "available": true,
          "group": false,
          "functions": [
            "validatePhoneE164"
          ],
          "signatures": [
            "pub fn validate_phone_e164(value: &str, default_country: &str) -> PhoneE164"
          ],
          "reference": "fune!(validation.phone-e164@^2);  // then call validate_phone_e164(…)",
          "url": "http://www.functionalweave.com/functions/validation.phone-e164"
        },
        {
          "id": "validation.uk-postcode",
          "tier": "core",
          "why": "Validates a UK postcode and puts it in canonical form.",
          "section": "contact",
          "from": "validation-basics",
          "version": "2.0.0",
          "summary": "Validate a UK postcode and normalise it to canonical upper case with one space before the inward code.",
          "status": "stable",
          "license": "NOASSERTION",
          "languages": [
            "typescript",
            "python",
            "rust"
          ],
          "available": true,
          "group": false,
          "functions": [
            "validateUkPostcode"
          ],
          "signatures": [
            "pub fn validate_uk_postcode(value: &str) -> UkPostcode"
          ],
          "reference": "fune!(validation.uk-postcode@^2);  // then call validate_uk_postcode(…)",
          "url": "http://www.functionalweave.com/functions/validation.uk-postcode"
        },
        {
          "id": "text.normalise-name",
          "tier": "optional",
          "why": "Tidies a typed name: spacing and title case with Mc, O' and hyphen rules.",
          "section": "contact",
          "from": "validation-basics",
          "version": "1.0.0",
          "summary": "Tidy a personal name: trim, collapse whitespace and title-case, with Mc, Mac, O', hyphen and van/de rules.",
          "status": "stable",
          "license": "NOASSERTION",
          "languages": [
            "typescript",
            "python",
            "rust"
          ],
          "available": true,
          "group": false,
          "functions": [
            "normaliseName"
          ],
          "signatures": [
            "pub fn normalise_name(value: &str) -> String"
          ],
          "reference": "fune!(text.normalise-name@^1);  // then call normalise_name(…)",
          "url": "http://www.functionalweave.com/functions/text.normalise-name"
        }
      ],
      "gaps": []
    },
    {
      "key": "bank",
      "title": "Bank details",
      "purpose": "Check payee and customer bank details before money moves.",
      "from": "validation-basics",
      "members": [
        {
          "id": "validation.iban",
          "tier": "core",
          "why": "Checks an IBAN's mod-97 checksum and its country's length.",
          "section": "bank",
          "from": "validation-basics",
          "version": "1.0.0",
          "summary": "Check an IBAN against the ISO 13616 mod-97-10 checksum and the registered length for its country.",
          "status": "stable",
          "license": "NOASSERTION",
          "languages": [
            "typescript",
            "python",
            "rust"
          ],
          "available": true,
          "group": false,
          "functions": [
            "isIban"
          ],
          "signatures": [
            "pub fn is_iban(value: &str) -> bool"
          ],
          "reference": "fune!(validation.iban@^1);  // then call is_iban(…)",
          "url": "http://www.functionalweave.com/functions/validation.iban"
        },
        {
          "id": "validation.bic",
          "tier": "optional",
          "why": "Checks a SWIFT/BIC code's format and splits it.",
          "section": "bank",
          "from": "validation-basics",
          "version": "1.0.0",
          "summary": "Check the format of a SWIFT/BIC code (8 or 11 characters) and split it into its parts.",
          "status": "stable",
          "license": "NOASSERTION",
          "languages": [
            "typescript",
            "python",
            "rust"
          ],
          "available": true,
          "group": false,
          "functions": [
            "validateBic"
          ],
          "signatures": [
            "pub fn validate_bic(value: &str) -> BicCheck"
          ],
          "reference": "fune!(validation.bic@^1);  // then call validate_bic(…)",
          "url": "http://www.functionalweave.com/functions/validation.bic"
        },
        {
          "id": "validation.uk-sort-code-account",
          "tier": "optional",
          "why": "Checks a UK sort code and account number by the Vocalink modulus rules, against a table you supply.",
          "section": "bank",
          "from": "validation-basics",
          "version": "3.0.0",
          "summary": "Check a UK sort code and account number with the Vocalink/Pay.UK modulus rules, against a table you supply.",
          "status": "stable",
          "license": "NOASSERTION",
          "languages": [
            "typescript",
            "python",
            "rust"
          ],
          "available": true,
          "group": false,
          "functions": [
            "validateUkSortCodeAccount"
          ],
          "signatures": [
            "pub fn validate_uk_sort_code_account(sort_code: &str, account_number: &str, table: &UkModulusTable) -> UkSortCodeAccount"
          ],
          "reference": "fune!(validation.uk-sort-code-account@^3);  // then call validate_uk_sort_code_account(…)",
          "url": "http://www.functionalweave.com/functions/validation.uk-sort-code-account"
        },
        {
          "id": "validation.uk-modulus-table",
          "tier": "optional",
          "why": "Parses Vocalink's VALACDOS and SCSUBTAB files into that table.",
          "section": "bank",
          "from": "validation-basics",
          "version": "1.0.0",
          "summary": "Parse Vocalink's VALACDOS.txt and SCSUBTAB.txt into the table UK sort code modulus checking needs.",
          "status": "stable",
          "license": "NOASSERTION",
          "languages": [
            "typescript",
            "python",
            "rust"
          ],
          "available": true,
          "group": false,
          "functions": [
            "parseUkModulusTable"
          ],
          "signatures": [
            "pub fn parse_uk_modulus_table(valacdos: &str, scsubtab: &str) -> UkModulusTable"
          ],
          "reference": "fune!(validation.uk-modulus-table@^1);  // then call parse_uk_modulus_table(…)",
          "url": "http://www.functionalweave.com/functions/validation.uk-modulus-table"
        }
      ],
      "gaps": []
    },
    {
      "key": "check-digits",
      "title": "Identifiers and check digits",
      "purpose": "Catch mistyped reference numbers by their check digits.",
      "from": "validation-basics",
      "members": [
        {
          "id": "validation.luhn",
          "tier": "optional",
          "why": "Luhn mod-10 check, for card numbers and many reference numbers.",
          "section": "check-digits",
          "from": "validation-basics",
          "version": "1.0.0",
          "summary": "Check a digit string against the Luhn mod-10 checksum used by payment cards and many identifiers.",
          "status": "stable",
          "license": "NOASSERTION",
          "languages": [
            "typescript",
            "python",
            "rust"
          ],
          "available": true,
          "group": false,
          "functions": [
            "isLuhn"
          ],
          "signatures": [
            "pub fn is_luhn(value: &str) -> bool"
          ],
          "reference": "fune!(validation.luhn@^1);  // then call is_luhn(…)",
          "url": "http://www.functionalweave.com/functions/validation.luhn"
        },
        {
          "id": "validation.uk-company-number",
          "tier": "optional",
          "why": "Checks a Companies House number's format and prefix.",
          "section": "check-digits",
          "from": "validation-basics",
          "version": "2.0.1",
          "summary": "Check a Companies House company number's format and prefix (SC, NI, OC, LP...) and zero-pad it to eight characters.",
          "status": "stable",
          "license": "NOASSERTION",
          "languages": [
            "typescript",
            "python",
            "rust"
          ],
          "available": true,
          "group": false,
          "functions": [
            "validateUkCompanyNumber"
          ],
          "signatures": [
            "pub fn validate_uk_company_number(value: &str) -> UkCompanyNumber"
          ],
          "reference": "fune!(validation.uk-company-number@^2);  // then call validate_uk_company_number(…)",
          "url": "http://www.functionalweave.com/functions/validation.uk-company-number"
        },
        {
          "id": "validation.lei",
          "tier": "optional",
          "why": "Checks a Legal Entity Identifier's check digits.",
          "section": "check-digits",
          "from": "validation-basics",
          "version": "1.0.0",
          "summary": "Check a 20-character Legal Entity Identifier against its ISO 17442 / ISO 7064 MOD 97-10 check digits.",
          "status": "stable",
          "license": "NOASSERTION",
          "languages": [
            "typescript",
            "python",
            "rust"
          ],
          "available": true,
          "group": false,
          "functions": [
            "validateLei"
          ],
          "signatures": [
            "pub fn validate_lei(value: &str) -> LeiCheck"
          ],
          "reference": "fune!(validation.lei@^1);  // then call validate_lei(…)",
          "url": "http://www.functionalweave.com/functions/validation.lei"
        },
        {
          "id": "validation.gtin",
          "tier": "optional",
          "why": "Checks an EAN/UPC/GTIN barcode number.",
          "section": "check-digits",
          "from": "validation-basics",
          "version": "1.1.0",
          "summary": "Check an EAN-8, UPC-A, EAN-13 or GTIN-14 barcode number against the GS1 mod-10 check digit.",
          "status": "stable",
          "license": "NOASSERTION",
          "languages": [
            "typescript",
            "python",
            "rust"
          ],
          "available": true,
          "group": false,
          "functions": [
            "validateGtin"
          ],
          "signatures": [
            "pub fn validate_gtin(value: &str) -> GtinCheck"
          ],
          "reference": "fune!(validation.gtin@^1);  // then call validate_gtin(…)",
          "url": "http://www.functionalweave.com/functions/validation.gtin"
        }
      ],
      "gaps": []
    },
    {
      "key": "masking",
      "title": "Displaying sensitive values",
      "purpose": "Show stored identifiers without exposing them.",
      "from": "validation-basics",
      "members": [
        {
          "id": "text.mask",
          "tier": "optional",
          "why": "Masks all but the last digits of a card, account or phone number.",
          "section": "masking",
          "from": "validation-basics",
          "version": "1.0.0",
          "summary": "Mask all but the last n characters of a card, account or phone number, optionally keeping separators.",
          "status": "stable",
          "license": "NOASSERTION",
          "languages": [
            "typescript",
            "python",
            "rust"
          ],
          "available": true,
          "group": false,
          "functions": [
            "mask"
          ],
          "signatures": [
            "pub fn mask(value: &str, visible: i64, mask_char: &str, keep_separators: bool) -> String"
          ],
          "reference": "fune!(text.mask@^1);  // then call mask(…)",
          "url": "http://www.functionalweave.com/functions/text.mask"
        }
      ],
      "gaps": []
    }
  ],
  "gaps": [
    {
      "key": "breached-passwords",
      "text": "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.",
      "section": "passwords",
      "from": "auth"
    },
    {
      "key": "ip-rate-limiting",
      "text": "Rate limiting by IP address across accounts, with its shared counter store (Redis, a database).",
      "section": "login",
      "from": "auth"
    },
    {
      "key": "sessions",
      "text": "Server-side sessions and cookies: storing them, Secure/HttpOnly/SameSite flags, CSRF tokens and expiry are yours.",
      "section": "tokens",
      "from": "auth"
    },
    {
      "key": "asymmetric-jwt",
      "text": "RS256/ES256 tokens and JWKS key sets (for third-party identity providers): auth.jwt is HS256 only.",
      "section": "tokens",
      "from": "auth"
    },
    {
      "key": "email-verification",
      "text": "Sending verification and password-reset emails, and storing their one-time tokens with expiry.",
      "section": null,
      "from": "auth"
    },
    {
      "key": "oauth-social-login",
      "text": "OAuth 2.0 / OpenID Connect sign-in with Google, Microsoft, GitHub and the like.",
      "section": null,
      "from": "auth"
    },
    {
      "key": "mfa",
      "text": "Second factors: TOTP authenticator codes, SMS codes, passkeys/WebAuthn and recovery codes.",
      "section": null,
      "from": "auth"
    },
    {
      "key": "roles-permissions",
      "text": "Roles, permissions and organisation membership: who may do what is yours to model.",
      "section": null,
      "from": "auth"
    },
    {
      "key": "user-storage",
      "text": "The users table, unique email index, migrations and account deletion.",
      "section": null,
      "from": "auth"
    },
    {
      "key": "address-lookup",
      "text": "Turning a postcode into a list of addresses (Royal Mail PAF or a lookup API) is network work and not in caps.",
      "section": null,
      "from": "validation-basics"
    },
    {
      "key": "email-deliverability",
      "text": "Whether a mailbox exists (MX lookups, a confirmation email): validation.email checks the syntax only.",
      "section": null,
      "from": "validation-basics"
    },
    {
      "key": "vocalink-data",
      "text": "The Vocalink modulus tables themselves: download them from Pay.UK under its terms; caps parses them but does not ship them.",
      "section": null,
      "from": "validation-basics"
    }
  ],
  "counts": {
    "members": 34,
    "core": 12,
    "optional": 22,
    "unreviewed": 0,
    "deprecated": [],
    "unavailable": [
      "react.form.password-field",
      "react.form.text-field",
      "react.form.error-summary",
      "form.state"
    ],
    "missing": [],
    "gaps": 12
  },
  "warnings": [
    "not written in rust: react.form.password-field, react.form.text-field, react.form.error-summary, form.state"
  ],
  "url": "http://www.functionalweave.com/suites/auth",
  "owner": null
}
