Skip to main content
The Better Auth Invite Plugin supports three token generation strategies: default tokens, short codes, and custom generators. Each serves different use cases and security requirements.

Token types overview

The plugin defines three token types:
type TokensType = "token" | "code" | "custom";
Source: src/types.ts:341

Default token generation

The default token type generates a secure 24-character random string.

Using default tokens

const result = await client.invite.create({
  email: "[email protected]",
  role: "member",
  tokenType: "token",  // or omit for default
});

// Generates: "5f9a8b7c6d5e4f3a2b1c0d9e"

Token generation implementation

import { generateId } from "better-auth";

const tokenGenerators: Record<TokensType, () => string> = {
  token: () => generateId(24),  // 24 random characters
  // ...
};
Source: src/utils.ts:76-79

Default token characteristics

Length

24 characters

Character set

Alphanumeric (a-z, A-Z, 0-9)

Security

Cryptographically secure random generation

Use case

URL-safe invitation links

Setting as default

import { invite } from "better-auth-invite";

export const auth = betterAuth({
  plugins: [
    invite({
      defaultTokenType: "token",  // Default value
    }),
  ],
});
Source: src/types.ts:96

Code generation

Code type generates a short, user-friendly 6-character code ideal for manual entry.

Using code tokens

const result = await client.invite.create({
  email: "[email protected]",
  role: "member",
  tokenType: "code",
});

// Generates: "A1B2C3"

Code generation implementation

import { generateRandomString } from "better-auth/crypto";

const tokenGenerators: Record<TokensType, () => string> = {
  code: () => generateRandomString(6, "0-9", "A-Z"),
  // ...
};
Source: src/utils.ts:77

Code characteristics

Length

6 characters

Character set

Uppercase letters and numbers (A-Z, 0-9)

Readability

Easy to read and communicate verbally

Use case

Manual entry in forms or mobile apps

Code token use cases

// Generate code for mobile app
const { message } = await client.invite.create({
  role: "user",
  tokenType: "code",
  senderResponse: "token",
});

// Display to user: "Enter code: A1B2C3"
// Agent creates code for customer
const { message } = await client.invite.create({
  email: "[email protected]",
  role: "customer",
  tokenType: "code",
});

// Agent reads code over phone: "A1B2C3"
// Generate codes for event badges
const attendeeCodes = await Promise.all(
  attendees.map(async (attendee) => {
    const { message } = await client.invite.create({
      email: attendee.email,
      role: "attendee",
      tokenType: "code",
    });
    return { email: attendee.email, code: message };
  })
);
// Generate verification code
const { message } = await client.invite.create({
  role: "verified-user",
  tokenType: "code",
  expiresIn: 300,  // 5 minutes
  maxUses: 1,
});

// Send via SMS or display in app

Custom token generation

For advanced use cases, you can provide your own token generation logic.

Implementing custom tokens

import { invite } from "better-auth-invite";
import { randomBytes } from "crypto";

export const auth = betterAuth({
  plugins: [
    invite({
      defaultTokenType: "custom",
      generateToken: () => {
        // Custom logic here
        return randomBytes(32).toString("hex");
      },
    }),
  ],
});
Source: src/types.ts:86-88

Custom token resolution

The plugin resolves custom tokens with a secure fallback:
export const resolveTokenGenerator = (
  tokenType: TokensType,
  options: NewInviteOptions,
): (() => string) => {
  if (tokenType === "custom" && options.generateToken) {
    return options.generateToken;
  }

  const tokenGenerators: Record<TokensType, () => string> = {
    code: () => generateRandomString(6, "0-9", "A-Z"),
    token: () => generateId(24),
    custom: () => generateId(24), // Secure fallback
  };

  return tokenGenerators[tokenType];
};
Source: src/utils.ts:68-83
If you specify tokenType: "custom" without providing generateToken, the plugin falls back to the default 24-character token generator for security.

Custom token examples

import { randomUUID } from "crypto";

invite({
  generateToken: () => randomUUID(),
});

