Skip to main content
Definable uses Stytch as its authentication provider, providing secure user authentication, session management, and invitation flows. This page explains how Stytch is integrated into the backend and how it works with the Definable authentication system.

Overview

Stytch provides the following capabilities in Definable:
  • User Authentication: Password-based and magic link authentication
  • Session Management: JWT-based session tokens validated via JWKS
  • User Invitations: Email-based invitation system for organization onboarding
  • Webhook Events: Real-time user lifecycle event processing
  • External User Tracking: Links Stytch user IDs to internal user records

Architecture

JWKS-Based JWT Validation

Definable uses Stytch’s JWKS (JSON Web Key Set) endpoint to validate session tokens locally without making API calls to Stytch for every request.

How JWKS Validation Works

  1. Client receives JWT from Stytch after successful login
  2. Client sends JWT in Authorization: Bearer <token> header
  3. JWTBearer middleware extracts the token
  4. JWKS client fetches public keys from Stytch’s JWKS endpoint (cached)
  5. Token is cryptographically verified using the public key
  6. User lookup using the sub claim (stytch_id) from decoded token
  7. User context returned to the request handler

Implementation

The JWKS verification is implemented in src/libs/stytch/v1/jkws.py:

JWTBearer Dependency

The JWTBearer class in src/dependencies/security.py uses Stytch JWKS validation:

Key Features

  • RS256 Algorithm: Stytch uses asymmetric encryption (public/private key pairs)
  • Cached Keys: Public keys are cached for 10 minutes to reduce latency
  • Audience Validation: Ensures token was issued for your Stytch project
  • Claims Verification: Validates expiration, issued-at, audience, subject, and issuer
  • WebSocket Support: Same validation works for WebSocket connections using query parameters

Webhook Integration with Svix

Stytch sends webhook events to Definable when user lifecycle events occur (user creation, deletion, etc.). These webhooks are secured using Svix signature verification.

Webhook Flow

  1. User event occurs in Stytch (e.g., user signs up)
  2. Stytch sends webhook to Definable’s /api/auth endpoint
  3. Svix headers included: svix-id, svix-timestamp, svix-signature
  4. Signature verified using webhook secret
  5. Event processed based on action type

Svix Signature Verification

Implemented in src/utils/verify_wh.py:

Webhook Handler

The webhook handler in src/services/auth/service.py processes user events:

User Registration Flows

Definable supports two user registration flows via Stytch:

1. Regular User Registration

Flow:
  1. User signs up via Stytch (password or magic link)
  2. Stytch webhook triggers with action: "CREATE"
  3. UserModel created with stytch_id from webhook
  4. Default organization created and user assigned “owner” role
  5. Default auth token generated (365-day JWT)
  6. Starter subscription with initial credits created
Implementation:

2. Invitation-Based Registration

Flow:
  1. Admin invites user via email
  2. Pre-created UserModel with stytch_id=None, status=“invited”
  3. Invitation email sent via Stytch with trusted_metadata.external_user_id
  4. User clicks invitation link and signs up via Stytch
  5. Stytch webhook triggers with type: "invitation" in untrusted_metadata
  6. UserModel updated with stytch_id from Stytch
  7. OrganizationMember status changed from “invited” to “active”
  8. Invitation status updated to “ACCEPTED”
Implementation:

Sending Invitations

When an admin invites a user, Stytch is used to send the invitation email:

Password Authentication

Definable supports password-based authentication via Stytch:

Sign Up with Password

Login with Password

UserModel and stytch_id

The UserModel stores the Stytch user ID to link internal users with Stytch:
Key Points:
  • stytch_id is nullable to support invited users who haven’t signed up yet
  • stytch_id is unique and indexed for fast lookups during authentication
  • password field is deprecated - passwords are managed by Stytch
  • When invitation is accepted, stytch_id is populated from webhook

Configuration

Stytch integration requires the following environment variables:

Environment Settings

Defined in config/settings.py:

JWKS Endpoints

  • Test Environment: https://test.stytch.com/v1/sessions/jwks/{project_id}
  • Live Environment: https://api.stytch.com/v1/sessions/jwks/{project_id}

Security Considerations

Token Validation

  • Algorithm: RS256 (asymmetric cryptography)
  • Signature Verification: Tokens verified using Stytch’s public keys
  • Claim Validation: exp, iat, aud, sub, iss claims are required
  • Expiration: Tokens expire based on Stytch session duration (default: 1440 minutes / 24 hours)

Webhook Security

  • Svix Signatures: All webhooks must have valid Svix signatures
  • HMAC-SHA256: Signatures use HMAC with SHA256 hashing
  • Timestamp Verification: Prevents replay attacks
  • Secret Management: Webhook secret stored securely in environment variables

Metadata Security

  • Trusted Metadata: Stored securely by Stytch, not editable by users
    • Used for: external_user_id, is_invited flag
  • Untrusted Metadata: Can be set by clients
    • Used for: type: "invitation", temp: true for test users
    • Never trust for security decisions

Testing

Test Endpoints

Definable provides test endpoints for local development:
  • POST /api/auth/test_signup - Create user with password
  • POST /api/auth/test_login - Authenticate with password
  • POST /api/auth/verify_api_key - Verify API key validity
Note: Test signups include untrusted_metadata: {"temp": true} to prevent webhook processing during development.

Example Test Flow

Implementation Files

Core Files

  • src/dependencies/security.py: JWTBearer class with JWKS validation
  • src/libs/stytch/v1/jkws.py: JWKS client and token verification
  • src/libs/stytch/v1/base.py: Stytch API wrapper
  • src/services/auth/service.py: Webhook handler and user creation
  • src/utils/verify_wh.py: Svix signature verification
  • src/models/auth_model.py: UserModel with stytch_id field

Configuration Files

  • config/settings.py: Stytch environment variables
  • .env: Stytch credentials and configuration

Troubleshooting

Invalid Token Errors

Symptoms:
  • Error: “Invalid token signature”
  • Error: “Token has expired”
Solutions:
  1. Verify token is from correct Stytch environment (test vs live)
  2. Check JWKS endpoint is accessible
  3. Ensure STYTCH_PROJECT_ID matches the token’s audience claim
  4. Verify token hasn’t expired (check exp claim)

Webhook Signature Failures

Symptoms:
  • Error: “Invalid signature” (400 status)
  • Webhooks not processing
Solutions:
  1. Verify STYTCH_WEBHOOK_SECRET is correct
  2. Check webhook secret format (should start with whsec_)
  3. Ensure webhook body is not modified before verification
  4. Verify headers svix-id, svix-timestamp, svix-signature are present

User Not Found After Login

Symptoms:
  • Login succeeds in Stytch
  • Error: “User not found” in Definable
Solutions:
  1. Check if webhook was processed successfully
  2. Verify stytch_id matches between Stytch and UserModel
  3. Check if user was created with temp: true flag (skips webhook processing)
  4. Manually create user if webhook failed

Next Steps