Replies: 33 comments 31 replies
-
Thank you for the update. |
Beta Was this translation helpful? Give feedback.
-
@jennyf19 3 weeks pass and still nothing about @azure/node-token-validation? Is it not even possible to share some under development package? |
Beta Was this translation helpful? Give feedback.
-
@jennyf19 Something new in topic of replacement? |
Beta Was this translation helpful? Give feedback.
-
Any news here? |
Beta Was this translation helpful? Give feedback.
-
Never would expect from MS to deprecated a library before creating a replacement, unbelievable |
Beta Was this translation helpful? Give feedback.
-
Has there been any progress on this front? |
Beta Was this translation helpful? Give feedback.
-
Also - although this will be like talking to a brick wall, you said:
The fact that there have been 6 months of dead air in this discussion indicates that we are not in any way "going through this process together". |
Beta Was this translation helpful? Give feedback.
-
Does anyone have a link to an explanation of why this package was deprecated? Like, are there known vulnerabilities? Or is it just deprecated because it's abandonware and it therefore should not be trusted? |
Beta Was this translation helpful? Give feedback.
-
It seems like a very bad break from best practices to "deprecate" a node module without actually deprecating it in npm, especially when it's still being downloaded ~150k a week. |
Beta Was this translation helpful? Give feedback.
-
@jennyf19 are you or anyone else able to speak to progress that has been made? |
Beta Was this translation helpful? Give feedback.
-
@jennyf19 Randomly seeing the npm package of passport-azure-ad as deprecated killed me. I see that this message hangs for one year almost. So, is there any updates? And what is the current solution for the developers that has been using passport-azure-ad until getting surprised by npm deprecate warning? |
Beta Was this translation helpful? Give feedback.
-
Nope. `MSAL.js` is for acquiring tokens. `passport-azure-ad` was for
validating them.
…On Wed, Jul 24, 2024 at 8:30 AM zWaR ***@***.***> wrote:
@RomanKonopelkoVoypost <https://github.com/RomanKonopelkoVoypost>
@jennyf19 <https://github.com/jennyf19> I wonder the same. I found this:
https://github.com/AzureAD/microsoft-authentication-library-for-js
However, I haven't used it. I wonder if that's meant to be the replacement
and if so, how does the migration to it look like?
—
Reply to this email directly, view it on GitHub
<#2405 (reply in thread)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/ACTMQBLJZKIQ67Q4I2L6W3DZN7CALAVCNFSM6AAAAAA4B3HQLWVHI2DSMVQWIX3LMV43URDJONRXK43TNFXW4Q3PNVWWK3TUHMYTAMJTHE2TEOI>
.
You are receiving this because you commented.Message ID:
<AzureAD/microsoft-identity-web/repo-discussions/2405/comments/10139529@
github.com>
|
Beta Was this translation helpful? Give feedback.
-
A year has passed and there is no news. |
Beta Was this translation helpful? Give feedback.
-
My friends any news on that? |
Beta Was this translation helpful? Give feedback.
-
Beta Was this translation helpful? Give feedback.
-
Has it been abandoned? We are experimenting with integrating MS AD into our NodeJS app, but it proves to be a struggle at every step |
Beta Was this translation helpful? Give feedback.
-
Seems to be abandoned and no news about the future.. |
Beta Was this translation helpful? Give feedback.
-
Hi everyone, Since the It provides secure access to Microsoft services and APIs using OAuth 2.0 and OpenID Connect standards, and it’s actively maintained by Microsoft. Implementation steps1. Set Up Your Azure AD Application
2. Install MSAL Node PackageInstall the MSAL Node package in your NodeJS project: npm install @azure/msal-node express-session 3. Set Up MSAL in Your NodeJS AppSample Code for NodeJS Authentication with MSALNot suitable for production usage, only for developement/experimentation purposes // server.js
const express = require('express');
const session = require('express-session');
const { ConfidentialClientApplication } = require('@azure/msal-node');
const app = express();
const PORT = process.env.PORT || 3000;
// MSAL configuration
const msalConfig = {
auth: {
clientId: process.env.OAUTH2_CLIENT_ID,
authority: `https://login.microsoftonline.com/${process.env.TENANT_ID}`, // Tenant ID or common for multi-tenant
clientSecret: process.env.OAUTH2_CLIENT_SECRET,
},
};
const msalClient = new ConfidentialClientApplication(msalConfig);
// Configure session middleware
app.use(
session({
secret: process.env.SESSION_SECRET_KEY || 'default_secret',
resave: false,
saveUninitialized: false,
cookie: { secure: false }, // Set to true if using HTTPS
})
);
// Login route - redirects to Azure AD for authentication
app.get('/login', (req, res) => {
const authCodeUrlParameters = {
scopes: ['user.read'],
redirectUri: 'http://localhost:3000/auth/redirect', // Adjust the redirect URI
};
// Redirect to Microsoft login page
msalClient.getAuthCodeUrl(authCodeUrlParameters).then((response) => {
res.redirect(response);
}).catch((error) => {
console.error('Error generating auth code URL:', error);
res.status(500).send('Error during login');
});
});
// Callback route - handles Azure AD redirect with authorization code
app.get('/auth/redirect', (req, res) => {
const tokenRequest = {
code: req.query.code, // Authorization code from query params
scopes: ['user.read'],
redirectUri: 'http://localhost:3000/auth/redirect', // Must match exactly with Azure AD config
};
// Exchange authorization code for tokens
msalClient.acquireTokenByCode(tokenRequest).then((response) => {
req.session.user = {
username: response.account.username,
name: response.account.name,
idToken: response.idToken,
};
res.redirect('/profile');
}).catch((error) => {
console.error('Error acquiring token by code:', error);
res.status(500).send('Error during token acquisition');
});
});
// Protected route example
app.get('/profile', (req, res) => {
if (!req.session.user) {
return res.redirect('/login');
}
res.send(`Welcome, ${req.session.user.name}`);
});
// Logout route
app.get('/logout', (req, res) => {
req.session.destroy((err) => {
if (err) {
console.error('Error during logout:', err);
return res.status(500).send('Error during logout');
}
res.redirect('/');
});
});
app.listen(PORT, () => {
console.log(`Server is running on http://localhost:${PORT}`);
}); Explanation of Key Components
Advantages of Using MSAL
Final Steps
It worked for me ;) Hope this helps!!! |
Beta Was this translation helpful? Give feedback.
-
Dear community, I have found a "drop-in replacement" for the deprecated
This solution supports the Bearer token approach of Here is the code before the replacement: const { BearerStrategy } = require('passport-azure-ad');
const tenantId = process.env.AD_APPLICATION_TENANT_ID;
const clientId = process.env.AD_APPLICATION_CLIENT_ID;
const identityMetadata = `https://login.microsoftonline.com/${tenantId}/v2.0/.well-known/openid-configuration`;
const issuer = `https://login.microsoftonline.com/${tenantId}/v2.0`;
const options = {
identityMetadata: identityMetadata,
clientID: clientId,
validateIssuer: true,
issuer: issuer,
};
async function verifyCallBack(token, done) {
// do some stuff
return done(null, token, token);
}
const bearerStrategy = new BearerStrategy(options, verifyCallBack);
module.exports = {
bearerStrategy
}; And here is the new "drop-in replacement" code: const { Strategy, ExtractJwt } = require('passport-jwt');
const jwksRsa = require('jwks-rsa');
// The tenant ID is the Azure AD tenant ID. The client ID is the application ID of the app registration.
const tenantId = process.env.AD_APPLICATION_TENANT_ID;
const clientId = process.env.AD_APPLICATION_CLIENT_ID;
// This is the URL where the public keys for the tenant are stored.
const jwksUri = `https://login.microsoftonline.com/${tenantId}/discovery/v2.0/keys`;
// This issuer and audience are used for API scope with MSAL
const issuer1 = `https://sts.windows.net/${tenantId}/`;
const audience1 = `api://${clientId}`;
// This issuer and audience are used if authentication is enabled on App Service with token cache.
const issuer2 = `https://login.microsoftonline.com/${tenantId}/v2.0`;
const audience2 = `${clientId}`;
// The options for the JWT strategy
const options = {
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
audience: [audience1, audience2],
issuer: [issuer1, issuer2],
algorithms: ['RS256'],
secretOrKeyProvider: jwksRsa.passportJwtSecret({
cache: true,
rateLimit: true,
jwksRequestsPerMinute: 5,
jwksUri: jwksUri,
}),
};
// This function is called when the token is verified
async function verifyCallBack(token, done) {
// do something with the token
return done(null, token);
}
const bearerStrategy = new Strategy(options, verifyCallBack);
module.exports = { bearerStrategy }; This replacement should help anyone who still needs the Bearer token validation functionality after |
Beta Was this translation helpful? Give feedback.
-
Here's a way to do it in express without using passport (based on @DanielOverdevestPZH 's approach): import { expressjwt, Request as RequestJWT } from "express-jwt"
import type { Request } from "express"
import jwksRsa, { GetVerificationKey } from "jwks-rsa"
// needed for type inference
import "express-unless"
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
export function buildExpressMsalToken({
tenantId,
clientId
}: {
tenantId: string
clientId: string
}) {
// This is the URL where the public keys for the tenant are stored.
const jwksUri = `https://login.microsoftonline.com/${tenantId}/discovery/v2.0/keys`
// This issuer and audience are used for API scope with MSAL
const issuer1 = `https://sts.windows.net/${tenantId}/`
const audience1 = `api://${clientId}`
// This issuer and audience are used if authentication is enabled on App Service with token cache.
const issuer2 = `https://login.microsoftonline.com/${tenantId}/v2.0`
const audience2 = `${clientId}`
return expressjwt({
secret: jwksRsa.expressJwtSecret({
jwksUri: jwksUri,
cache: true,
rateLimit: true,
jwksRequestsPerMinute: 5
}) as GetVerificationKey,
audience: [audience1, audience2],
issuer: [issuer1, issuer2],
algorithms: ["RS256"],
credentialsRequired: true
})
}
export type RequestWithMsalAuth = {
auth?: RequestJWT["auth"] & {
name?: string
unique_name?: string
upn?: string
oid?: string
scp?: string
roles?: string[]
}
} & Request use it as follows, for example: const app = express()
const port = 3000
const tenantId = process.env.TENANT_ID
if (!tenantId) {
throw new Error(
"TENANT_ID not defined."
)
}
const clientId = process.env.CLIENT_ID
if (!clientId) {
throw new Error(
"CLIENT_ID not defined."
)
}
app.use(buildExpressMsalToken({tenantId,clientId})
app.get(
"",
(req: RequestWithMsalAuth, res, next) => {
// do stuff in your controller, req.auth will be typed
}
)
app.listen(port, () => {
console.log(`Server running on port ${port}`)
}) |
Beta Was this translation helpful? Give feedback.
-
Note that if you want to use DanielOverdevestPZH's solution to validate tokens issued by Azure AD B2C, you'll need to change your configuration like so: // This is the URL where the public keys for the tenant are stored.
const jwksUri = `https://${tenant_name}.b2clogin.com/${tenant_name}.onmicrosoft.com/${user_flow}/discovery/v2.0/keys`;
// This issuer and audience are used for API scope with MSAL
const issuer1 = `https://${tenant_name}.b2clogin.com/${tenantId}/v2.0/`;
const audience1 = clientId;
// The options for the JWT strategy
const options = {
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
audience: [audience1],
issuer: [issuer1],
algorithms: ["RS256"],
secretOrKeyProvider: jwksRsa.passportJwtSecret({
cache: true,
rateLimit: true,
jwksRequestsPerMinute: 5,
jwksUri: jwksUri,
}),
} Where:
|
Beta Was this translation helpful? Give feedback.
-
FWIW if anyone is using Fastify fastify-jwt-jwks works pretty well. This is a basic example of adding it as a plugin.
|
Beta Was this translation helpful? Give feedback.
-
Still no official replacement from Microsoft? It's 2025, and they deprecated https://github.com/AzureAD/passport-azure-ad in Aug. 2023. |
Beta Was this translation helpful? Give feedback.
-
Hi community, I saw the implementation of passport-azure-ad and it's using passport-http-bearer This is how I replace the implementation: import { Injectable, UnauthorizedException } from '@nestjs/common'; @Injectable() constructor(private configService: EnvironmentConfigServiceService) {
} async validate(token: string): Promise {
} export const AzureADGuard = AuthGuard('azure-ad'); Perhaps it's useful for someone looking for an alternative |
Beta Was this translation helpful? Give feedback.
-
Somewhat concerning that Microsoft is still referencing this deprecated library in "how to" information: (which was written or updated after it was already deprecated) |
Beta Was this translation helpful? Give feedback.
-
A few notes on this issue and a link to the start of a modern option for express (which I hope becomes unnecessary when MSFT provides a package.) The solutions provided by @DanielOverdevestPZH, me and others in this thread are good in the sense that they provide a simple, drop-in way to do basic MSAL token validation. But, like a lot of things, they suffer from what I think of as the curl rewrite problem, i.e., it's pretty easy to write something to handle the simple, happy-path flow, but much much harder to capture every use case. To illustrate a few possible problems with the approaches outlined here:
All of which is to say that, while drop-in code snippets are (extremely) helpful, they aren't a total solution, and a library really is needed. I have started such a library here. It currently acheives the same kind of basic validation as the drop-in snippets above but also:
It's only dependencies are There is also a lot of room for improvement. I'm not sure how much time I will have to do that (it's not a huge need in our organization), but am happy to try. Feel free to post issues/thoughts on the repo. |
Beta Was this translation helpful? Give feedback.
-
Hey everyone, I’ve put together a comprehensive tutorial on implementing authentication with Passport.js and Azure in 2025. If you’re interested, feel free to check it out here: Complete Guide to Authentication with Passport.js & Azure in 2025. Hope it helps, and I’d love to hear your thoughts! |
Beta Was this translation helpful? Give feedback.
-
Here is some working code based on an official msal example if this helps anyone. One thing I can't figure out is why the issuer doesn't match, but that is another issue. Hope this helps. This should really be built into the library. Kind of a joke that it is not. /**
* Validates the ID token and checks the nonce
* @param idToken - The ID token to validate
* @param nonce - The nonce to check against
* @returns The decoded token payload
*/
async function validateIdToken(idToken: string, nonce: string): Promise<JwtPayload> {
const config = getConfig();
const clientId = config.microsoftIdentity?.clientId;
if (!clientId) {
throw new Error('Microsoft identity client ID not set in config');
}
const client = jwksClient({
jwksUri: 'https://login.microsoftonline.com/common/discovery/v2.0/keys',
});
const getKey = (header: JwtHeader, callback: SigningKeyCallback): void => {
client.getSigningKey(header.kid, (err: Error | null, key: jwksClient.SigningKey | undefined) => {
if (err) {
callback(err);
return;
}
const signingKey = key?.getPublicKey();
callback(null, signingKey);
});
};
return new Promise((resolve, reject) => {
verify(
idToken,
getKey,
{
audience: clientId,
// TODO: figure out why you can't use the common issuer. How do you specify the tenant?
// If uncommented, will get errors that the issue is incorrect, as the
// returned issuer is some random tenant that I can't even find on the application page in azure.
// issuer: 'https://login.microsoftonline.com/common/v2.0',
},
(err, decoded) => {
if (err) {
reject(new Error(`Token validation failed: ${err.message}`));
return;
}
if (!decoded) {
reject(new Error('Token validation failed: no payload'));
return;
}
if ((decoded as JwtPayload).nonce !== nonce) {
reject(new Error('Nonce mismatch'));
return;
}
resolve(decoded as JwtPayload);
}
);
});
} |
Beta Was this translation helpful? Give feedback.
-
I spent some time figuring out how to implement this for a use case that doesn't use express (or passport). The goal was minimal dependencies and custom code (especially for the security sensitive parts such as JWT verification). The custom code is mainly for extracting the JWT as a first step, largely adapted from // extract JWT
/**
* Implementation of Auth header parsing adapted from https://github.com/mikenicholson/passport-jwt/blob/fed94fa005c5b2dcb7e6d5d5372e3b20cae898f1/lib/extract_jwt.js#L70-L72
*/
import type { IncomingMessage } from 'node:http'
export const fromAuthHeaderAsBearerToken = (request: IncomingMessage): string | null => {
const headerValue = request.headers.authorization
if (headerValue === undefined) {
return null
}
const authParams = parseAuthHeader(headerValue)
return authParams && authParams.scheme.toLowerCase() === 'bearer' ? authParams.value : null
}
const AUTH_HEADER_REGEX = /(\S+)\s+(\S+)/
function parseAuthHeader(value: string) {
const matches = value.match(AUTH_HEADER_REGEX)
return matches && { scheme: matches[1], value: matches[2] }
}
// setup JWKS
import { createRemoteJWKSet, type JWTVerifyGetKey } from 'jose'
async function getJwksUri(openIdConfigurationDocumentUrl: string) {
return (await fetch(openIdConfigurationDocumentUrl).then((res) => res.json())).jwks_uri
}
export const createRemoteJwks = async (authorityUrl: string): Promise<JWTVerifyGetKey> =>
createRemoteJWKSet(
new URL(await getJwksUri(authorityUrl + '.well-known/openid-configuration')),
)
// server code
import { jwtVerify } from 'jose'
const config = {
authorityUrl: 'https://your-adfs-server.com',
expectedClaims: {
audience: 'https://this-resource-server.com',
issuer: 'https://your-adfs-server.com/services/trust',
},
}
const jwks = await createRemoteJwks(config.authorityUrl) // create as a singleton, since it memoizes network requests
async function isRequestAuthorized(request: IncomingMessage) {
try {
const jwt = fromAuthHeaderAsBearerToken(request)
if (jwt === null) {
throw new Error('Unable to extract Bearer token from Authorization header')
}
await jwtVerify(jwt, jwks, { ...config.expectedClaims })
} catch (e) {
console.info(e, 'Failed to authorize request')
return false
}
return true
} Then you would have your server call You can also easily change the method of extracting the JWT from the request, by changing the call to For the record, probably the best approach would be to use this library function: https://github.com/panva/oauth4webapi/blob/v2.12.2/docs/functions/validateJwtAccessToken.md This would effectively bring the validation code down to one function call. However, the catch is in the docs:
Access tokens issued by AD FS are unfortunately non-compliant with the spec (for example missing the It is probably possible to get this working by configuring custom claims for the access token issuance in your AD FS config. If someone manages to get that working please tag me. |
Beta Was this translation helpful? Give feedback.
-
The fact that's it's been 2 years and no updates it's pretty safe to assume it's 100% abandoned with no alternative in the future, I wouldn't expect much less from Microsoft what a joke of a company. For those using nestjs I incorporated @loucadufault implementation with a custom passport strategy based on the passport-azure-ad import { Logger } from '@nestjs/common';
import passport from 'passport';
import { Strategy } from 'passport';
import { createRemoteJWKSet, type JWTVerifyGetKey } from 'jose';
import { jwtVerify } from 'jose';
const CLOCK_SKEW = 300; // 5 minutes
const POLICY_REGEX = /^b2c_1a?_[0-9a-z._-]+$/i; // policy is case insensitive
const AUTH_HEADER = 'authorization';
const ACCEPTED_AUTH_SCHEME = 'Bearer';
const AUTH_HEADER_REGEX = /(\S+)\s+(\S+)/;
export class BearerStrategy extends Strategy {
private logger = new Logger(BearerStrategy.name);
name: string;
_verify: any;
_options: any;
constructor(options, verifyFn) {
super();
this.name = 'oauth-bearer';
if (!options) throw new Error('In BearerStrategy constructor: options is required');
if (!verifyFn || typeof verifyFn !== 'function') {
throw new Error('In BearerStrategy constructor: verifyFn is required and it must be a function');
}
this._verify = verifyFn;
// Initialize options with defaults
this._options = this._initializeOptions(options);
// Validate options
this._validateOptions(options);
}
_initializeOptions(options) {
// Clock skew validation
if (
options.clockSkew &&
(typeof options.clockSkew !== 'number' || options.clockSkew <= 0 || options.clockSkew % 1 !== 0)
)
throw new Error('clockSkew must be a positive integer');
options.clockSkew = options.clockSkew || CLOCK_SKEW;
options.passReqToCallback = options.passReqToCallback === true;
options.validateIssuer = options.validateIssuer !== false;
options.allowMultiAudiencesInToken = options.allowMultiAudiencesInToken === true;
options.isB2C = options.isB2C === true;
// Handle audience
if (options.audience && typeof options.audience === 'string') {
options.audience = [options.audience];
} else if (!options.audience || !Array.isArray(options.audience) || options.length === 0) {
options.audience = [options.clientID, 'spn:' + options.clientID];
}
// Handle issuer
if (options.issuer === '') options.issuer = null;
if (options.issuer && Array.isArray(options.issuer) && options.issuer.length === 0) options.issuer = null;
if (options.issuer && !Array.isArray(options.issuer)) options.issuer = [options.issuer];
return options;
}
_isHttpsUri(uri) {
return uri && uri.indexOf('https://') === 0;
}
_validateOptions(options) {
if (!options.clientID || options.clientID === '')
throw new Error('In BearerStrategy constructor: clientID cannot be empty');
if (!options.identityMetadata || !this._isHttpsUri(options.identityMetadata))
throw new Error('In BearerStrategy constructor: identityMetadata must be provided and must be a https url');
if (options.scope && (!Array.isArray(options.scope) || options.scope.length === 0))
throw new Error('In BearerStrategy constructor: scope must be a non-empty array');
// B2C validation
if (options.isB2C) {
if (!options.policyName || !POLICY_REGEX.test(options.policyName))
throw new Error('In BearerStrategy constructor: invalid policy for B2C');
}
// Common endpoint detection
options._isCommonEndpoint = options.identityMetadata.indexOf('/common/') != -1;
if (!options.validateIssuer) {
this.logger.warn('Production environments should always validate the issuer.');
}
}
async _createRemoteJwks(identityMetadata: string): Promise<JWTVerifyGetKey> {
return await createRemoteJWKSet(new URL(await this._getJwksUriFromOpenIdConfiguration(identityMetadata)));
}
_parseAuthHeader(value: string) {
const matches = value.match(AUTH_HEADER_REGEX);
return matches && { scheme: matches[1], value: matches[2] };
}
_fromAuthHeaderAsBearerToken(request: Request): string | null {
const headerValue = request.headers[AUTH_HEADER];
if (headerValue === undefined) {
return null;
}
const authParams = this._parseAuthHeader(headerValue);
if (authParams && ACCEPTED_AUTH_SCHEME.toLowerCase() === authParams.scheme.toLowerCase()) {
return authParams.value;
}
return null;
}
async _getJwksUriFromOpenIdConfiguration(identityUri: string): Promise<string> {
const openIdConfigurationDocument = await fetch(identityUri).then((res) => res.json());
return openIdConfigurationDocument.jwks_uri;
}
_validatePayloadContent(payload, options) {
const hasCommonElem = (array1, array2) => {
for (let i = 0; i < array1.length; i++) {
if (array2.indexOf(array1[i]) !== -1) return true;
}
return false;
};
// (1) issuer
// - check the existence and the format of payload.iss
// - validate if options.issuer is set
if (typeof payload.iss !== 'string' || payload.iss === '') return new Error('invalid iss value in payload');
if (options.validateIssuer !== false) {
if (
!options.issuer ||
options.issuer === '' ||
(Array.isArray(options.issuer) && options.issuer.length === 0)
)
return new Error('options.issuer is missing');
let valid = false;
if (Array.isArray(options.issuer)) valid = options.issuer.indexOf(payload.iss) !== -1;
else valid = options.issuer === payload.iss;
if (!valid) return new Error('jwt issuer is invalid');
}
// (2) subject (id_token only. We don't check subject for access_token)
// - check the existence and the format of payload.sub
// - validate if options.subject is set
if (options.isAccessToken !== true) {
if (typeof payload.sub !== 'string' || payload.sub === '') {
return new Error('invalid sub value in payload');
}
if (options.subject && options.subject !== payload.sub)
return new Error('jwt subject is invalid. expected');
}
// (3) audience
// - always validate
// - allow payload.aud to be an array of audience
// - options.audience must be a string
// - if there are multiple audiences, then azp claim must exist and must equal client_id
if (typeof options.audience === 'string') options.audience = [options.audience, 'spn:' + options.audience];
if (options.allowMultiAudiencesInToken === false && Array.isArray(payload.aud) && payload.aud.length > 1)
return new Error('multiple audience in token is not allowed');
const payload_audience = Array.isArray(payload.aud) ? payload.aud : [payload.aud];
if (!hasCommonElem(options.audience, payload_audience)) return new Error('jwt audience is invalid');
if (payload_audience.length > 1) {
if (typeof payload.azp !== 'string' || payload.azp !== options.clientID)
return new Error('jwt azp is invalid');
}
// (4) expiration
// - check the existence and the format of payload.exp
// - validate unless options.ignoreExpiration is set true
if (typeof payload.exp !== 'number') return new Error('invalid exp value in payload');
if (!options.ignoreExpiration) {
if (Math.floor(Date.now() / 1000) >= payload.exp + options.clockSkew) {
return new Error('jwt is expired');
}
}
// (5) nbf
// - check if it exists
if (payload.nbf) {
if (typeof payload.nbf !== 'number') return new Error('nbf value in payload is not a number');
if (payload.nbf >= payload.exp) return new Error('nbf value in payload is not before the exp value');
if (payload.nbf >= Math.floor(Date.now() / 1000) + options.clockSkew) return new Error('jwt is not active');
}
}
async authenticate(this: passport.StrategyCreated<this, this & passport.StrategyCreatedStatic>, req) {
try {
const jwks = await this._createRemoteJwks(this._options.identityMetadata);
const jwt = this.fromAuthHeaderAsBearerToken(req);
if (jwt === null) {
throw new Error('Unable to extract Bearer token from Authorization header');
}
const res = await jwtVerify(jwt, jwks, {});
console.log('res: ', res);
// check if no res payload and fail
if (!res.payload) return this.failWithLog(this, 'no payload in jwt');
this._validatePayloadContent(res.payload, this._options);
return this.success(res.payload);
} catch (error) {
console.log(error);
return this.failWithLog(this, error);
}
}
failWithLog(this: passport.StrategyCreated<this, this & passport.StrategyCreatedStatic>, message) {
this.logger.log(`authentication failed due to: ${JSON.stringify(message)}`);
return this.fail(message);
}
} import { Injectable } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { BearerStrategy } from './bearer.strategy';
@Injectable()
export class JwtStrategy extends PassportStrategy(BearerStrategy, 'azure-ad') {
constructor() {
const issuer = `https://login.microsoftonline.com/{tenant ID}/v2.0`;
const jwksUri = `https://login.microsoftonline.com/{tenant ID}/v2.0/.well-known/openid-configuration`;
super({
identityMetadata: jwksUri,
issuer: issuer,
clientID: process.env.AD_CLIENT_ID,
audience: process.env.AD_CLIENT_ID,
loggingLevel: process.env.JWT_LOG_LEVEL,
loggingNoPII: false
});
}
async validate(payload: unknown): Promise<any> {
return payload;
}
} |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
Node JS validation replacement for passport.js
Dear Node JS customers,
We understand that many of you have been frustrated by the lack of a replacement for this deprecated passport.js library. We want you to know that we hear your concerns and are actively working on a solution. In the meantime, we want to assure you that we haven't forgotten about you. Auth validation is very complicated and hard to get right. For this reason, we will leverage Microsoft.IdentityModel, which has a proven track record over many years, and is hitting impressive perf metrics with the latest improvements in IdentityModel 7x on .NET 8, making Ahead of Time (AOT) comparable with non-AOT, especially in terms of request per second, which is very promising for our goal of covering all services, regardless of language with Microsoft.IdentityModel.
We are experimenting with a simple wrapper over a shared library in Node JS that will allow you to continue using the Microsoft identity platforms. This wrapper is currently only available to internal Microsoft services, but we're working hard to make it available to all of our customers as soon as possible. Thank you for your patience and understanding as we work to improve our offerings. We'll keep you updated as we make progress on this front. Please provide us with your feedback/questions/concerns as we go through this process together.
Beta Was this translation helpful? Give feedback.
All reactions