1
Send the code
auth.signin.start takes a method and an identifier and returns a challengeId. The methods are email_code, email_link and sms_code.It never reveals whether the address is already a user. That is deliberate: an endpoint that answers differently for a known address is an account-enumeration oracle, and this one cannot be used as one.Its permission is auth:public, so your frontend can call it with a publishable key. No secret key in a browser.2
Exchange it for a session
auth.signin.verify takes the challengeId and the six digits. On a first sign-in it creates the user and a verified identity, then issues a session: a short-lived access token and a rotating refresh token.No sign-up call is needed first — an account is a side effect of proving an address. The environment’s sign-up settings can refuse that first verify instead of creating the user: signup_closed when sign-up is off, signup_invite_only when it needs an invitation, signup_domain_blocked for a disposable email domain (bot protection, on by default), and rate_limited past the per-IP sign-up limit. Somebody who already has an account signs in either way.Importing users you already have is a server call instead: auth.user.create makes a user with a verified email or phone, without a sign-in.3
Handle the second factor
When the user has a second factor, or the environment requires one,
verify returns mfa.mfaToken instead of a session. Read for the session before assuming you have one — a client that reads session.accessToken unconditionally crashes on exactly the accounts that took security seriously.Finish with auth.mfa.verify, which takes the token and the digits from the authenticator app, or a recovery code.When the environment requires a second factor and the user has none yet, the token’s methods is ["enrol"] and auth.mfa.verify refuses with mfa_enrol_required. Enrol one first with auth.factor.totp.enroll and auth.factor.totp.activate.4
Check the token on your own API
verifyAccessToken in @trylath/sdk validates a token against your environment’s published keys, so your backend does not call Lath on every request. requireUser does it for a standard Request.See the SDK guide for the framework-shaped versions.
