User onboarding
Take a signup form submission and run the full onboarding workup deterministically: validate the email, score the chosen password, mint a stable internal ID, derive a URL-safe handle from the display name, generate a recovery / API secret, hash the password for storage, and verify the 2FA setup code. One pass, every step a pure-CPU call.
When to use this pack
An onboarding/account-provisioning agent receives a signup payload ({email, password, displayName, totpCode, totpSecret}) and needs to validate every field, mint every supporting identifier, and produce a storable record — without leaking the plaintext password into intermediate logs. The pack chains the steps in dependency order: validate before mint, mint stable IDs before deriving display fields, hash before storing. Output is a clean 'persistable record' shape plus a 'reject this signup because…' explanation when any step fails.
Tools in this pack
Workflow
- Validate the email with email-validate. Returns a structured verdict beyond a regex — surfaces whether the local-part / domain are well-formed, whether the domain looks like a typo of a major provider (gmial.com), and whether the address has obvious red flags. Reject here on hard failures (malformed) and surface soft warnings (likely typo) for the agent to confirm with the user. Every step after assumes a valid email so any downstream 'send confirmation email' step doesn't fail silently.
- Score the password with password-strength. Returns a score (0-4 or similar zxcvbn-style band) plus the actual weakness reason ('common password', 'contains username', 'too short'). Reject below a threshold and surface the *specific* weakness — 'your password is too weak' is unhelpful, 'your password contains your username' tells the user exactly what to change. Doing this here, before any hashing or mint step, means a rejected signup leaves no trace of the bad password anywhere.
- Mint the stable internal ID with uuid. UUID v4 is the right default — globally unique, no information leakage, decoupled from any user-controlled field. This is the dedupe / primary-key for the rest of the workup; every downstream record (user record, audit row, related entity) references this UUID rather than email or handle, both of which can change. Generate ONCE per signup and never regenerate.
- Derive the URL-safe handle with slugify on displayName. 'Ada Lovelace' → 'ada-lovelace'; 'Søren Kierkegaard' → 'soren-kierkegaard' (diacritics folded). Two failure modes to handle: collision with an existing handle (append the first 6 chars of the UUID), and reserved / forbidden slugs (admin, api, login — match against a denylist). The UUID from step 3 is the collision-resolution suffix because it's the only stable thing you have.
- Generate auxiliary secrets with password. Use this for: a one-time recovery code (16-20 alphanumeric, presented to the user and never stored plaintext server-side), an API key for the user's first programmatic access (32-48 chars), or a temporary password for an admin-created account that the user must change at first login. Output is uniformly random and meets common entropy requirements out of the box — no need to argue policy with the user.
- Hash with hash. NOTE: this is for FINGERPRINTING and dedupe lookups — real password storage MUST use bcrypt / argon2 / scrypt with per-user salt + work factor, not a bare SHA-256. Legitimate use cases for hash here: pwned-password fingerprint (SHA-1 of the password, sent as a 5-char prefix to a k-anonymity API), recovery-code fingerprint for fast lookup (the recovery code itself is shown to the user once and stored only as a hash), and integrity fingerprints for audit logging. Surface the algorithm choice (sha256 by default) and never use hash output as the primary password store.
- Verify 2FA setup with totp. Takes the base32 secret you generated server-side during enrollment, computes the current code, compares to what the user typed in. Three outcomes: (a) match → 2FA is wired up correctly, persist the secret encrypted-at-rest; (b) off by one window (the user took 30+ seconds to type) → accept and warn; (c) mismatch → either the user scanned the QR but typed wrong, or the QR encoded a different secret — re-issue the secret rather than letting them retry indefinitely. Doing this BEFORE finalizing the account means a broken 2FA enrollment fails the entire signup, not the next login attempt.
Run it in Claude
claude mcp add agent402 -s user -- npx -y agent402-mcp@latest
Then paste this prompt into Claude:
Onboard this signup using Agent402: email=ada@example.com, password=S0meStrongPassw0rd!, displayName=Ada Lovelace, totpSecret=JBSWY3DPEHPK3PXP, totpCode=492039. (1) email-validate the email. If invalid, return {accepted: false, reason: 'invalid-email', detail}. If 'likely-typo', surface the suggested correction and ask the user to confirm rather than rejecting outright. (2) password-strength on the password. If score < 3, return {accepted: false, reason: 'weak-password', specifically: <why>, suggestion: 'add length / drop common-pattern / vary character classes'}. Hard-reject signups that contain the email local-part or the displayName. (3) uuid (v4). Save as userId — this is the dedupe key for the rest of the steps. (4) slugify the displayName. If the slug collides with a reserved word (admin/api/login/root/help/about) or an existing handle, append the first 6 chars of userId hex. (5) password — generate one recovery code (length=20, alphanumeric) and one API key (length=48, alphanumeric+symbol). Return both to the user ONCE; persist only the hash. (6) hash the recovery code with alg=sha256 — this is what you'll store. Separately, hash the user-chosen password ONLY for the pwned-password k-anonymity probe (first 5 chars of SHA-1) — DO NOT use the SHA-256 output as the password store; the prompt MUST recommend bcrypt/argon2 for real persistence. (7) totp with secret=totpSecret. Compare computed code to totpCode. If match, set enrollment=confirmed. If off-by-one window, accept-with-warning. If mismatch, reject 2FA and instruct re-enroll (don't reject the whole signup; let the user retry the QR). Final return: {accepted: true|false, reason?, userId, handle, email, recoveryCodeHash, apiKey, twofaConfirmed, persistableRecord: {userId, email, handle, passwordStorage: 'TODO: replace SHA-256 with bcrypt/argon2', recoveryCodeHash, totpSecretEncrypted}}. All seven tools are pure-CPU (PoW-eligible / free tier). Budget ≤ $0.01 even paid. Never log plaintext password / recovery code / API key — these appear in the return value only.
← All skill packs