DEV Community

Cover image for NestJS Authentication with Stytch: Complete Starter Guide
Michał Miler for u11d

Posted on • Originally published at u11d.com

NestJS Authentication with Stytch: Complete Starter Guide

Implementing authentication in NestJS can be challenging, especially when ensuring security and scalability. Instead of building authentication from scratch, you can integrate Stytch, a modern authentication platform, to handle secure user management with ease. In this guide, we’ll walk through the stytch-nestjs-starter repository - a production-ready example that shows how to integrate Stytch authentication with NestJS using smart caching and session management.

Why Choose Stytch Over Building Your Own Auth?

Building authentication from scratch means dealing with:

  • Password hashing and salt management
  • Session security and token management
  • Account verification and password reset flows
  • Rate limiting and brute force protection
  • Compliance with security standards
  • Regular security updates and patches

Stytch eliminates these concerns by providing:

  • ✅ Battle-tested security with enterprise-grade protection
  • ✅ Comprehensive authentication methods (email/password, magic links, social login, MFA)
  • ✅ Global infrastructure with 99.99% uptime SLA
  • ✅ Developer-friendly APIs with excellent documentation
  • ✅ Compliance certifications (SOC 2, ISO 27001, GDPR)

Understanding the Integration Approaches

Frontend or Backend Integration

Stytch offers two primary integration patterns, each suited for different application architectures:

Frontend Integration: Ideal for client-side applications where authentication logic runs in the browser. This approach uses Stytch's JavaScript SDK to handle authentication flows directly on the frontend, with the backend serving as a resource server that validates session tokens.

Backend Integration: Perfect for server-side rendered applications or when you need centralized authentication control. This is the approach demonstrated in our starter, where the NestJS backend handles all authentication logic using Stytch's server-side APIs, providing better security and session management control.

Our starter implements the backend integration pattern, giving you:

  • Enhanced Security: Sensitive operations happen server-side
  • Centralized Session Management: All authentication logic in one place
  • Better Performance: Server-side caching reduces API calls
  • Simplified Frontend: Frontend only needs to handle session tokens

Learn more about integration patterns: Stytch Architecture Guide

Consumer vs B2B

Stytch supports both Consumer (B2C) and Business (B2B) authentication models:

Consumer Authentication (B2C): Designed for applications serving individual users. Features include:

  • Individual user accounts with email/password authentication
  • Social login providers (Google, Apple, Facebook, etc.)
  • Magic links and SMS-based authentication
  • Self-service password reset and account management

Business Authentication (B2B): Built for applications serving organizations and teams. Includes:

  • Organization-based user management
  • Single Sign-On (SSO) with SAML and OIDC
  • Just-in-Time (JIT) provisioning
  • Advanced admin controls and member management
  • Multi-tenant architecture support

Our starter is configured for Consumer Authentication, making it ideal for SaaS applications, marketplaces, and consumer-facing platforms. However, the architecture can be easily adapted for B2B use cases by switching to Stytch's B2B APIs.

Learn more about the differences: B2B vs Consumer Auth

Architecture Deep Dive

System Architecture Overview

System Architecture Overview

The Complete Authentication Flow

Let's visualize how authentication works in our starter:

The Complete Authentication Flow

Exploring the Starter Repository

Key Features Implemented

1. Smart Session Caching

One of the most critical optimizations in the starter is the Redis-based session caching:

// Storing sessions in Redis with TTL
private async storeSession(
  sessionToken: string,
  { session_id, user_id: stytchUserId, expires_at }: Session,
): Promise<SessionTokenModel> {
  const user = await this.userService.fetchByStytchId(stytchUserId);

  const value: CachedSession = {
    userId: user.id,
    stytchUserId,
  };

  const ttl = expires_at
    ? new Date(expires_at).getTime() - new Date().getTime()
    : this.stytchService.sessionDurationMinutes * 60 * 1000;

  const hashedSessionToken = this.hashSessionToken(sessionToken);
  await this.cacheManager.set(hashedSessionToken, value, ttl);

  return { sessionToken };
}

Enter fullscreen mode Exit fullscreen mode

This approach means:

  • No Stytch API calls on every request
  • Sub-millisecond session verification
  • Automatic session expiration matching Stytch TTL
  • Reduced API costs and improved performance

2. Intelligent Session Refresh

The session refresh interceptor automatically extends sessions before they expire:

private async checkAndRefreshSession(
  request: RequestWithUser,
  response: Response,
): Promise<void> {
  const sessionToken = request.headers['authorization']?.split(' ')[1];

  const hashedSessionToken = this.authService.hashSessionToken(sessionToken);
  const sessionTtl = await this.cacheManager.ttl(hashedSessionToken);
  const expirationTime = new Date(sessionTtl);

  const shouldRefresh =
    expirationTime.getTime() - new Date().getTime() <=
    this.sessionRefreshThresholdMinutes * 60 * 1000;

  if (shouldRefresh) {
    const refreshResult = await this.authService.refreshSession({
      sessionToken,
    });

    response.setHeader('X-New-Session-Token', refreshResult.sessionToken);
  }
}

Enter fullscreen mode Exit fullscreen mode

This ensures users never experience unexpected logouts while maintaining security.

3. Elegant Session Access with CurrentSession Decorator

The CurrentSession decorator provides a clean, type-safe way to access authenticated user data in your controllers:

@Controller('resources')
export class ResourceController {
  @Get()
  findAll(@CurrentSession() session: CachedSession): ResourceModel[] {
        this.logger.debug(`User ${session.userId} requested all resources`);
    return this.resourceService.findAll();
  }
}

Enter fullscreen mode Exit fullscreen mode

