mirror of
https://github.com/hanzoai/id.git
synced 2026-10-16 18:38:15 +00:00
registration is a control, not a footnote
The heading offers two things and only one of them was a control. Creating an account was 13px of text at the foot of the login page, on the same line as the link for people who forgot a password, so a first-time visitor — whose only business here is that door — had to read past every way of signing in to find it. It is now a button of the same size and surface as the ways in, directly under the credential it is the alternative to: 52px tall and the full width of the column, against 17px of inline text before. It carries the whole OIDC request as it always did, so registration can still return the new account to the app that asked for it. What is left at the foot is the one link that belongs there.
This commit is contained in:
@@ -127,6 +127,35 @@ test('prompt=login gets a screen, never a silent mint', async () => {
|
||||
assert.equal(document.querySelectorAll('input[type=password]').length, 1)
|
||||
})
|
||||
|
||||
// ── The other door ───────────────────────────────────────────────────────────
|
||||
// The heading offers two things and only one of them was a control: registration
|
||||
// was 13px of text at the foot of the page, on the same line as the link for
|
||||
// people who forgot a password. A visitor whose only business here is that door
|
||||
// had to read past every way of signing in to find it.
|
||||
test('registration is a control, and it is not in the footnote row', async () => {
|
||||
land()
|
||||
await mount([])
|
||||
|
||||
const create = [...document.querySelectorAll('a')].find((a) => a.textContent?.trim() === 'Create account')
|
||||
assert.ok(create, 'the page must offer registration')
|
||||
assert.ok(
|
||||
create.className.split(' ').includes('hanzo-id-btn'),
|
||||
`registration must be a control, not a sentence; it rendered as "${create.className}"`,
|
||||
)
|
||||
|
||||
// Carrying the whole OIDC request is what lets registration return the new
|
||||
// account to the app that asked for it.
|
||||
const to = new URL(create.getAttribute('href')!, 'https://hanzo.id')
|
||||
assert.equal(to.pathname, '/signup')
|
||||
for (const k of ['client_id', 'redirect_uri', 'state', 'code_challenge']) {
|
||||
assert.ok(to.searchParams.get(k), `${k} must survive the hop to /signup`)
|
||||
}
|
||||
|
||||
// What is left at the foot is the one link that belongs there.
|
||||
const foot = document.querySelector('.hanzo-id-footer-links')!
|
||||
assert.equal(foot.textContent?.trim(), 'Forgot password?')
|
||||
})
|
||||
|
||||
// ── The registration hint ────────────────────────────────────────────────────
|
||||
// hanzo.app's "Get started" forwards `signup=true` on the authorize request and
|
||||
// this page ignored it, so a net-new customer landed on "Sign in to Hanzo ID"
|
||||
|
||||
@@ -240,14 +240,23 @@ export function Login({ client, brand }: { client: AuthClient; brand: BrandContr
|
||||
kind={kind}
|
||||
onKind={setKind}
|
||||
/>
|
||||
{/* The heading offers two things and only one of them was a control.
|
||||
Registration was 13px of text at the foot of the page, beside the
|
||||
link for people who forgot a password — so a first-time visitor,
|
||||
whose ONLY business here is this door, had to read past every way of
|
||||
signing in to find it. It is a button of the same size and surface as
|
||||
the ways in, directly under the credential it is the alternative to.
|
||||
|
||||
Carry the OIDC request across: this is a full page load, so a bare
|
||||
href drops the client_id, redirect_uri, state and PKCE challenge the
|
||||
app sent, and registration then has nothing to return the new account
|
||||
to. `Signup` reads exactly these params. */}
|
||||
<a className="hanzo-id-btn ghost" href={`/signup${window.location.search}`}>
|
||||
Create account
|
||||
</a>
|
||||
</SocialButtons>
|
||||
<p className="hanzo-id-footer-links">
|
||||
{/* Carry the OIDC request across. These are full page loads, so a bare
|
||||
href drops the client_id, redirect_uri, state and PKCE challenge the
|
||||
app sent — and registration then has nothing to return the new user
|
||||
to. `Signup` reads exactly these params. */}
|
||||
<a href={`/forget${window.location.search}`}>Forgot password?</a> ·{' '}
|
||||
<a href={`/signup${window.location.search}`}>Create account</a>
|
||||
<a href={`/forget${window.location.search}`}>Forgot password?</a>
|
||||
</p>
|
||||
</main>
|
||||
<BrandFooter brand={brand} org={client.org} />
|
||||
|
||||
Reference in New Issue
Block a user