Restyle login page with white card layout + Certified branding - #110
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughThis PR introduces a theme-driven CSS system for the login page with redesigned card and typography, replaces Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
🦋 Changeset detectedLatest commit: dd5faf4 The changes in this PR will be included in the next version bump. Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
🚅 Deployed to the ePDS-pr-110 environment in ePDS
|
Coverage Report for CI Build 25102591635Coverage increased (+0.1%) to 48.544%Details
Uncovered ChangesNo uncovered changes found. Coverage Regressions1 previously-covered line in 1 file lost coverage.
Coverage Stats
💛 - Coveralls |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
White card on muted grey, pill buttons, segmented 6-box OTP input with paste/arrow/backspace/auto-submit, "Powered by Certified" footer, Certified brandmark as fallback top logo. Adds an ATProto/Bluesky toggle that swaps the email form into handle-entry mode and submits to <client-origin>/api/oauth/login?handle=X — the client then resolves the handle to its PDS and starts a fresh PAR. Off-PDS handles end up on their own PDS's auth screen. Removes the "Recover with backup email" link from the page. Hardcodes the Terms/Privacy notice to Certified's terms (the auth server is always Certified's regardless of which client started the flow), with both links underlined and pointing at certified.app. Hides the Terms/Privacy notice on the OTP step. h1 reads "Sign in" on the email step, "Enter your code" on the OTP step. Exposes CSS custom properties for trusted-client theming: --page-bg, --card-bg, --input-bg, --input-border, --muted-foreground, --card-border, --btn-secondary-border, --focus-border Defaults stay neutral grey; trusted clients can override any of these via injected branding.css. Inlines the Certified wordmark SVG so the "Powered by" footer can tint via currentColor.
The login-page restyle replaced the single visible OTP input with a
hidden #code field plus six .otp-box slots. page.fill('#code', ...)
resolves the locator but Playwright refuses to fill a type=hidden
input, timing out every OTP-step scenario.
Add a fillOtp(page, otp) helper that clears any stale digits, then
types one per box. The page's input handler auto-submits on the last
digit, so the explicit verify-button click is dropped at every call
site. The 'incorrect OTP {int} times' loop registers waitForResponse
before fillOtp since the submit fires from inside the helper.
Also reworks buildIncorrectOtpCode's DOM-inference fallback: length
now comes from .otp-box count and charset from a single box's
inputmode, since the hidden #code carries no maxlength / pattern.
The recovery flow's #code input is unchanged and untouched here.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Restores the "Recover with backup email" link on the OTP step, shown by default, controlled by the new --recovery-link-display CSS var so clients can hide it via branding.css. E2E: recovery flows navigate to /auth/recover directly (more robust than depending on the link's visibility, which is now client-configurable). DEFAULT_LOGIN_BG_RGB updated to match the new --page-bg default (#E8E8E8). Adds a changeset documenting the login-page CSS theming surface. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Validate brand_color as a hex literal before interpolating into <style>; escapeHtml() doesn't sanitise CSS context, so a value containing `;` or `}` could break out of the declaration. - OTP slot placeholder/aria-label now switch to "A"/"Character" when otpCharset === 'alphanumeric', matching the configured charset. - Clarify EPDS_CLIENT_THEME unset behaviour in packages/demo/.env.example (getTheme() returns null; demo uses its own light look; no branding CSS shipped → auth-service renders default Certified styling). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The atproto-login-button feature was authored before PR #110's restyle changed the email label from "Email address" to "Enter your email address". Update the assertion to match the new copy. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Autonomous loop done — ready for review/mergeCI: 15/15 green (e2e finished in 8m8s). Branch tip: Changes since last user touch:
Unresolved review threads: 0. Ready for review/merge. |
Replace the hardcoded https://certified.app/terms and /privacy URLs in the login page with values read from PDS_TERMS_OF_SERVICE_URL / PDS_PRIVACY_POLICY_URL — the same env vars upstream PDS already consumes, so deployments configure them once. The terms line is omitted entirely when either var is missing (strict; avoids dead half-links). Optional PDS_LEGAL_ENTITY_NAME controls the possessive in the line: "By signing in, you agree to <name>'s Terms of Use and Privacy Policy." When unset, falls back to "By signing in, you agree to the Terms of Use and Privacy Policy." Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Upstream @atproto/oauth-provider-ui renders the consent + chooser pages with purple-on-white defaults that don't match the rest of an ePDS deployment. Inject a neutral default stylesheet (muted grey page bg, light card surface, dark primary button) into every /oauth/authorize response so unbranded deployments render coherently with auth-service's restyled login page. Trusted-client branding.css is now layered on top of the default — cascade order means client styles still win on overlapping selectors, no client opt-in or migration needed. CSP: each <style> block is hashed separately and appended to style-src. Also covers the /preview/consent and /preview/chooser routes so client-app developers iterate against the production look. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Justifies the absence of any preview-only override that synthesises `epds_handle_login_url`: the /preview/login button visibility is identical to the real /oauth/authorize flow, so iterating against real client metadata is the only way to see the button. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
| updateHiddenCode(); | ||
| if (box.value && idx < otpBoxes.length - 1) otpBoxes[idx + 1].focus(); | ||
| if (hiddenCode.value.length === otpBoxes.length) { | ||
| document.getElementById('form-verify-otp').requestSubmit(); |
There was a problem hiding this comment.
This auto-submit caused a few bugs, one of which is fixed by #134.



Summary
Restyle the OAuth login page (
packages/auth-service/src/routes/login-page.ts):Before:
After:

currentColor), Certified brandmark as fallback top logo when a client omitslogo_uri.LoginForm). Handle submission redirects to<client-origin>/api/oauth/login?handle=X, which resolves the handle dynamically and starts a fresh PAR against the correct PDS — so off-PDS handles end up on their own PDS's auth screen, just like the demo's flow.By signing in, you agree to Certified's Terms of Use and Privacy Policy.— both links underlined and pointing tohttps://certified.app/termsandhttps://certified.app/privacy. Copy does not vary per client (the auth server is always Certified's, regardless of which client initiated the flow).Email address→Enter your email address,Continue with email→Continue, removedto use <AppName>subtitle, removed the h1 subtitle treatment.Theming — CSS variables for trusted clients
Trusted clients can override these from their injected
branding.css:Defaults:
--page-bg: #E8E8E8,--card-bg: #F8F8F8,--input-bg: #ffffff,--input-border: #e5e5e5,--muted-foreground: #999. The email input and the ATProto button consume the same--input-bg/--input-borderso they stay visually matched.Hiding the "Recover with backup email" link
The recovery link in the OTP step is shown by default. Clients that don't want to surface backup-email recovery from the OAuth login flow can hide it from their
branding.css:The link itself stays in the DOM (so the recovery flow at
/auth/recoveris still reachable via direct navigation, e.g. from the/accountpage) — only its appearance on the login page is suppressed. Default isblock.Assets added
packages/auth-service/public/certified-text-monochrome.svg— wordmark (inlined into the page, themeable viacurrentColor)packages/auth-service/public/certified-brandmark.svg— square icon (fallback top logo, served at/static/certified-brandmark.svg)Before / After
Before:
After:
Preview URLs (PR-110 Railway environment)
Test plan
<client-origin>/api/oauth/login?handle=Xand (for off-PDS handles) ends up on the correct PDS's auth screen.logo_uri— Certified brandmark appears as top logo.logo_uri— its logo appears at top.branding.cssoverrides work: set--page-bg,--card-bg,--input-bg,--input-border,--muted-foregroundand confirm all surfaces retint in unison.:root { --recovery-link-display: none; }from a client'sbranding.csshides it.https://certified.app/termsandhttps://certified.app/privacyin new tabs.🤖 Generated with Claude Code
Summary by CodeRabbit
Release Notes
New Features
Style
Tests