Log in

Authentication

Forte provides built-in authentication for your project's users. You configure the authentication methods you want to support, and Forte handles user creation, session management, and security. Your application interacts with Forte's SDK to authenticate users and manage their sessions.

Forte supports Google OAuth and one-time-passcode (OTP) login over email or SMS. For password-based sign-in, see Passwords.

Client-side API

The authentication flows on this page are part of Forte's client-side API (forte.users.*). Call them directly from your frontend — the response sets the Forte-User-Session-Token cookie automatically. Never call these from code that also holds FORTE_API_TOKEN.

Google OAuth

Google OAuth lets your users sign in with their Google accounts. Forte manages the entire OAuth flow. Configure your Google OAuth Client ID on your project, and Forte handles the rest: token verification, user creation, deduplication, CAPTCHAs, and session management.

How It Works

  1. Your application renders Google's Sign-In button using your project's Google OAuth Client ID.
  2. When a user completes the Google sign-in flow, Google returns a credential token to your application.
  3. Your application sends the credential token to Forte's callback endpoint, along with a reCAPTCHA token if bot protection is enabled.
  4. Forte verifies the token, creates or links the user, and returns a session token.

Example

typescript
import { GoogleLogin } from "@react-oauth/google";
import { ForteClient } from "@forteplatforms/sdk";
 
const forte = new ForteClient();
 
function LoginButton({ projectId }: { projectId: string }) {
  const handleSuccess = async (response) => {
    const csrfToken = crypto.randomUUID();
 
    // If reCAPTCHA is configured on your project, execute before calling the callback
    const recaptchaToken = await grecaptcha.execute("YOUR_RECAPTCHA_SITE_KEY", {
      action: "login",
    });
 
    const result = await forte.users.googleAuthLoginCallback({
      projectId,
      gCsrfToken: csrfToken,
      credential: response.credential,
      recaptchaToken, // optional — required if reCAPTCHA is configured
    });
 
    // result.userObject — the authenticated user
    // result.sessionToken.sessionToken — use for subsequent requests
    // result.sessionToken.expirationTime — token expiry (default: 365 days)
    console.log("Logged in as", result.userObject.fullName);
  };
 
  return <GoogleLogin onSuccess={handleSuccess} />;
}

Setup

Configure your Google OAuth Client ID on your project in the Console or via the CLI. Once configured, your application can use Google's Sign-In button and send the resulting credential to Forte.

User Creation and Linking

  • If the user is signing in for the first time, Forte automatically creates a new user account with a verified Google contact method.
  • If a user with the same email already exists in the project, Forte links the Google account to the existing user.
  • Google OAuth contact methods are verified by default since Google has already verified the user's email.

Response

A successful authentication returns:

  • The user object with the user's profile and contact methods
  • A session token for authenticating subsequent requests
  • The session token's expiration time (defaults to 365 days)
Session Cookies

Forte automatically sets a Forte-User-Session-Token cookie on the response. Your application can use either the cookie or the session token from the response body to authenticate subsequent requests.

Bot Protection with reCAPTCHA

Forte supports Google reCAPTCHA v3 for automatic bot protection on your project's authentication endpoints. When configured, Forte validates a reCAPTCHA token on every sign-up and login request, rejecting requests that appear to come from bots or automated scripts.

reCAPTCHA v3 runs invisibly in the background — your users never see a challenge or checkbox. Instead, Google assigns a risk score to each request, and Forte rejects requests with scores that indicate automated or suspicious activity.

Optional Feature

reCAPTCHA is only enforced when a secret key is configured on your project. If no key is set, authentication endpoints work normally without reCAPTCHA validation.

Why Use reCAPTCHA

  • Prevent credential stuffing: Block automated login attempts that test stolen credentials against your application.
  • Stop spam registrations: Prevent bots from creating fake user accounts in bulk.
  • No user friction: reCAPTCHA v3 is invisible — legitimate users are never interrupted with challenges.

Setup

  1. Go to the Google reCAPTCHA admin console and create a new site.
  2. Select Score based (v3) as the reCAPTCHA type.
  3. Add your application's domain(s) to the allowed domains list.
  4. Google will provide two keys:
    • Site key — use this in your frontend to load the reCAPTCHA script and generate tokens.
    • Secret key — set this in your Forte project settings.
  5. In the Forte Console, go to Project Settings and enter your reCAPTCHA v3 secret key.

Frontend Integration

Load the reCAPTCHA v3 script in your application and execute it before calling Forte's authentication endpoints. Pass the resulting token as recaptchaToken in your SDK calls.

typescript
// Load reCAPTCHA v3 script in your HTML:
// <script src="https://www.google.com/recaptcha/api.js?render=YOUR_SITE_KEY"></script>
 
