Files
hanzo.ai/e2e/unit/phone.spec.ts
zeekay ac2da21e5f sign-in: wallet and phone, and a person claims a wallet's account
/login and /signup draw "Continue with wallet" and "Continue with phone"
after Google and GitHub, each only where IAM's methods say it is on.

Wallet: where IAM signs a wallet in (@hanzo/iam 0.21.37 `wallet`) and the
browser injects an EIP-1193 wallet; the press is loginWithWallet (IAM's
challenge, personal_sign of its EIP-4361 text, IAM's verify) and ends as a
password's sign-in ends. A wallet IAM has not seen is made an account in an
org of its own, with no email.

Phone: where IAM says it sends codes (`code`) and has answered. The number
is read in its country with the SMS consent ticked, then IAM's code flow.
IAM's address lookup answers an email address only, so a number is texted as
any is and the account that holds it is signed in by the code.

Settings -> General -> Sign in another way adds an email or a phone to the
signed-in account through IAM's own proof (addAddress, PUT /v1/iam/address),
drawn only where IAM's methods say `address`. Same account, org, keys and
balance; nothing is created, so nothing earns twice.

Gates: wallet.spec (a throwaway secp256k1 key behind an injected provider),
phone.spec, address.spec. login.spec expects the phone after the providers.
2026-10-26 16:47:32 -07:00

42 lines
1.8 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { test, expect } from '@playwright/test'
import { e164 } from '@hanzo/iam/validation'
import { CONSENT, countries, RESEND, TRIES, wait } from '../../lib/auth/phone'
test('IAM’s own limits: one code per number per 30 seconds, five guesses a code', () => {
expect(RESEND).toBe(30)
expect(TRIES).toBe(5)
})
test('another code waits out the rest of the 30 seconds, and never less than nothing', () => {
expect(wait(0, 0)).toBe(30)
expect(wait(0, 999)).toBe(30)
expect(wait(0, 1000)).toBe(29)
expect(wait(0, 29_999)).toBe(1)
expect(wait(0, 30_000)).toBe(0)
expect(wait(0, 90_000)).toBe(0)
})
test('every country is offered with its calling code, named in the reader’s language', () => {
const en = countries('en')
expect(en.length).toBeGreaterThan(200)
expect(en.find((c) => c.value === 'US')).toEqual({ value: 'US', label: 'United States', callingCode: '1' })
expect(en.find((c) => c.value === 'GB')?.callingCode).toBe('44')
expect(countries('fr').find((c) => c.value === 'DE')?.label).toBe('Allemagne')
// In the locale's order.
const labels = en.map((c) => c.label)
expect(labels).toEqual([...labels].sort((a, b) => a.localeCompare(b, 'en')))
})
test('the number IAM is sent is E.164, the trunk prefix dropped, read in the picked country', () => {
expect(e164('(415) 555-0134', 'US')).toBe('+14155550134')
expect(e164('07911 123456', 'GB')).toBe('+447911123456')
expect(e164('+44 7911 123456', 'US')).toBe('+447911123456')
expect(e164('555', 'US')).toBeNull()
})
test('the consent is hanzo/id’s A2P wording, for Hanzo AI', () => {
expect(CONSENT).toMatch(/^I agree to receive text messages \(SMS\) from Hanzo AI at the number provided,/)
expect(CONSENT).toContain('Reply STOP to opt out at any time, or HELP for help.')
expect(CONSENT).toMatch(/Consent is not a condition of any purchase\.$/)
})