Overview
Numpay delegates identity to Web3Auth (MetaMask Embedded Wallets). A successful one-time-code login produces two things on the client:- A secp256k1 private key (the user’s EOA), reconstructed from shares held by Web3Auth’s Sapphire network. It never leaves the device.
- An ID token: a JWT signed by Web3Auth with ES256, whose identity claim is the verified phone number or email.
Login configuration
Login and registration sequence
Token verification
The backend verifies every protected request inauthenticateUser:
- JWKS:
https://api-auth.web3auth.io/jwks, fetched and cached byjose. - Audience check: without it, a token Web3Auth minted for any other app would verify. With it, only tokens for Numpay’s client ID pass.
- Identity claim: v10 tokens use
userId; older ones useverifierId. Both are accepted. The displaynameclaim is never used as identity. - Result:
req.user = { userId, phoneNumber? , email? }. Emails are trimmed and lowercased.
Rules the backend enforces
1
Identity comes only from the token
/user/register overwrites any phoneNumber or email in the body with the token’s values.2
One identity, one wallet
phoneNumber, email and walletAddress are unique columns. Registering a wallet already owned by another identity returns 409.3
Users edit only themselves
PUT /user/update/:id compares the target row’s phone or email with the token’s and returns 403 on mismatch. Wallet, phone and email can’t be changed through it.Client session handling
- The user record is cached in
localStorage.userfor instant reloads;/user/registeris called again on every session restore, so it doubles as “sync me”. - An axios interceptor adds
Authorization: Bearer <idToken>to every API call. - A
401clears the cached user and redirects to/login. - Visiting a payment link while logged out stores the URL in
redirectAfterLoginand returns to it after login.