// Execute reCAPTCHA before authentication
const recaptchaToken = await grecaptcha.execute("YOUR_SITE_KEY", {
  action: "login", // use "login" or "signup" for Google OAuth
});
 
// Pass the token to the Forte SDK
const result = await forte.users.googleAuthLoginCallback({
  projectId,
  gCsrfToken: csrfToken,
  credential: response.credential,
  recaptchaToken,
});

Error Handling

If reCAPTCHA validation fails, Forte returns a 400 error with the error code RECAPTCHA_VALIDATION_FAILED. This can happen when:

  • The reCAPTCHA token is missing or invalid.
  • The request received a low risk score (indicating bot-like behavior).
  • The token has expired (tokens are valid for a short time after generation).

reCAPTCHA Actions

When executing reCAPTCHA on your frontend, use these action names to match what Forte expects:

EndpointAction
Google OAuth login/signuplogin or signup
User registrationregister
OTP login requestlogin

OTP Login (Email and SMS)

Forte supports passwordless sign-in via a 6-digit one-time passcode sent to the user's email or phone number. The same API powers both channels — the request body's email or phoneNumber field selects which one. Codes are time-limited (10 minutes), single-use, attempt-limited (5 tries), and hashed at rest.

The flow is SDK-driven so you can build whatever sign-in UI fits your app. The notification templates rendered to your users are customizable on a per-project basis — see Notification Templates in the Console.

Enable per channel

OTP login uses the same emailLoginEnabled and phoneLoginEnabled flags as registration. Enable the channel you want in your project settings before calling createOtpLogin.

How It Works

  1. Your app collects an email address or phone number.
  2. Your app calls createOtpLogin with that contact. Forte returns a pendingLoginId and the code's expirationTime.
  3. If a user with that verified contact method exists, Forte sends a 6-digit code to it. If not, Forte returns the same response shape but sends nothing — this prevents account-existence enumeration.
  4. The user enters the code in your UI; your app calls completeOtpLogin with the pendingLoginId and code. Forte verifies, marks the code consumed, and returns a session token. The session cookie (Forte-User-Session-Token) is also set on the response.
  5. If the user needs a new code, your app calls resendLoginOtp. Resends are throttled to once every 60 seconds and never extend the original 10-minute window.

Request OTP

typescript
import { ForteClient } from "@forteplatforms/sdk";
 
const forte = new ForteClient();
 
// Email channel
const emailRequest = await forte.users.createOtpLogin({
  projectId,
  createOtpLoginRequest: {
    email: "alice@example.com",
    recaptchaToken, // optional — required if reCAPTCHA is configured
  },
});
 
// SMS channel
const smsRequest = await forte.users.createOtpLogin({
  projectId,
  createOtpLoginRequest: {
    phoneNumber: "+15551234567",
    recaptchaToken,
  },
});
 
// Stash pendingLoginId — you'll need it to verify or resend.
const { pendingLoginId, expirationTime } = emailRequest;

Exactly one of email or phoneNumber must be set. Sending both, or neither, returns a 400 with NO_CONTACT_METHOD_PROVIDED.

Verify OTP

typescript
const result = await forte.users.completeOtpLogin({
  projectId,
  pendingLoginId,
  completeOtpLoginRequest: { code: "123456" },
});
 
// result.userObject — the authenticated user
// result.sessionToken.sessionToken — use for subsequent requests
// result.sessionToken.expirationTime — 365 days from now

A wrong code returns 400 INVALID_VERIFICATION_CODE and increments the pending login's attempt counter. After 5 failed attempts the pending login is locked — even the correct code will be rejected and the user must request a new one. Expired and already-consumed codes return the same error.

Resend

typescript
const resent = await forte.users.resendLoginOtp({
  projectId,
  pendingLoginId,
});
// Same response shape as createOtpLogin.

Resends within 60 seconds of the last send return 429 VERIFICATION_CODE_RATE_LIMITED. After the throttle window passes, a fresh 6-digit code is generated and sent; the 10-minute expiration window is not extended.

Example Sign-In Component (SMS)

Forte Platforms

By continuing, you agree to receive a one-time passcode via SMS.

SMS Disclaimer Required

Your sign-in UI must display the message "By continuing, you agree to receive a one-time passcode via SMS" before sending an OTP. This is a compliance requirement for SMS-based authentication.

Customizing the Message

The email subject, email HTML body, and SMS body are all editable per project under the Login OTP tab in your project's notification settings. Supported template variables in login-OTP templates: {{code}}, {{projectName}}, {{contactValue}}. ({{userFullName}} is intentionally unsupported here — at OTP send time, the user's identity has not yet been confirmed.)

Next Steps

Search

Search documentation and console pages