nickmoline / stytch-laravel
Stytch integration for Laravel
Requires
- illuminate/support: >=12.0
- nickmoline/stytch-php: ^0.9.1
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.76
- orchestra/testbench: ^10.4
- pestphp/pest: ^3.8
- phpstan/phpstan: ^2.1
- phpstan/phpstan-phpunit: ^2.0
- phpunit/php-code-coverage: ^11.0
README
A Laravel package for integrating with Stytch authentication. This package provides seamless integration between Laravel and Stytch's authentication services, including automatic user creation and session management.
Installation
composer require nickmoline/stytch-laravel
Configuration
Publish the configuration file:
php artisan vendor:publish --provider="LaravelStytch\StytchServiceProvider" --tag="stytch-config"
Or publish all assets:
php artisan vendor:publish --provider="LaravelStytch\StytchServiceProvider"
This will create a config/stytch.php
file with the following configuration:
Note: You'll also need to configure the authentication guards in your config/auth.php
file. See the Authentication section below for details.
return [ 'project_id' => env('STYTCH_PROJECT_ID'), 'secret' => env('STYTCH_SECRET'), 'timeout' => $config['timeout'] ?? 600, 'custom_base_url' => env('STYTCH_CUSTOM_BASE_URL'), // Cookie configuration 'session_cookie_name' => env('STYTCH_SESSION_COOKIE_NAME', 'stytch_session'), 'jwt_cookie_name' => env('STYTCH_JWT_COOKIE_NAME', 'stytch_session_jwt'), // User model configuration 'user_model' => env('STYTCH_USER_MODEL', 'App\Models\User'), 'stytch_user_id_column' => env('STYTCH_USER_ID_COLUMN', 'stytch_user_id'), 'email_column' => env('STYTCH_EMAIL_COLUMN', 'email'), // Session configuration 'session_timeout' => env('STYTCH_SESSION_TIMEOUT', 3600), // 1 hour // Organization configuration 'organization' => [ 'enabled' => env('STYTCH_ORGANIZATION_ENABLED', true), ], ];
Add the following environment variables to your .env
file:
STYTCH_PROJECT_ID=your-project-id STYTCH_SECRET=your-secret STYTCH_TIMEOUT=600 STYTCH_CUSTOM_BASE_URL=https://test.stytch.com # Optional, for testing STYTCH_SESSION_COOKIE_NAME=stytch_session STYTCH_JWT_COOKIE_NAME=stytch_session_jwt STYTCH_USER_MODEL=App\Models\User STYTCH_USER_ID_COLUMN=stytch_user_id STYTCH_EMAIL_COLUMN=email STYTCH_SESSION_TIMEOUT=3600 STYTCH_ORGANIZATION_ENABLED=true
Database Migration
You'll need to add a stytch_user_id
column to your users table. The package provides a migration that you can publish:
php artisan vendor:publish --provider="LaravelStytch\StytchServiceProvider" --tag="stytch-migrations"
This will create a migration file that adds a stytch_user_id
column with the appropriate size for Stytch IDs (which follow the pattern [model]-[env]-[uuid]
, e.g., member-live-ebec2590-c493-49d0-9c7c-be4e692db24e
).
Alternatively, you can create the migration manually:
php artisan make:migration add_stytch_user_id_to_users_table
Add the following to your migration:
public function up(): void { Schema::table('users', function (Blueprint $table) { $table->string('stytch_user_id', 100)->nullable()->unique()->after('id'); }); } public function down(): void { Schema::table('users', function (Blueprint $table) { $table->dropColumn('stytch_user_id'); }); }
Note: The column size is set to 100 characters to accommodate Stytch's ID format which includes model type, environment, and UUID components.
User Model Setup
Add the HasStytchUser
trait to your User model:
<?php namespace App\Models; use Illuminate\Foundation\Auth\User as Authenticatable; use LaravelStytch\Traits\HasStytchUser; class User extends Authenticatable { use HasStytchUser; protected $fillable = [ 'name', 'email', 'stytch_user_id', ]; }
The trait provides modern Laravel 10+ attribute-based query scopes (stytchUserId
and stytchEmail
) for easy user lookup.
Organization Model Setup (Optional)
For B2B applications, you may want to track Stytch organizations. The package provides a trait for this, but you'll need to create your own migration based on your specific requirements.
Create Organization Model and Migration
php artisan make:model Organization -m
Create Your Organization Migration
Add the stytch_organization_id
column to your organization table migration:
public function up(): void { Schema::create('organizations', function (Blueprint $table) { $table->id(); $table->string('stytch_organization_id', 100)->nullable()->unique(); $table->string('name'); // Add your other organization fields here $table->timestamps(); }); }
Add the HasStytchOrganization Trait
<?php namespace App\Models; use Illuminate\Database\Eloquent\Model; use LaravelStytch\Traits\HasStytchOrganization; class Organization extends Model { use HasStytchOrganization; protected $fillable = [ 'name', 'stytch_organization_id', // Add your other fillable fields ]; }
The trait provides modern Laravel 10+ attribute-based query scopes (stytchOrganizationId
and stytchOrganizationName
) for easy organization lookup.
Usage
Using the Facade
You can use the Stytch facade to access Stytch's B2B and B2C clients:
use LaravelStytch\Facades\Stytch; // B2C operations $response = Stytch::b2c()->users()->get(['user_id' => 'user_123']); // B2B operations $response = Stytch::b2b()->organizations()->get(['organization_id' => 'org_123']); // Magic links $response = Stytch::b2c()->magicLinks()->create([ 'email' => 'user@example.com', 'login_magic_link_url' => 'https://yourapp.com/authenticate', ]); // OAuth $response = Stytch::b2c()->oauth()->authenticate([ 'token' => 'oauth_token_123', 'token_type' => 'oauth', ]);
Authentication
The package provides custom authentication guards that integrate seamlessly with Laravel's authentication system and allow for multiple authentication methods. The guards use Laravel's session system to cache Stytch authentication data, reducing API latency by avoiding repeated calls to Stytch on every request.
Session-Based Caching
The guards automatically create Laravel sessions after successful Stytch authentication, storing:
- User ID and Stytch user ID
- Authentication timestamp
- User details (email, name)
- B2B member and organization data (when applicable)
This data is cached in Laravel's session for the duration specified by STYTCH_SESSION_TIMEOUT
(default: 1 hour), eliminating the need to call Stytch's API on every request. When the session expires, the guard will re-authenticate with Stytch using the stored tokens.
Authentication Guards
The package registers custom authentication guards that you can use with Laravel's built-in auth
middleware:
Available Guards
stytch-b2c
- Authenticates B2C users with Stytch session or JWT tokensstytch-b2b
- Authenticates B2B users with Stytch session or JWT tokens (includes organization support)
Configuration
Add the guards to your config/auth.php
file:
'guards' => [ 'web' => [ 'driver' => 'session', 'provider' => 'users', ], 'api' => [ 'driver' => 'sanctum', 'provider' => 'users', ], // Stytch B2C Guard - for consumer applications 'stytch-b2c' => [ 'driver' => 'stytch-b2c', 'provider' => 'users', ], // Stytch B2B Guard - for business applications 'stytch-b2b' => [ 'driver' => 'stytch-b2b', 'provider' => 'users', ], ],
Usage with Laravel's Auth Middleware
// Protect routes with specific guards Route::middleware('auth:stytch-b2c')->group(function () { // B2C authenticated routes }); Route::middleware('auth:stytch-b2b')->group(function () { // B2B authenticated routes }); // Use multiple guards for endpoints that accept different authentication methods Route::middleware('auth:web,stytch-b2c,stytch-b2b')->group(function () { // Routes that work with any of these authentication methods }); // In controllers class DashboardController extends Controller { public function __construct() { $this->middleware('auth:web,stytch-b2c'); } }
Manual Authentication
You can also manually authenticate users:
use LaravelStytch\Facades\Stytch; use Illuminate\Support\Facades\Auth; // Authenticate a session token $response = Stytch::b2c()->sessions()->authenticate([ 'session_token' => $sessionToken ]); if (isset($response['user'])) { $user = User::stytchUserId($response['user']['user_id'])->firstOrCreate([ 'name' => $response['user']['name'] ?? '', 'email' => $response['user']['emails'][0]['email'] ?? '', ]); Auth::login($user); } // Authenticate a JWT $response = Stytch::b2c()->sessions()->authenticateJwt([ 'session_jwt' => $jwtToken ]); if (isset($response['user'])) { $user = User::stytchUserId($response['user']['user_id'])->firstOrCreate([ 'name' => $response['user']['name'] ?? '', 'email' => $response['user']['emails'][0]['email'] ?? '', ]); Auth::login($user); }
Using Query Scopes
The trait provides modern Laravel 10+ attribute-based query scopes for finding users:
// Find user by Stytch user ID $user = User::stytchUserId('stytch_user_123')->first(); // Find user by Stytch email $user = User::stytchEmail('user@example.com')->first(); // Chain with other Eloquent methods $user = User::stytchUserId('stytch_user_123')->firstOrCreate([ 'name' => 'John Doe', 'email' => 'john@example.com' ]); // Use in complex queries $activeUsers = User::stytchEmail('admin@example.com') ->where('active', true) ->orderBy('created_at') ->get(); // Organization queries (if using B2B) $organization = Organization::stytchOrganizationId('org-live-123456')->first(); $organizations = Organization::stytchOrganizationName('Acme Corp')->get(); ### Accessing Session Data You can access the cached Stytch session data programmatically: ```php use Illuminate\Support\Facades\Auth; // Get the current guard $guard = Auth::guard('stytch-b2c'); // Access session data $sessionData = $guard->getStytchSessionData(); if ($sessionData) { $stytchUserId = $sessionData['stytch_user_id']; $memberId = $sessionData['member_id'] ?? null; $organizationId = $sessionData['organization_id'] ?? null; $organizationName = $sessionData['organization_name'] ?? null; } // Clear the session (useful for logout) $guard->clearStytchSession();
Session Management
The package automatically manages Laravel sessions for Stytch authentication:
- Session Creation: After successful Stytch authentication, a Laravel session is created with all relevant user and organization data
- Session Validation: On subsequent requests, the guard checks if the session is valid and not expired
- Session Expiry: Sessions expire after the configured timeout (default: 1 hour)
- Session Regeneration: Session IDs are regenerated after authentication for security
- Automatic Re-authentication: When sessions expire, the guard automatically re-authenticates with Stytch using stored tokens
This approach significantly reduces API latency while maintaining security and providing a seamless user experience.
Features
- Custom Authentication Guards: Seamless integration with Laravel's authentication system using custom guards
- Multiple Guard Support: Use Laravel's native multiple guard support for endpoints that accept different authentication methods
- Session-Based Caching: Automatic Laravel session creation to cache Stytch authentication data and reduce API latency
- Automatic User Creation: Users are automatically created in your Laravel application when they first authenticate with Stytch
- Session Management: Full integration with Laravel's session and authentication system with automatic session expiry and regeneration
- Flexible Configuration: Configurable cookie names, user model, column names, and session timeout
- B2B and B2C Support: Full access to both Stytch B2B and B2C APIs
- Trait-based User Model: Easy integration with existing user models
- Optional Organization Support: Trait for B2B applications that need to track Stytch organizations
- Performance Optimized: Reduces Stytch API calls by caching authentication data in Laravel sessions
Testing
composer test
License
The MIT License (MIT). Please see License File for more information.