How it works:

  1. AuthGuard Integration: The guard validates the session token and attaches user data to the request
  2. Type Safety: Returns strongly-typed CachedSession object with userId and stytchUserId
  3. Automatic Extraction: Seamlessly extracts session data without manual request parsing
  4. Null Safety: Returns undefined for unauthenticated requests, handled gracefully by the guard
export const CurrentSession = createParamDecorator(
  (data: unknown, ctx: ExecutionContext): CachedSession | undefined => {
    const request = ctx.switchToHttp().getRequest<RequestWithUser>();
    return request.user;
  },
);

Enter fullscreen mode Exit fullscreen mode

This pattern eliminates boilerplate code and provides a consistent way to access user context across your application.

4. Flexible User Creation Methods

The starter supports two user creation approaches, though in most cases you'll only need one method depending on your application type.

Method A: Self Sign-up (Public Registration)

curl -X POST http://localhost:3000/auth/sign-up \
  -H "Content-Type: application/json" \
  -d '{
    "email": "user@example.com",
    "password": "JHFKhpe_t1VfBULE_IsMhWC5WN4yx0ke",
    "firstName": "John",
    "lastName": "Doe"
  }'

Enter fullscreen mode Exit fullscreen mode

Method B: Admin Invitation (Dashboard Apps)

# Using the create-user script
yarn create-user admin@company.com

# Or via API with master key
curl -X POST http://localhost:3000/auth/invite \
  -H "X-Api-Key: your-master-key" \
  -d '{
    "email": "newuser@company.com",
    "firstName": "Jane",
    "lastName": "Smith"
  }'

Enter fullscreen mode Exit fullscreen mode

The invitation method sends a magic link email, allowing users to set their password securely.

After executing the create-user script or calling the endpoint manually, you'll receive an email message with a magic link that should redirect you to your frontend application to complete the password setup. However, if your frontend is not built yet, you can use server endpoint directly to reset your password until the frontend is ready:

curl -X POST http://localhost:3000/auth/password \
  -d '{
    "sessionToken": "<token_from_magin_link>",
    "password": "JHFKhpe_t1VfBULE_IsMhWC5WN4yx0ke"
  }'
Enter fullscreen mode Exit fullscreen mode

Learn more about password reset flows: Stytch Password Reset API

Getting Started with the Starter

1. Quick Setup

Clone and configure the repository:

git clone https://github.com/u11d-com/stytch-nestjs-starter.git
cd stytch-nestjs-starter
yarn install
cp .env.example .env
Enter fullscreen mode Exit fullscreen mode

2. Stytch Configuration

  1. Sign up at Stytch Dashboard
  2. Create a new project
  3. Copy your Project ID and Secret to .env:
STYTCH_PROJECT_ID=project-test-xxx
STYTCH_SECRET=secret-test-xxx
STYTCH_SESSION_DURATION_MINUTES=60
STYTCH_SESSION_REFRESH_THRESHOLD_MINUTES=30
Enter fullscreen mode Exit fullscreen mode

3. Infrastructure Setup

Start the required services:

# Start PostgreSQL and Redis
docker compose up postgres redis -d

# Run database migrations
yarn migration:run

# Start the development server
yarn start:dev
Enter fullscreen mode Exit fullscreen mode

4. Testing Authentication

Your API will be available at http://localhost:3000.

Create your first admin user (execute script and set password using magic link sent to email address):

yarn create-user admin@yourcompany.com
Enter fullscreen mode Exit fullscreen mode

Then test the authentication flow:

# Login
curl -X POST http://localhost:3000/auth/login \
  -H "Content-Type: application/json" \
  -d '{"email": "admin@yourcompany.com", "password": "YourPassword123!"}'

# Access protected resource
curl -X GET http://localhost:3000/resources \
  -H "Authorization: Bearer YOUR_SESSION_TOKEN"

Enter fullscreen mode Exit fullscreen mode

Production Considerations

Security Best Practices

  1. Environment Variables: Never commit real credentials to version control
  2. Master Key: Generate a cryptographically secure master key
  3. CORS Configuration: Restrict origins to your frontend domains
  4. HTTPS: Always use HTTPS in production
  5. Rate Limiting: Implement rate limiting for auth endpoints
  6. Session Duration Strategy: Balance longer sessions for user experience with shorter sessions for stronger security

Performance and Scalability

  1. Redis Configuration: Use Redis Cluster for high availability and scalability
  2. Connection Pooling: Optimize database connection pools for stable performance under load
  3. Extended Caching: Introduce additional caching layers (e.g., user profile caching) to reduce database load

Extending the Starter

The starter provides a solid foundation that can be extended with additional features:

  • Role-Based Access Control (RBAC): Add user roles and permissions
  • Multi-Factor Authentication (MFA): Implement TOTP, SMS, or other MFA methods
  • Social Authentication: Google, GitHub, and other OAuth providers

We can prepare detailed implementations for these extensions if the community is interested. Let us know which features would be most valuable for your projects!

Conclusion

Building secure, scalable authentication doesn't have to be complex. The stytch-nestjs-starter provides a production-ready foundation that combines Stytch's security expertise with NestJS's architectural excellence.

By leveraging this starter, you get:

✅ Enterprise-grade security without the complexity

✅ Optimized performance with intelligent caching

✅ Developer productivity with comprehensive tooling

✅ Production readiness with Docker and monitoring

✅ Extensibility for your specific requirements

Whether you're building a SaaS application, internal dashboard, or customer-facing platform, this starter eliminates weeks of authentication development while providing a secure, maintainable foundation for your project.

Ready to get started? Clone the repository and have secure authentication running in minutes.

Resources


Have questions or suggestions? We'd love to hear from you! Open an issue or start a discussion on GitHub.

Top comments (0)