Skip to main content
Technology & EngineeringAuth Services214 lines

Stytch

Build with Stytch for passwordless and modern authentication. Use this skill when

Quick Summary34 lines
You are an auth specialist who integrates Stytch into projects. Stytch is an
authentication platform focused on passwordless methods — magic links, OTPs,
passkeys, and biometrics — with strong B2B multi-tenancy support.

## Key Points

- Use magic links or OTPs as the primary auth method — better UX than passwords
- Store Stytch session tokens in httpOnly cookies — never in localStorage
- Validate sessions on every request — sessions can be revoked server-side
- Use B2B SDK for multi-tenant apps — organizations, roles, and SSO built in
- Set appropriate session durations — 7 days for consumer, shorter for enterprise
- Use Stytch's user metadata to store app-specific data alongside auth
- Storing session tokens in localStorage — XSS vulnerability
- Not validating sessions server-side — expired or revoked sessions must be caught
- Building organization management yourself when Stytch B2B handles it
- Using long session durations for sensitive apps — use shorter sessions with refresh
- Not handling magic link expiry — links expire after the configured time
- Sending OTPs without rate limiting — prevents abuse

## Quick Example

```bash
npm install stytch
```

```typescript
// Revoke current session
await client.sessions.revoke({ session_token: sessionToken });

// Revoke all sessions for a user
await client.sessions.revokeAll({ user_id: userId });
```
skilldb get auth-services-skills/StytchFull skill: 214 lines
Paste into your CLAUDE.md or agent config

Stytch Authentication Integration

You are an auth specialist who integrates Stytch into projects. Stytch is an authentication platform focused on passwordless methods — magic links, OTPs, passkeys, and biometrics — with strong B2B multi-tenancy support.

Core Philosophy

Passwordless first

Stytch was built for a post-password world. Magic links, email OTPs, SMS OTPs, passkeys, and biometric auth are first-class. Password auth exists but isn't the default. Passwordless reduces friction and eliminates credential stuffing.

B2B and B2C in one platform

Stytch offers separate B2C and B2B SDKs. B2B includes organization management, SSO/SAML, SCIM provisioning, and role-based access — the enterprise auth features startups usually build months later.

API-first

Stytch's API is clean and RESTful. The SDKs are thin wrappers. You can integrate Stytch purely via API calls without a frontend SDK if you prefer full control.

Setup

Install

npm install stytch

Initialize

import * as stytch from 'stytch';

const client = new stytch.Client({
  project_id: process.env.STYTCH_PROJECT_ID!,
  secret: process.env.STYTCH_SECRET!,
});

Key Techniques

Magic link authentication

// Send magic link
export async function POST(req: Request) {
  const { email } = await req.json();

  const response = await client.magicLinks.email.loginOrCreate({
    email,
    login_magic_link_url: `${process.env.NEXT_PUBLIC_URL}/api/auth/callback`,
    signup_magic_link_url: `${process.env.NEXT_PUBLIC_URL}/api/auth/callback`,
  });

  return Response.json({ sent: true });
}

// Handle callback
export async function GET(req: Request) {
  const url = new URL(req.url);
  const token = url.searchParams.get('token');

  if (!token) return new Response('Missing token', { status: 400 });

  const response = await client.magicLinks.authenticate({
    token,
    session_duration_minutes: 60 * 24 * 7, // 7 days
  });

  // Set session token as cookie
  const headers = new Headers();
  headers.set('Set-Cookie', `stytch_session=${response.session_token}; Path=/; HttpOnly; Secure; SameSite=Lax; Max-Age=${60 * 60 * 24 * 7}`);
  headers.set('Location', '/dashboard');

  return new Response(null, { status: 302, headers });
}

Email OTP

// Send OTP
const response = await client.otps.email.loginOrCreate({
  email: 'user@example.com',
  expiration_minutes: 10,
});
const methodId = response.email_id;

// Verify OTP
const authResponse = await client.otps.authenticate({
  method_id: methodId,
  code: userEnteredCode,
  session_duration_minutes: 60 * 24 * 7,
});

OAuth (Google, GitHub, etc.)

// Start OAuth flow
export async function GET() {
  const url = client.oauth.google.getAuthorizationURL({
    login_redirect_url: `${process.env.NEXT_PUBLIC_URL}/api/auth/oauth/callback`,
    signup_redirect_url: `${process.env.NEXT_PUBLIC_URL}/api/auth/oauth/callback`,
  });

  return Response.redirect(url);
}

// Handle callback
export async function GET(req: Request) {
  const url = new URL(req.url);
  const token = url.searchParams.get('token')!;

  const response = await client.oauth.authenticate({
    token,
    session_duration_minutes: 60 * 24 * 7,
  });

  // response.user contains user info
  // response.session_token is the session
  // Set cookie and redirect
}

Session validation

import { cookies } from 'next/headers';

export async function validateSession() {
  const sessionToken = (await cookies()).get('stytch_session')?.value;
  if (!sessionToken) return null;

  try {
    const response = await client.sessions.authenticate({
      session_token: sessionToken,
    });
    return response.user;
  } catch {
    return null;
  }
}

// Use in pages
export default async function DashboardPage() {
  const user = await validateSession();
  if (!user) redirect('/login');

  return <div>Welcome {user.emails[0]?.email}</div>;
}

Session revocation

// Revoke current session
await client.sessions.revoke({ session_token: sessionToken });

// Revoke all sessions for a user
await client.sessions.revokeAll({ user_id: userId });

B2B organizations

import * as stytch from 'stytch';

const b2bClient = new stytch.B2BClient({
  project_id: process.env.STYTCH_PROJECT_ID!,
  secret: process.env.STYTCH_SECRET!,
});

// Create organization
const org = await b2bClient.organizations.create({
  organization_name: 'Acme Corp',
  organization_slug: 'acme',
  email_allowed_domains: ['acme.com'],
});

// Invite member
await b2bClient.magicLinks.email.invite({
  organization_id: org.organization.organization_id,
  email_address: 'employee@acme.com',
});

// SSO/SAML for enterprise orgs
await b2bClient.sso.saml.createConnection({
  organization_id: orgId,
  display_name: 'Acme SSO',
});

Best Practices

  • Use magic links or OTPs as the primary auth method — better UX than passwords
  • Store Stytch session tokens in httpOnly cookies — never in localStorage
  • Validate sessions on every request — sessions can be revoked server-side
  • Use B2B SDK for multi-tenant apps — organizations, roles, and SSO built in
  • Set appropriate session durations — 7 days for consumer, shorter for enterprise
  • Use Stytch's user metadata to store app-specific data alongside auth

Anti-Patterns

  • Storing session tokens in localStorage — XSS vulnerability
  • Not validating sessions server-side — expired or revoked sessions must be caught
  • Building organization management yourself when Stytch B2B handles it
  • Using long session durations for sensitive apps — use shorter sessions with refresh
  • Not handling magic link expiry — links expire after the configured time
  • Sending OTPs without rate limiting — prevents abuse

Install this skill directly: skilldb add auth-services-skills

Get CLI access →