Authentication

Passwordless magic link authentication for your customers.

How It Works

StackBE provides passwordless authentication via magic links. Here's the flow:

  1. Your app calls POST /auth/magic-link with the user's email
  2. StackBE sends an email with a secure, time-limited link
  3. User clicks the link and is redirected to your callback URL with a token
  4. Your app verifies the token and receives a session
  5. Use the session token for authenticated API requests

Send Magic Link

Request a magic link for a customer. If the customer doesn't exist and autoCreateCustomer is enabled, they'll be created automatically.

typescript
// Using the SDK
await stackbe.auth.sendMagicLink('user@example.com', {
  redirectUrl: 'https://yourapp.com/dashboard', // Optional: where to go after auth
});

// Or via API
// POST /v1/apps/{appId}/auth/magic-link
// Body: { "email": "user@example.com" }

The magic link expires after 15 minutes by default (configurable in app settings).

Verify Token

When the user clicks the magic link, they're redirected to your callback URL with a token query parameter. Verify it to get a session:

typescript
// In your callback route (e.g., /auth/callback)
const token = searchParams.get('token');

const result = await stackbe.auth.verifyToken(token);
// Returns: { sessionToken, customer, entitlements }

// Store the session token (e.g., in a cookie)
cookies.set('session', result.sessionToken, {
  httpOnly: true,
  secure: true,
  sameSite: 'lax',
  maxAge: 60 * 60 * 24 * 7, // 7 days
});

// Redirect to dashboard
redirect('/dashboard');

Using Sessions

Once you have a session token, use it to make authenticated requests on behalf of the customer:

typescript
// Get current session info
const session = await stackbe.auth.getSession(sessionToken);
// Returns: { customer, entitlements, organizationId?, orgRole? }

// The session includes:
// - customer: { id, email, name, metadata }
// - entitlements: { planName, features: {...} }
// - organizationId: (if in org context)
// - orgRole: 'owner' | 'admin' | 'member' (if in org context)

Session Caching

The SDK can cache sessions to reduce API calls. Configure with sessionCacheTTL:

typescript
const stackbe = new StackBE({
  apiKey: process.env.STACKBE_API_KEY!,
  appId: process.env.STACKBE_APP_ID!,
  sessionCacheTTL: 120, // Cache for 2 minutes
});

Organization Context

For apps with organizations enabled, customers can belong to multiple orgs. After login, they can switch context:

typescript
// List customer's organizations
const orgs = await stackbe.organizations.list(customerId);

// Switch to an organization context
const newSession = await stackbe.auth.switchOrganization(
  sessionToken,
  organizationId
);

// The new session token includes org context:
// { organizationId: 'org_123', orgRole: 'admin' }

When in org context, entitlement checks use the organization's subscription, not the individual customer's.

Callback URLs

When a user clicks the magic link, StackBE redirects them to your callback URL. Configure allowed URL patterns in your dashboard, then pass the callback URL with each request.

Step 1: Configure Allowed URL Patterns

In your StackBE dashboard, configure which callback URLs are allowed:

  1. Go to app.stackbe.io
  2. Select your app → Settings → Dev
  3. Add patterns to "Allowed Callback URLs"
text
# Example patterns (one per line):
https://app.yoursite.com/*
https://*.vercel.app/*
http://localhost:*/*

Localhost URLs (http://localhost:*/*) are always allowed by default. Use * as a wildcard to match any characters.

Step 2: Pass Callback URL with Each Request

Pass the callback URL with every magic link request. This is the recommended approach:

typescript
// Determine callback URL based on environment
const callbackUrl = process.env.NODE_ENV === 'production'
  ? 'https://yourapp.com/auth/callback'
  : 'http://localhost:3000/auth/callback';

await stackbe.auth.sendMagicLink('user@example.com', {
  redirectUrl: callbackUrl,  // Must match an allowed pattern
});

Benefits of This Approach

  • Single source of truth - callback URL lives in your code
  • Preview deployments work - use wildcards like https://*.vercel.app/*
  • Local dev just works - localhost is always allowed
  • Security via allowlist - only approved URLs can be used

SDK Configuration (Optional)

The SDK also supports devCallbackUrl for automatic localhost detection:

typescript
const stackbe = new StackBE({
  apiKey: process.env.STACKBE_API_KEY!,
  appId: process.env.STACKBE_APP_ID!,
  devCallbackUrl: 'http://localhost:3000/auth/callback',
});

// Uses devCallbackUrl when NODE_ENV !== 'production'
await stackbe.auth.sendMagicLink('user@example.com');

Note: Explicit redirectUrl always takes priority over SDK defaults.

Custom Verification URL (Branded Emails)

By default, magic link emails contain a link to api.stackbe.io. To show your own domain in email links, configure a Custom Verification URL.

How It Works

  1. Set your Custom Verification URL in app settings (e.g., https://yourapp.com/auth/verify)
  2. Magic link emails now link to your domain: https://yourapp.com/auth/verify?token=xxx&appId=xxx
  3. Your endpoint redirects to StackBE to complete verification
  4. StackBE verifies the token and redirects to your callback URL

Implementation (One-Line Redirect)

Create a simple route that forwards to StackBE:

typescript
// Next.js App Router: app/auth/verify/route.ts
export async function GET(request: Request) {
  const url = new URL(request.url);
  const token = url.searchParams.get('token');
  const appId = url.searchParams.get('appId');

  // Redirect to StackBE for verification
  return Response.redirect(
    `https://api.stackbe.io/v1/apps/${appId}/auth/verify?token=${token}&redirect=true`
  );
}

Configuration

  1. Go to app.stackbe.io → Your App → Settings → Dev
  2. Set "Custom Verification URL" to your endpoint (e.g., https://yourapp.com/auth/verify)
  3. Deploy your redirect endpoint
  4. Test by requesting a magic link

Next Steps