Package Exports
- oauth-entra-id
- oauth-entra-id/express
- oauth-entra-id/nestjs
Readme
oauth-entra-id
A secure, performant, and feature-rich
OAuth 2.0 integration for Microsoft Entra ID
fully abstracted and production-ready.
About π
A secure, performant, and feature-rich OAuth 2.0 integration for Microsoft Entra ID β fully abstracted and production-ready.
This library simplifies the Authorization Code Grant flow (with PKCE), token rotation, B2B authentication, and the On-Behalf-Of (OBO) flow with a strong focus on type safety, security and performance.
Designed to be framework-agnostic and developer-friendly, it eliminates the complexity of managing Microsoft Entra ID authentication β so you can focus on building your application, not your auth layer.
Features π
- π Secure backend-driven OAuth 2.0 Authorization Code Grant flow with PKCE
- β‘ High performance optimized for production environments
- πͺ Built-in cookie-based authentication with token management and rotation
- π’ On-Behalf-Of (OBO) flow for downstream services access
- π€ B2B app support (client credentials)
- π§© Fully typed results and errors via
Result<T>andOAuthError - π¦Ύ Framework-agnostic core with Express and NestJS bindings
Getting Started π
Installation π₯
npm install oauth-entra-idRequires Node.js 16 or higher.
Azure Portal Setup π οΈ
Basic setup for Microsoft Entra ID (Azure AD):
- Go to the Azure Portal.
- Select your App registration or create a new one.
- You can find the
Client IDandTenant IDin the app registration overview. - Under "Authentication", add a new platform:
- Choose "Web" and set the redirect URI to your server callback URL (e.g.,
https://your-server.com/auth/callback).
- Choose "Web" and set the redirect URI to your server callback URL (e.g.,
- Under "Certificates & secrets", create a new client secret and copy it. This will be your
clientSecret. - To add scopes for your app you can either use one of the following methods:
- Choose "API permissions" and add the required Microsoft Graph permissions (e.g.,
openid,profile,email). - Or, if you want you can create a custom scope for your app by going to "Expose an API" and defining a new scope (e.g.,
api://<your-client-id>/access).
- Choose "API permissions" and add the required Microsoft Graph permissions (e.g.,
- Important step: go to "Manifest and edit the manifest file to set the
requestedAccessTokenVersionto2. This is required for the package to work correctly with OAuth 2.0. - If you want to add roles you can do so by going to "App roles" and defining the roles you need. Make sure to assign these roles to users or groups in your Azure AD.
Setting up B2B app authentication:
- Go to "App roles" and create a new app role for "Application" type.
- Then go to "API permissions" add new permission and select the application that is allowed to authenticate to your app.
- In the configuration of
OAuthConfigof the first app, make sure to set theacceptB2BRequestsproperty totrue. - Then in the configuration of
OAuthConfigof the second app, make sure to set theb2bTargetedAppsproperty with the app name and scope of the first app, the scope should usually end with/.default. - That's it now the second app can authenticate to the first app using the client credentials flow.
Setting up On-Behalf-Of (OBO) flow for downstream services (works only if applications share the same tenant):
- Go to "Expose an API" and create a new scope for your downstream service (e.g.,
api://<your-client-id>/access). - Then scroll down to "Authorized client applications" and add the client ID of the application that will be using the OBO flow.
- In the configuration of
OAuthConfigof the first app, make sure to set thedownstreamServicesproperty with the service name, scope, andsecretKeyof the downstream service, the scope should usually end with/.default. - That's it now the first app can acquire tokens for the downstream service using the OBO flow.
Configuration βοΈ
export interface OAuthConfig {
azure: {
clientId: string; // Client ID from Azure App registration
tenantId: string; // 'common' for multi-tenant apps or your specific tenant ID
scopes: string[]; // e.g., ['openid', 'profile', 'email']
clientSecret: string; // Client secret from Azure App registration
// Optional for On-Behalf-Of (OBO) flow
downstreamServices?: Array<{
serviceName: string; // Unique identifier of the downstream service
scope: string; // Usually ends with `/.default`
serviceUrl: string | string[]; // URL(s) of the downstream service
encryptionKey: string; // 32 character encryption key for the service
cryptoType?: 'node' | 'web-api'; // Defaults to 'node'
accessTokenMaxAge?: number; // Defaults to 1 hour
}>;
// Optional for B2B apps
b2bApps?: Array<{
appName: string; // Unique identifier of the B2B app
scope: string; // Usually ends with `/.default`
}>;
};
frontendUrl: string | string[]; // Allowed frontend redirect URL(s)
serverCallbackUrl: string; // Server callback URL (must match the one registered in Azure)
encryptionKey: string; // 32 character encryption key for the access and refresh tokens
// Optional advanced settings
advanced?: {
loginPrompt?: 'email' | 'select-account' | 'sso'; // Defaults to 'sso'
acceptB2BRequests?: boolean; // If true, allows B2B authentication. Defaults to `false`
cryptoType?: 'node' | 'web-api'; // Defaults to 'node'
disableCompression?: boolean; //Whether to disable compression for access tokens. Defaults to `false`
cookies?: {
timeUnit?: 'ms' | 'sec'; // Defaults to 'sec'
disableSecure?: boolean;
disableSameSite?: boolean;
accessTokenMaxAge?: number; // Defaults to 1 hour
refreshTokenMaxAge?: number; // Defaults to 30 days
};
};
}Error Handling β οΈ
This package uses a custom Result<T> discriminated union to handle async operations in a type-safe, exception-free way.
If the method returns this type make sure to handle the error case or check if the success property is true before accessing the result.
Usually methods with this return type will start with the prefix try or verify, indicating that they may fail and return an error.
If the return type is not Result<T>, it will throw an OAuthError with a specific error type and message.
Both ResultErr and OAuthError give you the following properties:
type- The type of the error for examplenullish_valuemessage- A human-readable error message that can be shown to the user.statusCode- The HTTP status code for the error, useful for API responses.description- A detailed description of the error, useful for debugging. Don't show this to the user, to avoid leaking sensitive information.
Usage π―
The package provides three main modules for different frameworks:
oauth-entra-id- Core package for any TS/JS framework (e.g., Express, NestJS, HonoJS, Fastify, etc.). jump to Core.oauth-entra-id/express- For Express.js applications (recommended). Jump to Express.oauth-entra-id/nestjs- For NestJS applications (recommended). Jump to NestJS.
There is another provider called LiteProvider, that you can import from the core package. This class has 2 methods verifyJwt and tryGetB2BToken(which is the same as the normal method).
You can use this provider if your server is a B2B only server with unencrypted JWT tokens, and you don't need the full OAuth 2.0 flow.
Usage - Core π§±
The core package provides the flexibility to integrate OAuth 2.0 with Entra ID in any Node.js framework.
Let's start by creating a global instance of OAuthProvider in your application. This instance will be used to handle authentication, token exchange, and other OAuth-related operations.
Example of creating a basic instance of OAuthProvider:
import { OAuthProvider } from 'oauth-entra-id';
import env from './env';
const oauthProvider = new OAuthProvider({
azure: {
clientId: env.AZURE_CLIENT_ID,
tenantId: env.AZURE_TENANT_ID,
scopes: [env.AZURE_CLIENT_SCOPE],
clientSecret: env.AZURE_CLIENT_SECRET,
},
frontendUrl: env.FRONTEND_URL,
serverCallbackUrl: `${env.SERVER_URL}/auth/callback`,
encryptionKey: env.SECRET,
});Core Methods (Examples with HonoJS) π§©
settings
You can access the settings of the OAuthProvider instance using the settings property.
interface OAuthSettings {
readonly loginPrompt: 'email' | 'select-account' | 'sso';
readonly acceptB2BRequests: boolean;
readonly b2bApps?: string[];
readonly downstreamServices?: string[];
readonly disableCompression: boolean;
readonly cookies: {
readonly timeUnit: 'sec' | 'ms';
readonly isSecure: boolean;
readonly isSameSite: boolean;
readonly accessTokenExpiry: number;
readonly refreshTokenExpiry: number;
readonly accessTokenName: string;
readonly refreshTokenName: string;
};
}getAuthUrl()
Generate an OAuth2 authorization URL for user login (PKCE-backed).
Parameters:
params(optional):loginPrompt(optional) - Override the default prompt (sso|email|select-account).email(optional) - Email address to pre-fill the login form.frontendUrl(optional) - Frontend URL override to redirect the user after authentication.
Returns:
- Promise of an object:
authUrl- The URL to redirect the user for authentication.ticket- A unique ticket for the authentication session, this is used for bearer flow only.
app.post('/authenticate', async (c) => {
const { loginPrompt, email, frontendUrl } = await c.req.json();
const { authUrl } = await oauthProvider.getAuthUrl({ loginPrompt, email, frontendUrl });
return c.json({ url: authUrl });
});getTokenByCode()
Exchange an authorization code for encrypted tokens and metadata
Parameters:
params:code- The authorization code received from the OAuth flow.state- The state parameter received from Microsoft.
Returns:
- Promise of an object:
accessToken- Access token object containing the token value, suggested name, and options.refreshToken(optional) - Refresh token object containing the token value, suggested name, and options.frontendUrl- The frontend URL to redirect the user after authentication.ticketId- Ticket ID useful for bearer flow, store the tokens in a cache or database with a key based on this ticket ID. Later you can use this ticket ID to retrieve the tokens.msalResponse- The MSAL response object for extra information if needed.
app.post('/callback', async (c) => {
const { code, state } = await c.req.parseBody();
const { frontendUrl, accessToken, refreshToken } = await oauthProvider.getTokenByCode({ code, state });
setCookie(c, accessToken.name, accessToken.value, accessToken.options);
if (refreshToken) setCookie(c, refreshToken.name, refreshToken.value, refreshToken.options);
return c.redirect(frontendUrl);
});getLogoutUrl()
Build a logout URL and cookie-deletion instructions.
Parameters:
params(optional):frontendUrl(optional) - Frontend URL override to redirect the user after log out.
Returns:
- Promise of an object:
logoutUrl- The URL to redirect the user for logout.deleteAccessToken- Access token cookie object containing the token name, value, and options.deleteRefreshToken- Refresh token cookie object containing the token name, value, and options.
app.post('/logout', async (c) => {
const { frontendUrl } = await c.req.json();
const { logoutUrl, deleteAccessToken, deleteRefreshToken } = await oauthProvider.getLogoutUrl({ frontendUrl });
deleteCookie(c, deleteAccessToken.name, deleteAccessToken.options);
deleteCookie(c, deleteRefreshToken.name, deleteRefreshToken.options);
return c.json({ url: logoutUrl });
});verifyAccessToken<T>()
Verify the access token (either encrypted or in JWT format) and extract its payload. Make sure that user access tokens are encrypted and app tokens aren't.
Parameters:
accessToken- The access token string either encrypted or in JWT format.
Returns:
- Promise of a
Resultobject:meta- Metadata about the user that has been extracted from the payload.payload- The payload of the access token.rawJwt- The access token in JWT format.injectedData- If the token has been injected with extra data, it will be returned here with the typeT.hasInjectedData- If the token has injected data, this will betrue. Otherwise, it will befalse.
T is a generic type if you want to specify the type of the injected data.
It will remain undefined if the token does not have injected data.
An example will be shown below.
tryRefreshTokens()
Verifies and uses the refresh token to get new set of access and refresh tokens.
Parameters:
refreshToken- Encrypted refresh token string.
Returns:
- Promise of a
Resultobject:newAccessToken- The new access token object containing the token value, suggested name, and options.newRefreshToken- The new refresh token object containing the token value, suggested name, and options.meta- Metadata about the user that has been extracted from the payload.payload- The payload of the access token.rawJwt- The access token in JWT format.msalResponse- The MSAL response object for extra information if needed.
An example will be shown below.
tryInjectData<T>()
Embed non-sensitive metadata into the access token.
Make sure not to inject sensitive data and also do not inject too much data, as it can lead to token size issues.
Parameters:
params:accessToken- The access token string either encrypted or in JWT format.data- The data to inject into the token. This can be any object.
Returns:
- Promise of a
Resultobject:newAccessToken- New access token that has been injected with the data.injectedData- The injected data of typeT.
T is a generic type if you want to specify the type of the injected data.
It will be inferred from the data parameter.
An example of verifyAccessToken, getTokenByRefresh, and injectData:
export const protectRoute = createMiddleware(async (c, next) => {
const { accessTokenName, refreshTokenName } = oauthProvider.settings.cookies;
const accessToken = getCookie(c, accessTokenName);
const refreshToken = getCookie(c, refreshTokenName);
if (!accessToken && !refreshToken) throw new HTTPException(401, { message: 'Unauthorized' });
const at = await oauthProvider.verifyAccessToken<{ randomNumber: number }>(accessToken);
if (at.success) {
if (at.hasInjectedData) {
c.set('userInfo', {
uniqueId: at.meta.uniqueId,
email: at.meta.email,
name: at.meta.name,
injectedData: at.injectedData,
});
return await next();
}
const inj = await oauthProvider.tryInjectData({ accessToken: at.rawJwt, data: getRandomNumber() });
if (inj.success) setCookie(c, inj.newAccessToken.name, inj.newAccessToken.value, inj.newAccessToken.options);
c.set('userInfo', {
uniqueId: at.meta.uniqueId,
email: at.meta.email,
name: at.meta.name,
injectedData: inj.injectedData,
});
return await next();
}
const rt = await oauthProvider.tryRefreshTokens(refreshToken);
if (rt.error) throw new HttpException(rt.error.statusCode, { message: rt.error.message });
const inj = await oauthProvider.tryInjectData({ accessToken: rt.rawJwt, data: getRandomNumber() });
const final = inj.success ? inj.newAccessToken : rt.newAccessToken;
setCookie(c, final.name, final.value, final.options);
if (rt.newRefreshToken) setCookie(c, rt.newRefreshToken.name, rt.newRefreshToken.value, rt.newRefreshToken.options);
c.set('userInfo', {
uniqueId: rt.meta.uniqueId,
email: rt.meta.email,
name: rt.meta.name,
injectedData: inj.injectedData,
});
return await next();
});tryDecryptTicket()
Decrypts a ticket and returns the ticket ID. Useful for bearer flow.
Parameters:
ticket- The ticket string to decrypt.
Returns:
- Promise of a
Resultobject:ticketId- The decrypted ticket ID.
tryGetB2BToken()
Acquire an app token for a specific app, using the client credentials flow. Caches tokens for better performance.
This method is useful for B2B applications that need to authenticate and authorize themselves against other services.
Note: This method is only available if b2bTargetedApps is configured in the advanced section of the OAuthConfig.
Parameters:
params:apporapps- The name of the B2B app or an array of app names to generate tokens for.
Returns:
- Promise of a
Resultof an object with:resultorresults- An object or an array of objects (based on the parameters) containing:appName- The name of the B2B app.clientId- The client ID of the B2B app.token- The B2B access token string.msalResponse- The MSAL response object for extra information if needed.isCached- A boolean indicating if the token was cached or not.expiresAt- The expiration time of the token minus 5 minutes.
protectedRouter.post('/get-b2b-info', async (c) => {
const { app } = await c.req.json();
const { result, error } = await oauthProvider.tryGetB2BToken({ app });
if (error) throw new HttpException(error.statusCode, { message: error.message });
const res = await axios.get(env.OTHER_SERVER, {
headers: { Authorization: `Bearer ${result.token}` },
});
return c.json({ data: res.data });
});getTokenOnBehalfOf()
Acquire tokens for trusted downstream services via the On-Behalf-Of (OBO) flow.
This method is useful for scenarios where your application needs to call downstream services on behalf of the user, using the user's access token.
Note: This method is only available if downstreamServices is configured in the advanced section of the OAuthConfig.
Parameters:
params:accessToken- The access token string either encrypted or in JWT format.serviceorservices- The name of the downstream service or an array of service names to acquire tokens for.
Returns:
Promise of an object or an array of objects (based on the parameters) containing:
resultorresults- An object or an array of objects (based on the parameters) with the following properties:serviceName- The name of the OBO service.clientId- The client ID of the OBO service.accessToken- The OBO access token string.msalResponse- The MSAL response object for extra information if needed.
app.post('/on-behalf-of', protectRoute, async (c) => {
const { services } = await c.req.json();
const accessToken = c.get('userInfo').accessToken;
const { results } = await oauthProvider.getOnBehalfOfToken({ accessToken, services });
for (const { accessToken } of results) {
setCookie(c, accessToken.name, accessToken.value, accessToken.options);
}
return c.json({ message: 'On Behalf Of tokens generated successfully' });
});Usage - Express π«
When using the package with Express, you should import from oauth-entra-id/express to easily integrate OAuth2.0.
Note: you can use the core package with Express, but you will need to implement your own logic for handling authentication, token exchange, and other OAuth-related operations.
Also the oauthProvider instance is injected in the request object, so you can access it using req.oauthProvider.
Also, you need to install cors package:
npm install cors
# For TypeScript
npm install --save-dev @types/corsThen in the root of your Express app, import authConfig and configure it:
import express from 'express';
import cors from 'cors';
import { authConfig } from 'oauth-entra-id/express';
import env from './env';
function bootstrap() {
const app = express();
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(
cors({
origin: env.FRONTEND_URL,
methods: 'GET,POST,PUT,DELETE,OPTIONS',
allowedHeaders: ['Content-Type', 'Authorization'],
credentials: true, // <-- Allow credentials to be included in CORS requests
}),
);
// Other configurations...
app.use(
authConfig({
azure: {
clientId: env.AZURE_CLIENT_ID,
tenantId: env.AZURE_TENANT_ID,
scopes: [env.AZURE_CLIENT_SCOPE],
clientSecret: env.AZURE_CLIENT_SECRET,
},
frontendUrl: env.FRONTEND_URL,
serverCallbackUrl: `${env.SERVER_URL}/auth/callback`,
encryptionKey: env.SECRET,
}),
);
// Here you can add your routes and other configurations
const port = Number(env.PORT) || 3000;
app.listen(port, () => {
console.log(`π Express server running at http://localhost:${port}`);
});
}
bootstrap();Now you can define your routes for login, callback, and logout using POST methods for security.
import express, { type Router } from 'express';
import { handleAuthentication, handleCallback, handleLogout } from 'oauth-entra-id/express';
export const authRouter: Router = express.Router();
authRouter.post('/authenticate', handleAuthentication()); // Returns {url: authUrl}
authRouter.post('/callback', handleCallback()); // Set tokens in cookies and redirect to frontendUrl
authRouter.post('/logout', handleLogout()); // Delete cookies and returns {url: logoutUrl}To secure your routes, you can use the protectRoute() middleware and access the user information from the request object.
protectRoute() can receive an optional callback function that will be called with the user information after the authentication is verified. This is useful if you want to perform additional actions or validations based on the user information.
import express, { type Router } from 'express';
import { type CallbackFunction, OAuthError } from 'oauth-entra-id';
import { protectRoute } from 'oauth-entra-id/express';
const protectedRouter: Router = express.Router();
// Optional
const callbackFunction: CallbackFunction = async ({ userInfo, injectData }) => {
if (userInfo.isApp === false && !userInfo.injectedData) {
const { error } = await injectData({ randomNumber: getRandomNumber() });
if (error) throw new OAuthError(error);
}
};
protectedRouter.use(protectRoute(callbackFunction));
protectedRouter.get('/user-info', (req: Request, res: Response) => {
res.status(200).json({ message: 'Protected route :)', user: req.userInfo });
});
protectedRoute.post('/on-behalf-of', sharedHandleOnBehalfOf());Usage - NestJS πͺΊ
When using the package with NestJS, you should import from oauth-entra-id/nestjs to easily integrate OAuth2.0.
Note: you can use the core package with NestJS, but you will need to implement your own logic for handling authentication, token exchange, and other OAuth-related operations.
Also the oauthProvider instance is injected in the request object, so you can access it using req.oauthProvider.
Start at the root of your NestJS app, import authConfig and configure it:
import { NestFactory } from '@nestjs/core';
import { authConfig } from 'oauth-entra-id/nestjs';
import { AppModule } from './app.module';
import env from './env';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.enableCors({
origin: env.FRONTEND_URL,
methods: 'GET,POST,PUT,DELETE,OPTIONS',
allowedHeaders: ['Content-Type', 'Authorization'],
credentials: true, // <-- Allow credentials to be included in CORS requests
});
// Other configurations...
app.use(
authConfig({
azure: {
clientId: env.AZURE_CLIENT_ID,
tenantId: env.AZURE_TENANT_ID,
scopes: [env.AZURE_CLIENT_SCOPE],
clientSecret: env.AZURE_CLIENT_SECRET,
},
frontendUrl: env.FRONTEND_URL,
serverCallbackUrl: `${env.SERVER_URL}/auth/callback`,
encryptionKey: env.SECRET,
}),
);
// Other configurations...
const port = Number(env.PORT) || 3000;
await app.listen(port);
console.log(`π NestJS server running on: http://localhost:${port}`);
}
bootstrap();Now you can define your routes for login, callback, and logout using POST methods for security.
import type { Request, Response } from 'express';
import { Controller, Req, Res, Post } from '@nestjs/common';
import { handleAuthentication, handleCallback, handleLogout } from 'oauth-entra-id/nestjs';
@Controller('auth')
export class AuthController {
@Post('authenticate')
async authenticate(@Req() req: Request, @Res() res: Response) {
await handleAuthentication(req, res); // Returns {url: authUrl}
}
@Post('callback')
async callback(@Req() req: Request, @Res() res: Response) {
await handleCallback(req, res); // Set tokens in cookies and redirect to frontendUrl
}
@Post('logout')
async logout(@Req() req: Request, @Res() res: Response) {
await handleLogout(req, res); // Delete cookies and returns {url: logoutUrl}
}
}Let's create the guard that will protect your routes while getting the user information.
isAuthenticated() can receive an optional callback function that will be called with the user information after the authentication is verified. This is useful if you want to perform additional actions or validations based on the user information.
import type { Request, Response } from 'express';
import { CanActivate, ExecutionContext, Injectable, UnauthorizedException } from '@nestjs/common';
import { type CallbackFunction, OAuthError } from 'oauth-entra-id';
import { isAuthenticated } from 'oauth-entra-id/nestjs';
// Optional
const callbackFunction: CallbackFunction = async ({ userInfo, injectData }) => {
if (userInfo.isApp === false && !userInfo.injectedData) {
const { error } = await injectData({ randomNumber: getRandomNumber() });
if (error) throw new OAuthError(error);
}
};
@Injectable()
export class ProtectRoute implements CanActivate {
async canActivate(context: ExecutionContext): Promise<boolean> {
const req = context.switchToHttp().getRequest<Request>();
const res = context.switchToHttp().getResponse<Response>();
return await isAuthenticated(req, res, callbackFunction);
}
}Now you can use the ProtectRoute to protect your routes and get the user information.
import type { Request } from 'express';
import { handleOnBehalfOf } from 'oauth-entra-id/nestjs';
import { Controller, Get, UseGuards, Req } from '@nestjs/common';
import { ProtectRoute } from '../guards/protect-route.guard';
@Controller('protected')
@UseGuards(ProtectRoute) // You can also apply the guard for specific routes, controller, or globally (you would need to write the logic in the guard)
export class ProtectedController {
constructor() {}
@Get('user-info')
getUserInfo(@Req() req: Request) {
return { message: 'Protected route :)', user: req.userInfo };
}
@Post('on-behalf-of')
async onBehalfOf(@Req() req: Request, @Res() res: Response) {
await handleOnBehalfOf(req, res);
}
}Demo Apps π
You can explore the demo apps to see how to integrate the package into your applications.
React Demo App π₯οΈ - React 19 frontend showcasing best practices for frontend integration in an OAuth 2.0 cookie-based flow.
React 19, React Router, TanStack Query (React Query), TanStack Form, Zustand, Tailwind, ShadCN Components, Axios and Zod.
Express Demo App π«- Express server, implements
oauth-entra-id/expressfor authentication.NestJS Demo App πͺΊ - NestJS server, implements
oauth-entra-id/nestjsfor authentication.HonoJS Demo App π₯ - HonoJS server, implements authentication using the core utilities of the package.
Fastify Demo App β‘ - Fastify server, implements authentication using the core utilities of the package.
In each server demo you get a fully working server and these features implemented: Auth flows and protected routes, Input validation, HTTP Security Headers, CORS, Rate limiting, Logging, Error handling, and more.
Architecture ποΈ
Browser Authentication Flow πͺ

Browser Middleware Flow β

Browser On-Behalf-Of Flow π

Mobile Authentication Flow πͺ

Mobile Middleware Flow β

Notesβ
- CORS: Make sure to set the
credentialsoption totruein your CORS configuration. This allows cookies to be sent with cross-origin requests. - Express and NestJS Exports: The package exports handleX functions for Express and NestJS. They work on a cookie-based session only. If you want to use bearer tokens, you need to implement your own logic using the core package.
- TSConfig: Make sure you set the
moduleis notcommonjsin yourtsconfig.json. Our recommendation is to setmoduletonode16andtargettoes6. - NestJS: The package uses the
expressinstance of NestJS, so make sure to use theexpressinstance for the package to work, or use the core utilities. - Frontend Cookies - Make sure to include credentials with every request from the frontend to the backend.
// Fetch API
fetch('http://localhost:3000/protected/user-info', {
method: 'GET',
credentials: 'include',
});
// Axios
const axiosInstance = axios.create({
withCredentials: true,
});
axiosInstance.get('http://localhost:3000/protected/user-info');License π
This project is licensed under the MIT License.
Thank you!