// Generates: "123e4567-e89b-12d3-a456-426614174000"
import { generateId } from "better-auth";

invite({
  generateToken: () => `inv_${generateId(20)}`,
});

// Generates: "inv_a1b2c3d4e5f6g7h8i9j0"
import { generateId } from "better-auth";

invite({
  generateToken: () => {
    const timestamp = Date.now().toString(36);
    const random = generateId(16);
    return `${timestamp}-${random}`;
  },
});

// Generates: "lx4y2z-a1b2c3d4e5f6g7h8"
import { createHash } from "crypto";
import { generateId } from "better-auth";

invite({
  generateToken: () => {
    const payload = generateId(20);
    const checksum = createHash("sha256")
      .update(payload)
      .digest("hex")
      .substring(0, 4);
    return `${payload}-${checksum}`;
  },
});

// Generates: "a1b2c3d4e5f6g7h8i9j0-7f3a"

Per-invite token type

You can override the default token type for individual invitations:
// Default is "token"
invite({
  defaultTokenType: "token",
});

// Override for specific invitations
await client.invite.create({
  role: "beta-user",
  tokenType: "code",  // Use code for this invite
});

await client.invite.create({
  role: "admin",
  tokenType: "custom",  // Use custom for this invite
});
Source: src/body.ts:21-26

Security considerations

Token entropy

Different token types have different security characteristics:
Token TypeLengthCharacter SetEntropyBrute Force Resistance
token2462 chars (a-z, A-Z, 0-9)~143 bitsExcellent
code636 chars (A-Z, 0-9)~31 bitsModerate
customVariableVariableVariableDepends on implementation
Code tokens are less secure due to their shorter length. Always use additional security measures:
  • Set short expiration times
  • Limit maxUses to 1
  • Implement rate limiting
  • Use only for low-risk operations
invite({
  defaultTokenType: "token",
  invitationTokenExpiresIn: 24 * 60 * 60,  // 24 hours
});
Default tokens are cryptographically secure and suitable for sensitive operations like admin invitations.

For codes (manual entry)

await client.invite.create({
  role: "user",
  tokenType: "code",
  expiresIn: 600,        // 10 minutes (short expiry)
  maxUses: 1,            // Single use only
});
Always limit code validity period due to lower entropy.

For custom tokens

import { randomBytes } from "crypto";

invite({
  generateToken: () => {
    // Ensure minimum 128 bits of entropy
    return randomBytes(16).toString("base64url");
  },
});
Use cryptographically secure random number generators from crypto module.

Token storage

All tokens are stored in the invite table with a unique constraint:
{
  invite: {
    fields: {
      token: { 
        type: "string", 
        unique: true  // Enforces uniqueness
      },
      // ...
    }
  }
}
Source: src/schema.ts:6
The unique constraint ensures no two invitations can have the same token, preventing conflicts regardless of token type.

Token in URLs

Tokens are used in invitation URLs:
const url = `${baseURL}/invite/${token}?callbackURL=${callbackURL}`;
All three token types are URL-safe:
  • token: a-zA-Z0-9 characters
  • code: A-Z0-9 characters
  • custom: Should generate URL-safe strings
Source: src/utils.ts:309
If your custom generator includes special characters, use encodeURIComponent() or generate URL-safe tokens using base64url encoding.

Token validation

All token types go through the same validation:
// 1. Token exists
const invitation = await adapter.findInvitation(token);
if (!invitation) throw error("INVALID_TOKEN");

// 2. Not expired
if (getDate() > invitation.expiresAt) throw error("INVALID_TOKEN");

// 3. Usage limit not exceeded
const timesUsed = await adapter.countInvitationUses(invitation.id);
if (timesUsed >= invitation.maxUses) throw error("NO_USES_LEFT");

// 4. Status is pending
if (invitation.status !== "pending") throw error("INVALID_TOKEN");
Source: src/routes/activate-invite-logic.ts:29-47 Validation is identical regardless of token type, ensuring consistent security.

Build docs developers (and LLMs) love