Skip to Content
FeaturesCustomer Authentication

Customer Authentication

Customer authentication provides login, registration, and identity management for your end users. Foir supports password-based login, one-time passwords (OTP), and OAuth/OIDC providers.

Overview

Authentication Methods

MethodDescriptionUse Case
PasswordEmail + passwordTraditional login
OTPOne-time password sent via emailPasswordless login
OAuth/OIDCExternal identity providers (Google, Auth0, etc.)Social login, SSO

All methods return JWT access and refresh tokens upon successful authentication. The access token is included in subsequent API requests to identify the customer.

JWT Tokens

  • Access token: short-lived token included in the Authorization header for authenticated requests
  • Refresh token: longer-lived token used to obtain new access tokens without re-authenticating

In the Admin

Setting Up Auth Providers

  1. Go to Settings > Authentication
  2. View the list of configured auth providers
  3. Click Add Provider to configure a new one:
    • Key: unique identifier (e.g., google)
    • Name: display name (e.g., “Google Login”)
    • Type: OAuth, OIDC, or other supported type
    • Client ID / Secret: credentials from the provider
    • Redirect URL: the callback URL for OAuth flows
    • Enabled: toggle to activate or deactivate
    • Priority: ordering for display in login UIs
  4. Save

JWT Configuration

Configure JWT settings in Settings > Authentication > JWT:

  • Token expiration times
  • Signing configuration
  • Refresh token behavior

Via the CLI

# List configured auth providers foir auth-providers list # Get details for a specific provider foir auth-providers get <id> # Create a new auth provider foir auth-providers create --data '{ "key": "google", "name": "Google Login", "type": "OAUTH", "enabled": true, "config": { "clientId": "your-client-id", "clientSecret": "your-client-secret", "redirectUrl": "https://yoursite.com/auth/callback" } }' # Update an auth provider foir auth-providers update <id> --data '{"enabled": false}' # Delete an auth provider foir auth-providers delete <id> # View current auth configuration foir auth config # Verify a customer token foir auth verify-token <token>

Via the API

Standard Login (Password)

mutation CustomerLogin($email: String!, $password: String!) { customerLogin(email: $email, password: $password) { success accessToken refreshToken user { id email userType } } }

Registration

mutation CustomerRegister($email: String!, $password: String!) { customerRegister(email: $email, password: $password) { success accessToken refreshToken user { id email } emailVerificationRequired } }

Logout

mutation CustomerLogout { customerLogout { success message } }

OTP Authentication

Request a one-time password, then use it to log in:

mutation RequestOTP($email: String!) { customerRequestOTP(email: $email) { success expiresAt message } }
mutation LoginWithOTP($email: String!, $otp: String!) { customerLoginOTP(email: $email, otp: $otp) { success accessToken refreshToken user { id email } } }

Password Reset

mutation RequestReset($email: String!) { customerRequestPasswordReset(email: $email) { success message } }
mutation ResetPassword($token: String!, $newPassword: String!) { customerResetPassword(token: $token, newPassword: $newPassword) { success message } }

Update Password (Authenticated)

mutation UpdatePassword($currentPassword: String!, $newPassword: String!) { customerUpdatePassword( currentPassword: $currentPassword newPassword: $newPassword ) { success message } }

Token Refresh

mutation RefreshToken($refreshToken: String!) { customerRefreshToken(refreshToken: $refreshToken) { success accessToken refreshToken } }

Current User

query CurrentUser { currentUser { id email emailVerified status userType createdAt } }

Auth Providers

List available providers for your login UI:

query ListProviders { authProviders { id key name type enabled isDefault priority } }

OAuth Callback

After the user completes the OAuth flow, exchange the code:

mutation ProviderCallback($code: String!, $state: String!) { customerProviderCallback(input: { providerKey: "google" code: $code state: $state }) { accessToken refreshToken user { id email } isNewCustomer } }

Auth Configuration

Query the project’s auth settings to build dynamic login forms:

query AuthConfig { authConfig { authMethods publicRegistrationEnabled passwordPolicy { minLength requireUppercase requireLowercase requireNumbers requireSpecialChars } } }

Complete Login Flow Example

// 1. Check auth config to know which methods are available const { data: config } = await client.query({ query: AUTH_CONFIG }); // 2. Login with email and password const { data: login } = await client.mutate({ mutation: CUSTOMER_LOGIN, variables: { email, password } }); if (login.customerLogin.success) { // 3. Store tokens localStorage.setItem('accessToken', login.customerLogin.accessToken); localStorage.setItem('refreshToken', login.customerLogin.refreshToken); // 4. Use access token for authenticated requests const { data: user } = await client.query({ query: CURRENT_USER, context: { headers: { Authorization: `Bearer ${login.customerLogin.accessToken}` } } }); }

Using Access Tokens

Include the access token in subsequent requests:

curl -X POST https://api.foir.io/graphql \ -H "Content-Type: application/json" \ -H "x-api-key: pk_live_..." \ -H "Authorization: Bearer eyJhbGc..." \ -d '{"query": "{ currentUser { id email } }"}'

Config System

Auth providers can also be defined in foir.config.ts using defineAuthProvider. See the Configuration reference for details.

Project Email & Brand

Every project has a brand block that controls how customer-facing emails (welcome, password reset, OTP, email verification) look and where they link. These are project-level settings, not per-user.

FieldPurpose
displayNameHuman-readable project name shown in emails (“Bob’s Country Bunker”)
logoUrlURL of the project logo embedded in email templates. Defaults to app.foir.dev/logo.png if unset.
primaryColorBrand colour applied to email buttons and accents (hex string, e.g. #2c4433)
fromNameDisplay name on the From header (From: Bob's Country Bunker <noreply@…>)
replyToAddress customers reach when replying to platform emails
supportEmailAddress shown to customers in error states (“Contact support@…”)
customerPortalBaseURLOrigin of your storefront/portal — used as the base for links in emails ({customerPortalBaseURL}/reset-password?token=…). Required for password reset, OTP, and welcome emails to work.
customerWelcomeEmailEnabledToggle: send a welcome email on customerRegister. Off by default.

Configuring brand fields

In the admin: Settings → Project → Brand. Fill in display name, logo URL, primary colour, support address, and your customer portal URL.

If customerPortalBaseURL is unset, password-reset emails fail at send time with a clear “customer portal base URL is not configured” error — they can’t generate the reset link without it. Set it before turning on registration flows.

Welcome emails

When customerWelcomeEmailEnabled is true, the platform sends a welcome email with the project’s branding to every new customer registered via customerRegister. Toggle it off if your application sends its own welcome email and you don’t want a duplicate.

Email service

Outbound emails use a single EmailService driven by Resend. Email tags and templates are project-aware — every send pulls the project’s brand block to render the template. If you maintain multiple projects under one tenant, each project’s emails use its own brand independently.

Best Practices

  • Always handle token refresh — implement automatic refresh when access tokens expire.
  • Query auth config first — build login UIs dynamically based on enabled methods and password policies.
  • Use OTP for passwordless flows — simpler for users and avoids password management.
  • Verify tokens server-side — use foir auth verify-token or the currentUser query to validate sessions.
  • Set appropriate token lifetimes — balance security with user convenience.
Last updated on