OpenTrip Docs
BackendHTTP API

Authentication and session

Authentication and session

Auth is Better Auth mounted at /api/auth/*. See auth.md for server config, captcha, Google/WeChat OAuth, and defaultCurrency.

Session model for non-browser clients

  1. Session middleware runs on every request: auth.api.getSession({ headers }).
  2. Business routes under /api (except health, invite preview, uploads) require a session; otherwise 401.
  3. The web app sends cookies with credentials: "include" (apps/web/src/shared/api/client.ts).
  4. Mobile / native apps should:
    • Point the Better Auth client (or equivalent HTTP) at the same API origin and base path /api/auth.
    • Persist and re-send the session cookie (cookie jar / Cookie header) on every API call after sign-in. CORS credentials: true and TRUSTED_ORIGINS apply to browser clients; native apps talking to the API origin directly rely on cookie storage, not browser CORS.
    • Prefer the official Better Auth client for the platform when available so sign-in, sign-up, session refresh, and sign-out stay compatible.

The native WeChat shell uses the Better Auth bearer() plugin only during bootstrap. It exchanges wx.login() for a session, mints a one-time WebView code, and discards the bearer. The embedded PWA exchanges the code for the same HttpOnly cookie used by ordinary browsers, so business requests and domain authorization have no client-specific path. See ../../frontend/miniapp.md.

Client-relevant Better Auth surfaces

Not every Better Auth plugin path is listed here. Clients need at least:

ActionTypical path (under /api/auth)Notes
Email sign-upPOST …/sign-up/emailCreates unverified user; OTP emailed. Captcha when enabled
Verify email OTPPOST …/email-otp/verify-emailMarks verified + auto sign-in
Resend email OTPPOST …/email-otp/send-verification-otpCaptcha when enabled
Email sign-inPOST …/sign-in/emailUnverified → EMAIL_NOT_VERIFIED + OTP resent
Social sign-inPOST …/sign-in/socialGoogle or WeChat web QR when configured
Mini Program WeChat sign-inPOST …/wechat-mini-program/sign-inBody { code } from wx.login(); returns the normal Better Auth session token
SessionGET …/get-sessionCurrent user + session
Sign-outPOST …/sign-outClears session
Update userBetter Auth updateUsere.g. name, defaultCurrency

Native OAuth bridge

MethodPathPurpose
GET/api/mobile-auth/oauth/start?provider=google302 to Google with Better Auth OAuth state cookies (open inside ASWebAuth)
GET/api/mobile-auth/oauth/completeConvert the browser cookie session to a one-time app callback code
POST/api/mobile-auth/oauth/exchangeConsume { code } and return { token, session }

The callback code is hashed at rest, valid for three minutes, and consumed on first use. Native business requests send the returned session token as a Bearer credential.

Mini Program WebView bridge

MethodPathPurpose
POST/api/mobile-auth/webview/mintAuthenticate the shell bearer and return { data: { code } }
POST/api/mobile-auth/webview/exchangeConsume { code } and forward the Better Auth HttpOnly session cookie

The bridge reuses the one-time-token guarantees above. Both endpoints are private, no-store; the bearer is never accepted in a URL and the code is removed from the PWA fragment before exchange.

Cloudflare Turnstile (when CAPTCHA_PROVIDER=cloudflare-turnstile) intercepts protected auth POSTs via header x-captcha-response. Other CAPTCHA providers are not supported. See auth.md.

Avatar image for the signed-in user is not only Better Auth: use POST/DELETE /api/users/avatar so storage and profile stay consistent.


← API index · Auth deep-dive

On this page