Configuration

Configuration

All options you can pass to createAuth(). Only jwt.secret is required - everything else has a sensible default.

auth.ts
import { createAuth, RedisStore } from "guardo";
 
const auth = createAuth({
  // ─── Required ─────────────────────────────────────────
  jwt: {
    secret: "at-least-16-chars",
    accessTokenTTL: "15m", // default
    refreshTokenTTL: "7d", // default
    extraClaims: { iss: "my-app" }, // embedded in every token
    algorithm: "HS256", // default; use "RS256" for asymmetric keys
  },
 
  // ─── OTP ──────────────────────────────────────────────
  otp: {
    length: 6, // default
    expiry: 300, // seconds - default 5 minutes
  },
 
  // ─── Storage ──────────────────────────────────────────
  store: new RedisStore(redisClient), // default: MemoryStore
 
  // ─── Notifications ────────────────────────────────────
  notifier: myEmailNotifier, // default: NodemailerNotifier
 
  // ─── Rate limits ──────────────────────────────────────
  rateLimit: {
    otpSend: { max: 5, windowSeconds: 60 }, // per identifier
    otpVerify: { max: 10, windowSeconds: 60 }, // per identifier
    otpSendPerIp: { max: 20, windowSeconds: 60 }, // per IP
    otpVerifyPerIp: { max: 30, windowSeconds: 60 }, // per IP
  },
 
  // ─── User resolution ──────────────────────────────────
  // Called after OTP verification and on every authed request.
  resolveUser: async (identifier) => db.users.findByEmail(identifier),
 
  // Called when resolveUser returns null - auto-provision new users.
  onNewUser: async (identifier) => db.users.create({ email: identifier }),
 
  // ─── Lifecycle events ─────────────────────────────────
  events: {
    "login.success": ({ user }) => audit.log(user.id, "login"),
    "token.reuse_detected": ({ userId }) => alertTeam(userId),
  },
 
  // ─── httpOnly cookie transport (optional) ─────────────
  cookies: {
    accessTokenCookie: "guardo_access",
    refreshTokenCookie: "guardo_refresh",
    sameSite: "lax",
    secure: true,
  },
 
  // ─── OAuth / social login (optional) ──────────────────
  oauth: {
    providers: [
      new GoogleProvider({ clientId: G_ID, clientSecret: G_SECRET }),
      new GithubProvider({ clientId: GH_ID, clientSecret: GH_SECRET }),
    ],
    redirectUri: "https://myapp.com/auth/callback",
    resolveUser: async (profile) => db.users.findByEmail(profile.email),
    onNewUser: async (profile) =>
      db.users.create({ email: profile.email, name: profile.name }),
  },
});

Config reference

jwt (required)

OptionTypeDefaultDescription
jwt.secretstringrequiredSecret key for signing JWTs. Must be at least 16 characters or createAuth() throws.
jwt.accessTokenTTLstring"15m"Access token lifetime, e.g. "15m", "1h".
jwt.refreshTokenTTLstring"7d"Refresh token lifetime, e.g. "7d", "30d". Also bounds session TTL.
jwt.extraClaimsobject{}Additional fields embedded in every token.
jwt.algorithmstring"HS256"Signing algorithm. Use "RS256" (etc.) with an asymmetric key in secret.

otp

OptionTypeDefaultDescription
otp.lengthnumber6Length of generated OTP codes.
otp.expirynumber300OTP TTL in seconds.
otp.exposeCodebooleanfalseReturn the plaintext code from otp.send() (as code) for tests. Never enable in production.

Storage & delivery

OptionTypeDefaultDescription
storeStorageAdapterMemoryStoreWhere OTPs, sessions, and rate-limit windows are stored.
notifierNotifierNodemailerNotifierHow OTPs are delivered.
emailEmailNotifierOptions{}Shorthand passed to the default NodemailerNotifier. Ignored if you set notifier.

rateLimit

OptionTypeDefaultDescription
rateLimit.otpSendRateLimitRule5 / 60sMax OTP send requests per identifier.
rateLimit.otpVerifyRateLimitRule10 / 60sMax OTP verify requests per identifier.
rateLimit.otpSendPerIpRateLimitRule20 / 60sMax OTP send requests per IP. Only enforced when you pass ip to otp.send().
rateLimit.otpVerifyPerIpRateLimitRule30 / 60sMax OTP verify requests per IP.

A RateLimitRule is { max: number; windowSeconds: number }.

User resolution & lifecycle

OptionTypeDefaultDescription
resolveUser(identifier) => Promise<User | null>undefinedReturn a full User from an identifier. Used after OTP verification and in middleware so req.user is complete.
onNewUser(identifier) => Promise<User>undefinedCalled when resolveUser returns null. Auto-provision and return a new User.
eventsPartial<GuardoEvents>{}Lifecycle event handlers - see Events.
cookiesCookieOptionsundefinedEnable httpOnly cookie transport - see Express middleware.
oauthOAuthConfigundefinedEnable OAuth / social login - see OAuth module.

oauth

OptionTypeDefaultDescription
oauth.providersOAuthProvider[]requiredProviders to enable - GoogleProvider, GithubProvider, or any OAuth2Provider.
oauth.redirectUristringundefinedDefault callback URL. Can be overridden per request in oauth.start().
oauth.stateTtlSecondsnumber600How long a pending authorization state stays valid.
oauth.resolveUser(profile, providerId) => Promise<User | null>undefinedResolve an app User from an OAuth profile. Return null to provision via onNewUser.
oauth.onNewUser(profile, providerId) => Promise<User>undefinedProvision a new User when resolveUser returns null.
⚠️

If neither resolveUser nor onNewUser is provided, guardo falls back to treating the identifier itself as the user - { id: identifier, email/phone: identifier }. Fine for prototypes, but wire up resolveUser for real apps.