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
The Complete Authentication Flow
Let's visualize how authentication works in our starter:
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 };
}
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);
}
}
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();
}
}
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.
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"
}'
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 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
2. Stytch Configuration
- 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
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
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
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"
Production Considerations
Security Best Practices
- 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
Performance and Scalability
- 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 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
- 📖 Repository: stytch-nestjs-starter
- 🔐 Stytch Documentation: stytch.com/docs
- 🏗️ NestJS Documentation: docs.nestjs.com
Have questions or suggestions? We'd love to hear from you! Open an issue or start a discussion on GitHub.
Top comments (0)