Skip to content

fix(web): explain password requirements on signup instead of generic error - #1248

Merged
riderx merged 1 commit into
mainfrom
fix/register-password-errors
Oct 21, 2026
Merged

riderx merged 1 commit into
mainfrom
fix/register-password-errors

Conversation

@riderx

@riderx riderx commented Oct 21, 2026 •

Copy link
Copy Markdown
Member

Problem

A user (tech@hellobello.at) couldn't sign up on capgo.app/register using their browser's suggested strong password and only got "Invalid registration request".

The /auth/register API requires an uppercase letter, a lowercase letter, a number and a special character. Chrome's generated passwords often have no symbol (e.g. xY7kq2mPzR9wB4n), so the API rejects them. It does return the reason in an errors array, but the landing page ignored it and showed only the generic message. The form also had no hint and no client-side check.

Fix

  • Requirements hint under the password field, linked with aria-describedby.
  • passwordrules attribute on the input so Safari/Chrome password managers generate passwords that meet the policy.
  • Client-side check that matches the API policy (getPasswordRequirementErrors) and lists exactly what's missing before submitting. It also sets aria-invalid and moves focus to the field.
  • API error details: if the API returns errors, those are shown instead of "Invalid registration request".
  • New copy key password_requirements with translator context.

Screenshot

Submitting a Chrome-style generated password with no symbol:

register weak password

Tests

  • bun test apps/web/src/services/registration.test.ts: 5 pass. New tests cover API errors being shown and the client-side policy.
  • oxlint is clean on the changed files.

🤖 Generated with Claude Code


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need.

View guided diff

Summary by CodeRabbit

  • New Features

    • Added clear password requirements to the registration form: 6–72 characters, including uppercase, lowercase, a number, and a special character.
    • Added localized guidance explaining password requirements.
  • Bug Fixes

    • Registration now validates passwords before submission and highlights invalid entries with focused feedback.
    • Password-related registration errors now display clearer messages from the service.

…error

Browser-generated passwords (e.g. Chrome) often lack a symbol, which the
/auth/register API rejects with only 'Invalid registration request'.

- show the password requirements under the field and set passwordrules
  so password managers generate compliant passwords
- validate the password client-side and list what is missing
- surface the API 'errors' details instead of the generic message

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@riderx
riderx merged commit 0c182a0 into main Oct 21, 2026
6 of 7 checks passed
@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review 🔄 Running since 2026-10-21T15:37:39.205687Z 05dd953 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@coderabbitai

coderabbitai Bot commented Oct 21, 2026

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Note

Currently processing new changes in this PR. This may take a few minutes, please wait...

⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: ASSERTIVE
  • Plan: Advanced
  • Run ID: cf67ba6e-2496-44cf-9199-4a3398cdad5c
📥 Commits

Reviewing files that changed from the base of the PR and between 8c7e398 and 05dd953.

📒 Files selected for processing: 7
  • apps/shared/copy/messageContexts.ts
  • apps/shared/copy/messages.ts
  • apps/shared/copy/translationContextByText.ts
  • apps/web/src/pages/register.astro
  • apps/web/src/scripts/register.ts
  • apps/web/src/services/registration.test.ts
  • apps/web/src/services/registration.ts
✨ Finishing Touches
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@sonarqubecloud

Copy link
Copy Markdown

riderx added a commit that referenced this pull request Oct 21, 2026
main (#1248) added password requirement checks to register.ts; the VM test
context did not provide the imported helpers or password element methods.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
riderx added a commit that referenced this pull request Oct 21, 2026
main (#1248) added passwordrules to the register password field, which
Astro's attribute types do not include, so astro check failed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
riderx added a commit that referenced this pull request Oct 22, 2026
…no trial) for paid-ads visitors (#1234)

* feat(ads): direct offer (no free trial) for paid-ads visitors

Visitors landing with ?offer=direct (added to every ad URL) are persisted
via first-touch attribution (cookie + localStorage) and flagged before first
paint with data-offer="direct" on <html>. Trial copy (data-trial-copy) is
swapped for 14-day money-back guarantee copy (data-direct-copy) with pure
CSS, so there is no flash and normal visitors see no change.

- attribution: capture offer=direct only, persist it, send metadata.offer
  on signup and signup_offer to PostHog, forward it on register/console links
- OfferText component + global CSS toggles
- header, SignupCta, live-update, native-build, builder/webapp/no-mac
  solutions pages and /register/ render both variants
- new direct-offer message keys, contexts regenerated
- attribution tests extended

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(ads): money-back guarantee copy on /pricing/ for direct offer

Swap trial wording on the pricing page for offer=direct visitors: hero H1,
plan buttons, calculator CTAs, "How pricing works" highlight and the
Trial & Support FAQ (visible copy only, JSON-LD unchanged).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(ads): send offer via register API and fix pricing CTA contrast check

- RegisterUserInput accepts the optional `offer: 'direct'`; register and
  registration tests assert it reaches the POST /auth/register body and the
  user_signed_up event (signup_offer).
- Plan CTAs get a stable data-plan-cta hook. The contrast check now measures
  the visible label variant on /pricing/ and /pricing/?offer=direct instead
  of relying on the trial-specific aria-label.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(ads): per-ad images for ChatGPT builder-to-mobile campaigns

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(ads): ChatGPT ad images for use-case pages and money-back guarantee

Adds 12 images for live-update, native-build, build-without-mac and
webapp-to-mobile, puts the 14-day money-back guarantee on every image,
and moves them to public/ads/chatgpt/.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(ads): list image and tracking URL for every ChatGPT ad

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(attribution): expire the direct offer 180 days after capture

Store the capture time (offer_at) with the offer and drop offers that are
missing a timestamp or older than 180 days, in both the inline first-touch
script and lib/attribution. Revisits keep the original capture time, so
rewriting the cookie no longer extends the offer. offer_at is not forwarded
on register/console links.

normalizeOffer now accepts only the exact value 'direct' (no trimming or
case folding), and the PostHog fallback reader sets signup_offer.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(copy): direct-offer label for the comparison row and aria-label context

Direct visitors pay first, so the "Try before you pay" comparison row on
live-update and native-build now reads "Money-back guarantee" for them.

Rename direct_offer_plan_cta_aria to direct_offer_plan_cta_aria_label and
match _aria_label before the generic _label rule in the context generator,
so translators see it as an accessible label. Contexts regenerated with
bun run generate:message-contexts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(contrast): scan the direct-offer pricing page in an isolated context

The offer=direct navigation persisted the offer in the shared context, so
every later route was scanned as a paid-ads visitor. Run it in a throwaway
browser context and run the axe color-contrast scan on that variant too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(ads): native-build a_command copy matches the iOS-only command

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(register): stub password validation helpers in the VM harness

main (#1248) added password requirement checks to register.ts; the VM test
context did not provide the imported helpers or password element methods.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(types): declare the passwordrules input attribute for astro check

main (#1248) added passwordrules to the register password field, which
Astro's attribute types do not include, so astro check failed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(ads): deploy the site before importing the ChatGPT campaigns

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant