Email OTP (Otp)

The passwordless OTP method emails the user a short verification code. The user enters it on the code-verify page; on success they are logged in — or, under the register-first policy, sent to the register form with a verified-email token.

How It Works

sequenceDiagram
  actor U as User
  participant P as Login page
  participant A as Auth API
  participant S as Temp key store
  participant E as Email
  participant B as PostAuth pipeline
  U->>P: enter email
  P->>A: POST /api/login
  A->>S: store email:code under nonce
  A->>E: send code (FuncEmailSend/Enqueue)
  U->>P: enter code (+ nonce)
  P->>A: POST /api/login-code-verify
  A->>S: verify + consume code (single-use)
  alt existing account
    A->>B: authenticate → session + cookie
  else new email (register-first)
    A->>S: issue verified_key token
    A-->>U: redirect /register?verified_key=
    U->>A: submit names + verified_key
    A->>B: single INSERT → session
  end
  1. User enters their email on /auth/login and submits
  2. POST /auth/api/login generates a verification code, stores email:code under a random nonce, and sends it via FuncEmailSend/FuncEmailEnqueue
  3. The client keeps the nonce (sessionStorage, never the URL) and lands on /auth/login-code-verify
  4. POST /auth/api/login-code-verify checks nonce + code (attempt-limited, single-use) and verifies the email
  5. On success the post-auth pipeline resolves or creates the user, creates the session, and returns the redirect

Registration

Two policies, selected by ConfigShared.UserCreationPolicy:

Setup

a, err := auth.NewOtpAuth(types.ConfigOtp{
    ConfigShared: types.ConfigShared{
        Endpoint:                "/auth",
        UrlRedirectOnSuccess:    "/",
        UseCookies:              true,
        FuncTemporaryKeyGet:     tempKeyGet,
        FuncTemporaryKeySet:     tempKeySet,
        FuncUserFindByAuthToken: userFindByAuthToken,
        FuncUserLogout:          userLogout,
        FuncUserStoreAuthToken:  userStoreAuthToken,
        EnableRegistration:      true,
        // optional: UserCreationPolicy: types.CreateUserOnRegister,
        // optional: FuncEmailCaptured: captureLead,
    },
    FuncUserFindByEmail: userFindByEmail,
    FuncUserRegister:    userRegister,        // required when EnableRegistration
    FuncEmailSend:       emailSend,
})
// Mount the built-in routes:
mux.Handle("/auth/", http.StripPrefix("", a.Router()))

Routes

RouteBehavior
GET|POST /auth/loginEmail form + code-send API
GET /auth/login-code-verifyCode entry page
POST /auth/api/login-code-verifyCode verification → session or verified_key redirect
GET|POST /auth/registerRegister / completion form
POST /auth/api/register-code-verifyRegistration code verify → create + authenticate
GET /auth/logoutLogout page

Notes

A complete working example is available in examples/passwordless.