Introduction
Microsoft Entra ID (formerly Azure Active Directory) is Microsoft’s cloud identity solution. In 2026, enterprises require centralized authentication, short-lived tokens, Conditional Access, and fine-grained permission management. This tutorial walks you through securing a Node.js/TypeScript API with Entra ID step by step. You will learn how to register an application, configure scopes, issue JWT tokens, and validate them server-side. Each step includes ready-to-use code and concrete explanations to help you avoid common production configuration mistakes.
Prerequisites
- Azure account with Global Administrator or Application Administrator permissions
- Node.js 20+ and TypeScript 5.4+
- Solid knowledge of JWT and OAuth 2.0
- Azure CLI installed (version 2.60+)
Create the Application in Entra ID
az login
az ad app create --display-name "MonAPI-Production" --sign-in-audience "AzureADMyOrg" --is-fallback-public-client falseThis command creates the Entra ID application. Note the returned AppId and ObjectId. Always use AzureADMyOrg for enterprise environments and avoid uncontrolled multi-tenant applications.
Define Scopes and Permissions
{
"id": "00000000-0000-0000-0000-000000000000",
"appId": "votre-app-id",
"displayName": "MonAPI-Production",
"api": {
"requestedAccessTokenVersion": 2,
"oauth2PermissionScopes": [
{
"adminConsentDisplayName": "Accès complet à l'API",
"adminConsentDescription": "Permet à l'application d'accéder à toutes les fonctionnalités de l'API",
"userConsentDisplayName": "Accès à l'API",
"userConsentDescription": "Permet à l'application d'accéder à vos données via l'API",
"value": "access_as_user",
"type": "User",
"id": "11111111-1111-1111-1111-111111111111"
}
]
}
}This JSON manifest configures the 'access_as_user' scope. Import it with az ad app update --id your-app-id --app-manifest. Token version 2 is required to support modern claims.
MSAL Client-Side Configuration
import { Configuration } from '@azure/msal-browser';
export const msalConfig: Configuration = {
auth: {
clientId: 'votre-app-id',
authority: 'https://login.microsoftonline.com/votre-tenant-id',
redirectUri: 'http://localhost:3000/auth/callback'
},
cache: { cacheLocation: 'sessionStorage', storeAuthStateInCookie: false }
};This MSAL configuration uses the tenant-specific authority. Replace the IDs with your actual values. The sessionStorage cache limits token persistence.
Server-Side JWT Token Validation
import { jwtVerify } from 'jose';
import { createRemoteJWKSet } from 'jose';
const JWKS = createRemoteJWKSet(new URL('https://login.microsoftonline.com/votre-tenant-id/discovery/v2.0/keys'));
export async function validateToken(token: string) {
const { payload } = await jwtVerify(token, JWKS, {
issuer: 'https://login.microsoftonline.com/votre-tenant-id/v2.0',
audience: 'api://votre-app-id'
});
if (!payload.scp?.includes('access_as_user')) throw new Error('Scope insuffisant');
return payload;
}Strict validation with dynamic JWKS. Always verify the audience, issuer, and custom scope. This method rejects tokens issued for other applications.
Complete Express Middleware
import express from 'express';
import { validateToken } from './middleware/auth';
const app = express();
app.use(express.json());
app.use(async (req, res, next) => {
const authHeader = req.headers.authorization;
if (!authHeader?.startsWith('Bearer ')) return res.status(401).send('Token manquant');
try {
req.user = await validateToken(authHeader.split(' ')[1]);
next();
} catch (e) {
res.status(401).send('Token invalide');
}
});
app.get('/api/protected', (req, res) => res.json({ message: 'Accès autorisé', user: req.user }));
app.listen(4000);Express middleware that protects all routes. Validation errors return 401 without details to prevent information leakage.
Best Practices
- Always use version 2 tokens and specific scopes instead of broad roles
- Enable Conditional Access with MFA and location policies for critical applications
- Store secrets in Azure Key Vault, not in code
- Implement automatic key rotation via Entra ID
- Log only the necessary claims for debugging
Common Errors to Avoid
- Forgetting to set the exact audience in JWT validation
- Using v1 tokens instead of v2 with custom scopes
- Not configuring redirect URIs with HTTPS in production
- Ignoring token revocation on logout
Further Reading
Discover our advanced training on identity governance and Entra ID integration with Zero Trust architectures at Learni Group.