Signing in
After your application has checked the password, call beginSecondStep with the user’s ID, the browser’s details and, if it has one, the remembered-device cookie. Keep the SecondStep it returns in the server-side session: never in a cookie or a hidden field.
SecondStep step = tenonAuth.beginSecondStep(account.id(), new SignInContext(request.getRemoteAddr(), request.getHeader("User-Agent")), cookie(request, "tenon_device"), locale.getLanguage());const step = await tenonAuth.beginSecondStep(account.id, { context: { ipAddress: req.ip, userAgent: req.get('user-agent') }, trustedDeviceToken: req.cookies.tenon_device, language: 'ja',});step = tenon_auth.begin_second_step(str(account.id), SignInContext(client_ip, user_agent), trusted_device_token=request.cookies.get("tenon_device"), language="ja")var step = await tenonAuth.BeginSecondStepAsync(account.Id.ToString(), new SignInContext(HttpContext.Connection.RemoteIpAddress?.ToString(), Request.Headers.UserAgent), Request.Cookies["tenon_device"], "ja");What to show
Section titled “What to show”kind |
Page | Then |
|---|---|---|
CODE_ENTRY |
The code page: one field for 6 digits. With method EMAIL, say where the code went (emailSentTo, masked) and offer Send a new code (call beginSecondStep again, after 30 seconds) |
completeWithCode |
PUSH_WAIT |
The waiting page: “Approve the sign-in on your phone, and pick 42” (number). It reloads every 2 seconds, or polls with JavaScript |
pollApproval until it isn’t PENDING |
TRUSTED |
Nothing: a remembered browser | Sign in |
NO_METHOD |
The user has no method yet. Your policy: sign in with the password alone, or send them to set one up first | |
RECOVERY_ONLY |
No email could be sent. The recovery page only | completeWithRecoveryCode |
On the code and waiting pages, add a link Lost your phone? Use a recovery code. It leads to the recovery page: one field for a recovery code or a helpdesk access pass, then completeWithRecoveryCode.
The result
Section titled “The result”status |
What to do |
|---|---|
APPROVED |
Sign in. Then remember the browser if the user ticked the box (below) |
WRONG_CODE |
Show the page again: “Wrong code, attemptsRemaining tries left” |
PENDING |
Sign-in approval: keep waiting |
DENIED |
The user said on the phone that it wasn’t them. End the sign-in, and suggest they change their password: someone else knows it |
FAILED |
Too many wrong codes: back to the password |
EXPIRED |
Too slow: back to the password |
A FactorLockedException / FactorLockedError means 10 wrong codes in a row: the method is locked until lockedUntil. Show that time, and offer the recovery page.
“Don’t ask again on this device”
Section titled ““Don’t ask again on this device””Offer a checkbox on the code and waiting pages. When the user ticked it and the step is APPROVED:
TrustedDevice device = tenonAuth.rememberDevice(step.userId(), step.challengeId());// Cookie "tenon_device" = device.token(): HttpOnly, Secure, SameSite=Lax, until device.expiresAt() (14 days).const device = await tenonAuth.rememberDevice(step.userId, step.challengeId!);res.cookie('tenon_device', device.token, { httpOnly: true, secure: true, sameSite: 'lax', expires: device.expiresAt });device = tenon_auth.remember_device(step.user_id, step.challenge_id)response.set_cookie("tenon_device", device.token, httponly=True, secure=True, samesite="Lax", expires=device.expires_at)var device = await tenonAuth.RememberDeviceAsync(step.UserId, step.ChallengeId!.Value);Response.Cookies.Append("tenon_device", device.Token, new CookieOptions { HttpOnly = true, Secure = true, SameSite = SameSiteMode.Lax, Expires = device.ExpiresAt });When remembered browsers stop working:
- the user changes or removes their method;
- an Administrator signs them out of every device;
- after 14 days.
A token only works for its own user.
Languages
Section titled “Languages”language (ja or en) sets the email codes’ language. Without it they are bilingual. Phones show requests in their own language.
Limits that reach your users
Section titled “Limits that reach your users”- At most 5 approval requests or emails per user in 15 minutes: a
TooManyRequestserror withretryAfter. - Email codes: a new one at most every 30 seconds.
- Show these as “please wait”, never as a failed sign-in.