How to Implement Secure Authentication in NestJS with Stytch

Looking to add secure, scalable authentication to your NestJS app without reinventing the wheel? Instead of building login, sessions, and password flows from scratch, you can integrate Stytch authentication, a modern platform with battle-tested security. This guide walks you through the stytch-nestjs-starter, a production-ready starter that shows how to implement NestJS authentication with Stytch, including Redis session caching and smart session management.
Why Use Stytch for Authentication in NestJS?
Common Challenges with DIY NestJS Authentication:
- 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
Benefits of Stytch Authentication for NestJS Applications
- 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)
Stytch Integration Options for NestJS
Frontend vs Backend Authentication in NestJS
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 (B2C) vs Business (B2B) Authentication with Stytch
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 & Flow
NestJS Authentication Architecture with Stytch
How the NestJS–Stytch Authentication Flow Works
Let's visualize how authentication works in our starter:
Exploring the NestJS Stytch Authentication Starter
Redis Session Caching for Faster Auth in NestJS
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; await this.cacheManager.set(sessionToken, value, ttl); return { sessionToken }; }
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
Automatic Session Refresh with Stytch and NestJS
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 sessionTtl = await this.cacheManager.ttl(sessionToken); 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); } }
This ensures users never experience unexpected logouts while maintaining security.
Using the CurrentSession Decorator for Clean Auth Access
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(); } }
How it works:
- AuthGuard Integration: The guard validates the session token and attaches user data to the request
- Type Safety: Returns strongly-typed
CachedSession
object withuserId
andstytchUserId
- Automatic Extraction: Seamlessly extracts session data without manual request parsing
- 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; }, );
This pattern eliminates boilerplate code and provides a consistent way to access user context across your application.
Flexible User Creation in NestJS (Sign-up and Invitations)
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" }'
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" }'
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" }'
Learn more about password reset flows: Stytch Password Reset API.
Getting Started with the NestJS Stytch Auth Starter
Quick Setup and Configuration
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
Connecting Stytch to Your NestJS Project
- Sign up at Stytch Dashboard
- Create a new project
- 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
Running the Starter with PostgreSQL and Redis
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
Testing Authentication in NestJS with Stytch
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
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"
NestJS Authentication Best Practices for Production
Security Hardening for Stytch + NestJS Auth
- Environment Variables: Never commit real credentials to version control
- Master Key: Generate a cryptographically secure master key
- CORS Configuration: Restrict origins to your frontend domains
- HTTPS: Always use HTTPS in production
- Rate Limiting: Implement rate limiting for auth endpoints
- Session Duration Strategy: Balance longer sessions for user experience with shorter sessions for stronger security
Scaling NestJS Authentication with Redis and Connection Pooling
- Redis Configuration: Use Redis Cluster for high availability and scalability
- Connection Pooling: Optimize database connection pools for stable performance under load
- Extended Caching: Introduce additional caching layers (e.g., user profile caching) to reduce database load
Extending NestJS Authentication with Stytch
The stytch-nestjs-starter
is built to be a production-ready foundation, but most real-world applications require additional layers of authentication and authorization. Thanks to Stytch’s flexible APIs and NestJS’s modular architecture, you can easily extend the starter with advanced features that improve security, compliance, and user experience.
Below are some common extensions you can add on top of the starter to make your authentication system more robust and tailored to your application’s needs.
Role-Based Access Control (RBAC)
Most applications need more than just “logged in” vs “not logged in.” You often need to control access to resources based on user roles (e.g., admin
, editor
, viewer
).
With NestJS, you can implement RBAC by:
- Defining roles and permissions in your database schema.
- Attaching user roles to the Stytch session object when a user logs in.
- Creating NestJS guards that check permissions before granting access to a route.
For example, an AdminGuard
could allow only users with the admin
role to access certain endpoints. This approach integrates seamlessly with the CurrentSession
decorator described earlier, making role checks clean and consistent across your codebase.
Multi-Factor Authentication (MFA)
Passwords alone are no longer enough to protect sensitive applications. By adding MFA with Stytch, you can require users to verify their identity through an additional factor such as:
- TOTP (Time-based One-Time Passwords) via authenticator apps.
- SMS-based codes delivered to a verified phone number.
- Email or push-based verification flows.
NestJS can manage MFA by integrating Stytch’s APIs into your existing auth flow. For example, after a user successfully enters their password, you can enforce an additional verification step before issuing a session token. This ensures stronger security for high-value or compliance-heavy applications (e.g., fintech, healthcare, B2B SaaS).
Adding Social Authentication to NestJS with Stytch
Many users prefer logging in with their existing accounts instead of creating a new password. Stytch supports OAuth integrations with popular providers like Google, GitHub, Apple, Facebook, and Microsoft.
In a NestJS app, you can:
- Configure Stytch OAuth flows in your project dashboard.
- Add endpoints in your backend to handle the OAuth callback.
- Store the resulting user identity and link it to your application’s user model.
This allows you to offer a frictionless onboarding experience, improve conversion rates, and reduce password-related support tickets. You can also combine social login with MFA for stronger security without adding unnecessary complexity for users.
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: Secure and Scalable NestJS Authentication with Stytch
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 monitoringImplement secure, scalable NestJS auth with Stytch. Production-ready starter with Redis session caching, auto refresh, and deployment best practices.
- 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
- Repository: stytch-nestjs-starter
- Stytch Documentation: stytch.com/docs
- NestJS Documentation: docs.nestjs.com