Authentication Routes
The toolkit ships every auth endpoint as a mountable bundle. Mount it and you write no controllers; exclude a key and write only the route you want to change.
The rest of this page documents each endpoint's request and response shape, and shows the hand-written form for when you need to override one.
| Full route table | Shipped Routes |
| Framework integration | NestJS · Express · Fastify |
examples/starter-nestjs, examples/starter-express and examples/starter-fastify all mount
the shipped routes. examples/demo-nestjs hand-writes its controllers, as a reference for
overriding.
Mounting the shipped routes
- NestJS
- Express
- Fastify
export const authConfig: NAuthModuleConfig = {
routes: [{ prefix: 'auth' }],
};
AuthModule.forRoot() builds the controllers. No controllers array needed.
const authRouter = express.Router();
registerNAuthExpressRoutes(authRouter, nauth);
app.use('/auth', authRouter);
await fastify.register(
async (scope) => registerNAuthFastifyRoutes(scope, nauth),
{ prefix: '/auth' },
);
Choosing which routes to mount
By group
groups selects bundles. The admin and apiKeysAdmin groups are never mounted by default.
Route Groups lists every route in each one.
{ prefix: 'auth', groups: ['core', 'profile'] }
By key
exclude drops individual routes. It serves two different purposes.
Override — replace a route with your own:
{ prefix: 'auth', exclude: ['login'] }
Then declare just that route (see the sections below for the hand-written form).
Suppress — never expose a capability at all:
{ prefix: 'admin', groups: ['admin'], exclude: ['adminSetPassword', 'adminDeleteUser'] }
exclude removes the endpoint, not the capabilityAdminAuthService.setPassword is still a public method, still callable from a controller you
write, a script, or a background job. If the intent is "nobody may do this, however they reach
it", deny the action in your authorization provider as well:
if (action === 'admin.user.setPassword') {
return { allow: false, reason: 'Password changes go through the email reset flow' };
}
The service then refuses regardless of caller. See Authorization.
An unknown key in exclude throws at mount time, so a typo cannot silently re-expose a route you
meant to suppress.
Web and mobile from one backend
Mount the same routes twice, differing only in transport. Requires
tokenDelivery.method: 'hybrid' — the toolkit refuses at startup otherwise.
routes: [
{ prefix: 'auth', delivery: 'cookies' },
{ prefix: 'mobile/auth', delivery: 'json', groups: ['core', 'social'] },
]
See Token Management for what each mode changes.
Writing your own
Everything below shows the request and response shape of each endpoint, and the hand-written
form. You need this only for routes you exclude.
All endpoints use DTOs for request validation and return structured responses. The Challenge System handles multi-step authentication flows automatically.
Primary Authentication Flow
The core authentication flow consists of five primary endpoints that handle most authentication scenarios.
Signup
Creates a new user account. May return a challenge if email/phone verification is required.
- NestJS
- Express
- Fastify
import { Controller, Post, Body, HttpCode, HttpStatus } from '@nestjs/common';
import { AuthService, SignupDTO, AuthResponseDTO, Public } from '@nauth-toolkit/nestjs';
@Controller('auth')
export class AuthController {
constructor(private readonly authService: AuthService) {}
@Public()
@Post('signup')
@HttpCode(HttpStatus.CREATED)
async signup(@Body() dto: SignupDTO): Promise<AuthResponseDTO> {
return await this.authService.signup(dto);
}
}
import { Router, Request, Response, NextFunction, RequestHandler } from 'express';
import { NAuthInstance, ExpressMiddlewareType } from '@nauth-toolkit/core';
export function createAuthRoutes(nauth: NAuthInstance<ExpressMiddlewareType, RequestHandler>): Router {
const router = Router();
router.post('/signup', nauth.helpers.public(), async (req: Request, res: Response, next: NextFunction) => {
try {
res.status(201).json(await nauth.authService.signup(req.body));
} catch (err) { next(err); }
});
return router;
}
import { FastifyInstance } from 'fastify';
import { NAuthInstance } from '@nauth-toolkit/core';
export async function registerAuthRoutes(fastify: FastifyInstance, nauth: NAuthInstance<any, any>): Promise<void> {
fastify.post(
'/signup',
{ preHandler: [nauth.helpers.public()] },
nauth.adapter.wrapRouteHandler(async (req, res) => {
res.status(201).json(await nauth.authService.signup(req.body as any));
}) as any
);
}
Request DTO: SignupDTO
{
"email": "user@example.com",
"password": "SecurePass123!",
"firstName": "John",
"lastName": "Doe"
}
Response: AuthResponseDTO - Contains tokens or challenge
See Challenge System for challenge handling.
Login
Authenticates user with email/password. May return a challenge if MFA or verification is required.
- NestJS
- Express
- Fastify
import { Controller, Post, Body, HttpCode, HttpStatus } from '@nestjs/common';
import { AuthService, LoginDTO, AuthResponseDTO, Public } from '@nauth-toolkit/nestjs';
@Controller('auth')
export class AuthController {
constructor(private readonly authService: AuthService) {}
@Public()
@Post('login')
@HttpCode(HttpStatus.OK)
async login(@Body() dto: LoginDTO): Promise<AuthResponseDTO> {
return await this.authService.login(dto);
}
}
router.post('/login', nauth.helpers.public(), async (req: Request, res: Response, next: NextFunction) => {
try {
res.json(await nauth.authService.login(req.body));
} catch (err) { next(err); }
});
fastify.post(
'/login',
{ preHandler: [nauth.helpers.public()] },
nauth.adapter.wrapRouteHandler(async (req, res) => {
res.json(await nauth.authService.login(req.body as any));
}) as any
);
Request DTO: LoginDTO
{
"identifier": "user@example.com",
"password": "SecurePass123!"
}
Response: AuthResponseDTO - Contains tokens or challenge
See Challenge System for MFA and challenge handling.
Respond to Challenge
Unified endpoint for completing any challenge type (email verification, phone verification, MFA, password change).
- NestJS
- Express
- Fastify
import { Controller, Post, Body, HttpCode, HttpStatus } from '@nestjs/common';
import { AuthService, RespondChallengeDTO, AuthResponseDTO, Public } from '@nauth-toolkit/nestjs';
@Controller('auth')
export class AuthController {
constructor(private readonly authService: AuthService) {}
@Public()
@Post('respond-challenge')
@HttpCode(HttpStatus.OK)
async respondToChallenge(@Body() dto: RespondChallengeDTO): Promise<AuthResponseDTO> {
return await this.authService.respondToChallenge(dto);
}
}
router.post('/respond-challenge', nauth.helpers.public(), async (req: Request, res: Response, next: NextFunction) => {
try {
res.json(await nauth.authService.respondToChallenge(req.body));
} catch (err) { next(err); }
});
fastify.post(
'/respond-challenge',
{ preHandler: [nauth.helpers.public()] },
nauth.adapter.wrapRouteHandler(async (req, res) => {
res.json(await nauth.authService.respondToChallenge(req.body as any));
}) as any
);
Request DTO: RespondChallengeDTO
Email Verification:
{
"session": "challenge-session-token",
"type": "VERIFY_EMAIL",
"code": "123456"
}
Phone Verification (Collection/Update):
{
"session": "challenge-session-token",
"type": "VERIFY_PHONE",
"phone": "+1234567890"
}
Note: The phone field can be used to:
- Collect a phone number when user has none (e.g., social signup)
- Update an existing phone number if user entered wrong number during signup
After submitting phone, backend sends verification SMS and returns the same challenge for code verification.
Phone Verification (Code):
{
"session": "challenge-session-token",
"type": "VERIFY_PHONE",
"code": "123456"
}
MFA Verification:
{
"session": "challenge-session-token",
"type": "MFA_REQUIRED",
"method": "totp",
"code": "123456"
}
Response: AuthResponseDTO - Contains tokens or next challenge
See Challenge System for complete challenge flow documentation.
Refresh Token
Issues a new access token using a valid refresh token.
- NestJS
- Express
- Fastify
import { Controller, Post, Body, HttpCode, HttpStatus, Req } from '@nestjs/common';
import { AuthService, RefreshTokenDTO, TokenResponse, Public } from '@nauth-toolkit/nestjs';
import type { Request } from 'express';
@Controller('auth')
export class AuthController {
constructor(private readonly authService: AuthService) {}
@Public()
@Post('refresh')
@HttpCode(HttpStatus.OK)
async refresh(@Body() dto: RefreshTokenDTO | undefined, @Req() req: Request): Promise<TokenResponse> {
const dtoToUse: RefreshTokenDTO = dto ?? ({} as RefreshTokenDTO);
if (!dtoToUse.refreshToken) {
dtoToUse.refreshToken = req?.cookies?.['nauth_refresh_token'];
}
return await this.authService.refreshToken(dtoToUse);
}
}
router.post('/refresh', nauth.helpers.public(), async (req: Request, res: Response, next: NextFunction) => {
try {
const token = req.body?.refreshToken?.trim() || req.cookies?.['nauth_refresh_token'];
res.json(await nauth.authService.refreshToken({ refreshToken: token }));
} catch (err) { next(err); }
});
fastify.post(
'/refresh',
{ preHandler: [nauth.helpers.public()] },
nauth.adapter.wrapRouteHandler(async (req, res) => {
const token = (req.body as { refreshToken?: string })?.refreshToken?.trim() || req.cookies?.['nauth_refresh_token'];
res.json(await nauth.authService.refreshToken({ refreshToken: token }));
}) as any
);
Request DTO: RefreshTokenDTO
{
"refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
Response: TokenResponse - New access and refresh tokens
See Token Delivery for token rotation details.
Logout
Revokes the current session and clears authentication cookies. User identity is automatically resolved from the JWT context.
- NestJS
- Express
- Fastify
import { Controller, Get, Query, UseGuards, HttpCode, HttpStatus } from '@nestjs/common';
import { AuthService, LogoutDTO, LogoutResponseDTO, AuthGuard } from '@nauth-toolkit/nestjs';
@Controller('auth')
export class AuthController {
constructor(private readonly authService: AuthService) {}
@UseGuards(AuthGuard)
@Get('logout')
@HttpCode(HttpStatus.OK)
async logout(@Query() dto: LogoutDTO): Promise<LogoutResponseDTO> {
return await this.authService.logout(dto);
}
}
router.get('/logout', nauth.helpers.requireAuth({ csrf: false }), async (req: Request, res: Response, next: NextFunction) => {
try {
res.json(await nauth.authService.logout(req.query));
} catch (err) { next(err); }
});
fastify.get(
'/logout',
{ preHandler: [nauth.helpers.requireAuth({ csrf: false })] },
nauth.adapter.wrapRouteHandler(async (req, res) => {
res.json(await nauth.authService.logout(req.query as any));
}) as any
);
Request DTO: LogoutDTO
Query Parameters:
forgetMe(optional) - Iftrue, untrusts the device
Response: LogoutResponseDTO
{
"success": true
}
Password Management
Change Password
Allows authenticated users to change their password.
- NestJS
- Express
- Fastify
import { Controller, Post, Body, UseGuards, HttpCode, HttpStatus } from '@nestjs/common';
import { AuthService, ChangePasswordDTO, ChangePasswordResponseDTO, AuthGuard } from '@nauth-toolkit/nestjs';
@Controller('auth')
export class AuthController {
constructor(private readonly authService: AuthService) {}
@UseGuards(AuthGuard)
@Post('change-password')
@HttpCode(HttpStatus.OK)
async changePassword(@Body() dto: ChangePasswordDTO): Promise<ChangePasswordResponseDTO> {
return await this.authService.changePassword(dto);
}
}
router.post('/change-password', nauth.helpers.requireAuth(), async (req: Request, res: Response, next: NextFunction) => {
try {
res.json(await nauth.authService.changePassword(req.body));
} catch (err) { next(err); }
});
fastify.post(
'/change-password',
{ preHandler: [nauth.helpers.requireAuth()] },
nauth.adapter.wrapRouteHandler(async (req, res) => {
res.json(await nauth.authService.changePassword(req.body as any));
}) as any
);
Request DTO: ChangePasswordDTO
{
"oldPassword": "OldPass123!",
"newPassword": "NewSecurePass123!"
}
Response: ChangePasswordResponseDTO
{
"success": true
}
Forgot Password
Request a password reset code via email or SMS. The baseUrl field is used by the toolkit to construct the reset link in the email.
- NestJS
- Express
- Fastify
import { Controller, Post, Body, HttpCode, HttpStatus } from '@nestjs/common';
import { AuthService, ForgotPasswordDTO, ForgotPasswordResponseDTO, Public } from '@nauth-toolkit/nestjs';
@Controller('auth')
export class AuthController {
constructor(private readonly authService: AuthService) {}
@Public()
@Post('forgot-password')
@HttpCode(HttpStatus.OK)
async forgotPassword(@Body() dto: ForgotPasswordDTO): Promise<ForgotPasswordResponseDTO> {
dto.baseUrl = `${process.env.FRONTEND_BASE_URL || 'http://localhost:4200'}/auth/reset-password`;
return await this.authService.forgotPassword(dto);
}
}
router.post('/forgot-password', nauth.helpers.public(), async (req: Request, res: Response, next: NextFunction) => {
try {
res.json(await nauth.authService.forgotPassword({ ...req.body, baseUrl: 'https://localhost:4200' }));
} catch (err) { next(err); }
});
fastify.post(
'/forgot-password',
{ preHandler: [nauth.helpers.public()] },
nauth.adapter.wrapRouteHandler(async (req, res) => {
res.json(await nauth.authService.forgotPassword({ ...(req.body as object), baseUrl: 'https://localhost:4200' } as any));
}) as any
);
Request DTO: ForgotPasswordDTO
{
"identifier": "user@example.com"
}
Response: ForgotPasswordResponseDTO - Response details in same file
Confirm Forgot Password
Confirm password reset code and set new password.
- NestJS
- Express
- Fastify
import { Controller, Post, Body, HttpCode, HttpStatus } from '@nestjs/common';
import { AuthService, ConfirmForgotPasswordDTO, ConfirmForgotPasswordResponseDTO, Public } from '@nauth-toolkit/nestjs';
@Controller('auth')
export class AuthController {
constructor(private readonly authService: AuthService) {}
@Public()
@Post('forgot-password/confirm')
@HttpCode(HttpStatus.OK)
async confirmForgotPassword(@Body() dto: ConfirmForgotPasswordDTO): Promise<ConfirmForgotPasswordResponseDTO> {
return await this.authService.confirmForgotPassword(dto);
}
}
router.post('/forgot-password/confirm', nauth.helpers.public(), async (req: Request, res: Response, next: NextFunction) => {
try {
res.json(await nauth.authService.confirmForgotPassword(req.body));
} catch (err) { next(err); }
});
fastify.post(
'/forgot-password/confirm',
{ preHandler: [nauth.helpers.public()] },
nauth.adapter.wrapRouteHandler(async (req, res) => {
res.json(await nauth.authService.confirmForgotPassword(req.body as any));
}) as any
);
Request DTO: ConfirmForgotPasswordDTO - DTO details in same file
{
"identifier": "user@example.com",
"code": "123456",
"newPassword": "NewSecurePass123!"
}
Helper Endpoints
Get Setup Data
Get MFA setup data during MFA_SETUP_REQUIRED challenge.
- NestJS
- Express
- Fastify
import { Controller, Post, Body, HttpCode, HttpStatus, Inject, BadRequestException } from '@nestjs/common';
import { MFAService, GetSetupDataDTO, GetSetupDataResponseDTO, Public } from '@nauth-toolkit/nestjs';
@Controller('auth')
export class AuthController {
constructor(
@Inject(MFAService)
private readonly mfaService?: MFAService,
) {}
@Public()
@Post('challenge/setup-data')
@HttpCode(HttpStatus.OK)
async getSetupData(@Body() dto: GetSetupDataDTO): Promise<GetSetupDataResponseDTO> {
if (!this.mfaService) {
throw new BadRequestException('MFA service is not available');
}
return await this.mfaService.getSetupData(dto);
}
}
router.post('/challenge/setup-data', nauth.helpers.public(), async (req: Request, res: Response, next: NextFunction) => {
try {
res.json(await nauth.mfaService!.getSetupData(req.body));
} catch (err) { next(err); }
});
fastify.post(
'/challenge/setup-data',
{ preHandler: [nauth.helpers.public()] },
nauth.adapter.wrapRouteHandler(async (req, res) => {
res.json(await nauth.mfaService!.getSetupData(req.body as any));
}) as any
);
Request DTO: GetSetupDataDTO
{
"session": "challenge-session-token",
"method": "sms",
"setupData": { "phoneNumber": "+14155552671" }
}
setupData is optional and method-specific. For SMS it carries the phone number when the account has none (the challenge sets requiresPhoneCollection: "true"), see SMS MFA > Forced Setup.
Response: GetSetupDataResponseDTO - Provider-specific setup data
See MFA Guide for complete MFA setup flow.
Resend Code
Resend verification code for the current challenge.
- NestJS
- Express
- Fastify
import { Controller, Post, Body, HttpCode, HttpStatus } from '@nestjs/common';
import { AuthService, ResendCodeDTO, ResendCodeResponseDTO, Public } from '@nauth-toolkit/nestjs';
@Controller('auth')
export class AuthController {
constructor(private readonly authService: AuthService) {}
@Public()
@Post('challenge/resend')
@HttpCode(HttpStatus.OK)
async resendCode(@Body() dto: ResendCodeDTO): Promise<ResendCodeResponseDTO> {
return await this.authService.resendCode(dto);
}
}
router.post('/challenge/resend', nauth.helpers.public(), async (req: Request, res: Response, next: NextFunction) => {
try {
res.json(await nauth.authService.resendCode(req.body));
} catch (err) { next(err); }
});
fastify.post(
'/challenge/resend',
{ preHandler: [nauth.helpers.public()] },
nauth.adapter.wrapRouteHandler(async (req, res) => {
res.json(await nauth.authService.resendCode(req.body as any));
}) as any
);
Request DTO: ResendCodeDTO
{
"session": "challenge-session-token"
}
Response: ResendCodeResponseDTO
{
"destination": "u***r@example.com"
}
MFA Management
Get MFA Status
Get MFA configuration status for the current user.
- NestJS
- Express
- Fastify
import { Controller, Get, UseGuards } from '@nestjs/common';
import { MFAService, AuthGuard } from '@nauth-toolkit/nestjs';
@Controller('auth')
export class AuthController {
constructor(private readonly mfaService: MFAService) {}
@UseGuards(AuthGuard)
@Get('mfa/status')
async getMfaStatus() {
return await this.mfaService.getMfaStatus();
}
}
router.get('/mfa/status', nauth.helpers.requireAuth(), async (_req: Request, res: Response, next: NextFunction) => {
try {
res.json(await nauth.mfaService!.getMfaStatus());
} catch (err) { next(err); }
});
fastify.get(
'/mfa/status',
{ preHandler: [nauth.helpers.requireAuth()] },
nauth.adapter.wrapRouteHandler(async (_req, res) => {
res.json(await nauth.mfaService!.getMfaStatus());
}) as any
);
Response:
{
"enabled": true,
"required": false,
"methods": ["totp", "sms"],
"availableMethods": ["totp", "sms", "passkey"],
"hasBackupCodes": true,
"preferredMethod": "totp"
}
See Managing MFA Devices for complete MFA device management.
Social Authentication
Redirect-first social login. The backend owns the OAuth callback, sets cookies (or issues an exchangeToken), then redirects back to the frontend.
Start social login redirect
GET /auth/social/:provider/redirect?returnTo=/auth/callback&appState=12345
- NestJS
- Express
- Fastify
import { Controller, Get, Param, Query, Redirect } from '@nestjs/common';
import { Public, SocialRedirectHandler, StartSocialRedirectQueryDTO, StartSocialRedirectResponseDTO } from '@nauth-toolkit/nestjs';
@Controller('auth/social')
export class SocialRedirectController {
constructor(private readonly socialRedirect: SocialRedirectHandler) {}
@Public()
@Redirect()
@Get(':provider/redirect')
async start(
@Param('provider') provider: string,
@Query() dto: StartSocialRedirectQueryDTO,
): Promise<StartSocialRedirectResponseDTO> {
return await this.socialRedirect.start(provider, dto);
}
}
// socialRedirect = nauth.socialRedirect! (from NAuth.create())
router.get('/:provider/redirect', nauth.helpers.public(), async (req: Request, res: Response, next: NextFunction) => {
try {
const { url } = await socialRedirect.start(req.params.provider, req.query);
res.redirect(url);
} catch (err) { next(err); }
});
// socialRedirect = nauth.socialRedirect! (from NAuth.create())
fastify.get(
'/social/:provider/redirect',
{ preHandler: [nauth.helpers.public()] },
nauth.adapter.wrapRouteHandler(async (req, res) => {
const params = req.params as { provider: string };
const { url } = await socialRedirect.start(params.provider, req.query);
(res.raw as any).redirect(url, 302);
}) as any
);
Provider callback (backend)
Provider redirects back to:
GET /auth/social/:provider/callback(Google/Facebook)POST /auth/social/:provider/callback(Appleform_post)
Backend responds with:
- 302 → frontend
returnTo?appState=...(cookies success path), and sets httpOnly cookies in the same response - 302 → frontend
returnTo?appState=...&exchangeToken=...(json/hybrid, and cookies-with-challenge)
NestJS: simplified controller
The handler reads delivery and deviceToken from ContextStorage and applies cookies via HTTP_RESPONSE. The controller only passes provider and DTO.
import { Controller, Get, Post, Param, Query, Body, Redirect } from '@nestjs/common';
import { Public, SocialRedirectHandler, SocialCallbackQueryDTO, SocialCallbackFormDTO, SocialRedirectCallbackResponseDTO } from '@nauth-toolkit/nestjs';
@Controller('auth/social')
export class SocialRedirectController {
constructor(private readonly socialRedirect: SocialRedirectHandler) {}
@Public()
@Redirect()
@Get(':provider/callback')
async callbackGet(
@Param('provider') provider: string,
@Query() dto: SocialCallbackQueryDTO,
): Promise<SocialRedirectCallbackResponseDTO> {
return await this.socialRedirect.callback(provider, dto);
}
@Public()
@Redirect()
@Post(':provider/callback')
async callbackPost(
@Param('provider') provider: string,
@Body() dto: SocialCallbackFormDTO,
): Promise<SocialRedirectCallbackResponseDTO> {
return await this.socialRedirect.callback(provider, dto);
}
}
Exchange exchangeToken
POST /auth/social/exchange
{ "exchangeToken": "..." }
Response: AuthResponseDTO
OpenID Connect Interaction
When your application is also an OpenID Connect provider, four more routes bridge the provider and your login. OIDCProviderModule.forRoot() registers them for you at oidc/interaction/:uid, under any global prefix — the paths below assume none.
| Endpoint | Method | Auth | Purpose |
|---|---|---|---|
/oidc/interaction/:uid | GET | Optional | What the pending authorization request needs |
/oidc/interaction/:uid/abort | POST | Optional | Abandon the request with access_denied |
/oidc/interaction/:uid/confirm | POST | Optional | Record the consent decision |
/oidc/interaction/:uid/login | POST | Optional | Complete the login step |
"Optional" means the routes run inside the guard chain so a signed-in caller is identified, but an anonymous caller is answered, not rejected — a signed-out response is what tells your page to send the user to login. If you write these routes yourself, reproduce that: @UseGuards(AuthGuard) on the class and @Public() on every route.
Each returns JSON rather than a 302; the consent screen navigates itself with window.location.assign(redirectTo). See createOIDCInteractionController for the shipped implementation and Building the Consent Screen for the frontend.
User Profile
Get Current User
Get the authenticated user's profile. Use UserResponseDTO.fromEntity() to sanitize the response and exclude sensitive fields like password hashes.
- NestJS
- Express
- Fastify
import { Controller, Get, UseGuards } from '@nestjs/common';
import { AuthGuard, CurrentUser, UserResponseDTO } from '@nauth-toolkit/nestjs';
import type { IUser } from '@nauth-toolkit/nestjs';
@Controller('auth')
export class AuthController {
@UseGuards(AuthGuard)
@Get('profile')
async getProfile(@CurrentUser() user: IUser): Promise<UserResponseDTO> {
return UserResponseDTO.fromEntity(user);
}
}
import { IUser, UserResponseDTO } from '@nauth-toolkit/core';
router.get('/profile', nauth.helpers.requireAuth(), (_req: Request, res: Response, next: NextFunction) => {
try {
const user = nauth.helpers.getCurrentUser() as IUser;
res.json(UserResponseDTO.fromEntity(user));
} catch (err) { next(err); }
});
import { IUser, UserResponseDTO } from '@nauth-toolkit/core';
fastify.get(
'/profile',
{ preHandler: [nauth.helpers.requireAuth()] },
nauth.adapter.wrapRouteHandler(async (_req, res) => {
const user = nauth.helpers.getCurrentUser() as IUser;
res.json(UserResponseDTO.fromEntity(user));
}) as any
);
Response: UserResponseDTO - Sanitized user profile
Error Handling
All endpoints use NAuthException for structured error responses. Handle errors appropriately:
- NestJS
- Express
- Fastify
import { NAuthHttpExceptionFilter } from '@nauth-toolkit/nestjs';
// In main.ts
app.useGlobalFilters(new NAuthHttpExceptionFilter());
import { NAuthException } from '@nauth-toolkit/core';
app.use((error, req, res, next) => {
if (error instanceof NAuthException) {
return res.status(error.statusCode).json(error.toJSON());
}
next(error);
});
import { NAuthException } from '@nauth-toolkit/core';
fastify.setErrorHandler((error, request, reply) => {
if (error instanceof NAuthException) {
return reply.status(error.statusCode).send(error.toJSON());
}
reply.send(error);
});
See Error Handling for complete error handling guide.
Related Documentation
- Challenge System - Multi-step authentication flows
- AuthService API - Complete service reference
- DTO Reference - All request/response DTOs
- MFA Guide - Multi-factor authentication setup
- Social Login - OAuth integration
- OIDC Provider - Being an identity provider yourself
- Token Delivery - Token delivery modes