Authentication
GiftWrapt uses better-auth for sessions. Out of the box you get email + password sign-in, with optional OIDC, WebAuthn passkeys, and TOTP-based two-factor.
Email + Password
Section titled “Email + Password”The default and the only mode that’s enabled with no extra configuration. Anyone with a working email address can sign up; the first account on a fresh deployment is automatically promoted to admin.
Password resets are tokenized URLs sent over email. If outbound email isn’t configured the reset path still appears to work from the user’s perspective (so it doesn’t leak which accounts exist), but the email itself is a no-op. Configure Resend or SMTP before relying on password resets.
Password reset tokens are good for 60 minutes.
OIDC (Single Sign-On)
Section titled “OIDC (Single Sign-On)”Admins can wire up any OpenID Connect provider (Authentik, Authelia, Keycloak, Google Workspace, etc.) from the admin settings UI. The form takes:
- Issuer URL (preferred) - GiftWrapt discovers the endpoints automatically via
/.well-known/openid-configuration. - Authorization URL + Token URL (alternative) - explicit endpoints if your provider doesn’t expose discovery.
- Client ID and Client Secret.
- Scopes - defaults to
openid email profile.
OIDC config is loaded once at server startup. Saving changes in the admin form requires a server restart to take effect, the same as Audiobookshelf and other better-auth-based apps.
If the config is invalid at boot, GiftWrapt logs a warning and falls back to email + password only. Operators can still sign in, fix the config, and restart.
Passkeys (WebAuthn)
Section titled “Passkeys (WebAuthn)”Passkeys are an add-on, not a primary sign-in method. Users sign in with email + password first, then enroll a passkey from their account settings page. Subsequent sign-ins can use the passkey instead.
The relying-party identifier defaults to the hostname of BETTER_AUTH_URL. If you’re testing over an IP address or an ngrok tunnel, set BETTER_AUTH_URL to the exact origin the browser loads so WebAuthn’s same-origin check passes.
Two-Factor Auth (TOTP)
Section titled “Two-Factor Auth (TOTP)”Users can enable TOTP-based 2FA from their account settings. Enrollment requires entering a valid code before 2FA is flipped on - so a misconfigured authenticator app can’t lock the user out. Backup codes are generated at enrollment time and shown once; users should store them somewhere safe.
API Keys (Mobile App)
Section titled “API Keys (Mobile App)”The companion iOS app authenticates via per-device API keys minted from the user’s account settings. Each install gets its own key, stored in the device Keychain. Revoking one device’s key doesn’t sign the user out of the web.
API keys are kept fully separate from the web session cookie. The web’s CSRF defense relies on the sameSite=lax cookie, and API keys never travel as cookies.
Cookies and Origins
Section titled “Cookies and Origins”Session cookies are httpOnly, sameSite=lax, and Secure (when the deployment is HTTPS). Two environment variables affect cookie behavior:
TRUSTED_ORIGINS- comma-separated extra origins to trust beyondBETTER_AUTH_URL. Set this if you reach the same instance from more than one hostname (e.g. a public domain plus a LAN IP).INSECURE_COOKIES=true- drops theSecureflag, so cookies work on plain-HTTP origins. The server refuses to boot with bothINSECURE_COOKIES=trueand an HTTPSBETTER_AUTH_URL.
Rate Limiting
Section titled “Rate Limiting”better-auth’s built-in rate limiter is enabled with database-backed counters (so the limits hold across instances on Vercel / Render / Railway). Sign-in, sign-up, password change, and session reads all have stricter caps applied automatically.
API keys have their own per-key limit of 300 requests per minute. That’s tuned for a chatty native client that pings verifyApiKey + getSession on every request; it still throttles a runaway client or a leaked key.