mirror of
https://github.com/hanzo-apps/hanzo.ai.git
synced 2026-10-30 14:58:08 +00:00
/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.
42 lines
1.8 KiB
TypeScript
42 lines
1.8 KiB
TypeScript
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\.$/)
|
||
})
|