Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

passport-azure-ad

Package Overview
Dependencies
Maintainers
2
Versions
56
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

passport-azure-ad - npm Package Compare versions

Comparing version 4.3.1-beta.0 to 4.3.1

typings/browser.d.ts

6

CHANGELOG.md
<a name="4.0.0"></a>
# 4.3.1
## Bugs
- Replace `jwk-to-pem` with `node-jose` to remove dependency on `elliptic`: #3868
- Update `https-proxy-agent` to v5: #3996
# 4.3.0

@@ -4,0 +10,0 @@

204

lib/aadutils.js

@@ -1,34 +0,17 @@

/**
* Copyright (c) Microsoft Corporation
* All Rights Reserved
* MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this
* software and associated documentation files (the "Software"), to deal in the Software
* without restriction, including without limitation the rights to use, copy, modify,
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
* OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT
* OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/
'use strict';
const base64url = require('base64url');
const crypto = require('crypto');
const util = require('util');
"use strict";
exports.getLibraryProduct = () => { return 'passport-azure-ad' };
const base64url = require("base64url");
const crypto = require("crypto");
const util = require("util");
exports.getLibraryProduct = () => { return "passport-azure-ad" };
exports.getLibraryVersionParameterName = () => { return "x-client-Ver" };
exports.getLibraryProductParameterName = () => { return 'x-client-SKU' };
exports.getLibraryProductParameterName = () => { return "x-client-SKU" };
exports.getLibraryVersion = () => {
return '4.0.0';
return "4.3.0";
};

@@ -67,5 +50,5 @@

const headers = req.headers;
const protocol = (req.connection.encrypted || req.headers['x-forwarded-proto'] === 'https') ? 'https' : 'http';
const protocol = (req.connection.encrypted || req.headers["x-forwarded-proto"] === "https") ? "https" : "http";
const host = headers.host;
const path = req.url || '';
const path = req.url || "";
return `${protocol}://${host}${path}`;

@@ -108,3 +91,3 @@ };

exports.uid = (len) => {
var bytes = crypto.randomBytes(Math.ceil(len * 3 / 4));
const bytes = crypto.randomBytes(Math.ceil(len * 3 / 4));
return base64url.encode(bytes).slice(0,len);

@@ -115,3 +98,3 @@ };

const msb = hexStr[0];
if (msb < '0' || msb > '7') {
if (msb < "0" || msb > "7") {
return `00${hexStr}`;

@@ -130,5 +113,7 @@ }

// encode ASN.1 DER length field
// if <=127, short form
// if >=128, long form
/*
* encode ASN.1 DER length field
* if <=127, short form
* if >=128, long form
*/
function encodeLengthHex(n) {

@@ -145,7 +130,7 @@ if (n <= 127) {

exports.rsaPublicKeyPem = (modulusB64, exponentB64) => {
const modulus = new Buffer(modulusB64, 'base64');
const exponent = new Buffer(exponentB64, 'base64');
const modulus = new Buffer(modulusB64, "base64"); // eslint-disable-line security/detect-new-buffer -- Legacy code, Buffer needed
const exponent = new Buffer(exponentB64, "base64"); // eslint-disable-line security/detect-new-buffer -- Legacy code, Buffer needed
const modulusHex = prepadSigned(modulus.toString('hex'));
const exponentHex = prepadSigned(exponent.toString('hex'));
const modulusHex = prepadSigned(modulus.toString("hex"));
const exponentHex = prepadSigned(exponent.toString("hex"));

@@ -164,5 +149,5 @@ const modlen = modulusHex.length / 2;

const derB64 = new Buffer(encodedPubkey, 'hex').toString('base64');
const derB64 = new Buffer(encodedPubkey, "hex").toString("base64"); // eslint-disable-line security/detect-new-buffer -- Legacy code, Buffer needed
const pem = `-----BEGIN RSA PUBLIC KEY-----\n${derB64.match(/.{1,64}/g).join('\n')}\n-----END RSA PUBLIC KEY-----\n`;
const pem = `-----BEGIN RSA PUBLIC KEY-----\n${derB64.match(/.{1,64}/g).join("\n")}\n-----END RSA PUBLIC KEY-----\n`;

@@ -172,5 +157,7 @@ return pem;

// used for c_hash and at_hash validation
// case (1): content = access_token, hashProvided = at_hash
// case (2): content = code, hashProvided = c_hash
/*
* used for c_hash and at_hash validation
* case (1): content = access_token, hashProvided = at_hash
* case (2): content = code, hashProvided = c_hash
*/
exports.checkHashValueRS256 = (content, hashProvided) => {

@@ -181,11 +168,11 @@ if (!content)

// step 1. hash the content
var digest = crypto.createHash('sha256').update(content, 'ascii').digest();
const digest = crypto.createHash("sha256").update(content, "ascii").digest();
// step2. take the first half of the digest, and save it in a buffer
var buffer = new Buffer(digest.length/2);
for (var i = 0; i < buffer.length; i++)
const buffer = new Buffer(digest.length/2); // eslint-disable-line security/detect-new-buffer -- Legacy code, Buffer needed
for (let i = 0; i < buffer.length; i++)
buffer[i] = digest[i];
// step 3. base64url encode the buffer to get the hash
var hashComputed = base64url(buffer);
const hashComputed = base64url(buffer);

@@ -195,5 +182,7 @@ return (hashProvided === hashComputed);

// This function is used for handling the tuples containing nonce/state/policy/timeStamp in session
// remove the additional tuples from array starting from the oldest ones
// remove expired tuples in array
/*
* This function is used for handling the tuples containing nonce/state/policy/timeStamp in session
* remove the additional tuples from array starting from the oldest ones
* remove expired tuples in array
*/
exports.processArray = function(array, maxAmount, maxAge) {

@@ -205,5 +194,5 @@ // remove the additional tuples, start from the oldest ones

// count the number of those already expired
var count = 0;
for (var i = 0; i < array.length; i++) {
var tuple = array[i];
let count = 0;
for (let i = 0; i < array.length; i++) {
const tuple = array[i];
if (tuple.timeStamp + maxAge * 1000 <= Date.now())

@@ -220,6 +209,8 @@ count++;

// This function is used to find the tuple matching the given state, remove the tuple
// from the array and return the tuple
// @array - array of {state: x, nonce: x, policy: x, timeStamp: x} tuples
// @state - the tuple which matches the given state
/*
* This function is used to find the tuple matching the given state, remove the tuple
* from the array and return the tuple
* @array - array of {state: x, nonce: x, policy: x, timeStamp: x} tuples
* @state - the tuple which matches the given state
*/
exports.findAndDeleteTupleByState = (array, state) => {

@@ -229,5 +220,5 @@ if (!array)

for (var i = 0; i < array.length; i++) {
var tuple = array[i];
if (tuple['state'] === state) {
for (let i = 0; i < array.length; i++) {
const tuple = array[i];
if (tuple["state"] === state) {
// remove the tuple from the array

@@ -247,3 +238,3 @@ array.splice(i, 1);

for (var i = 0; i < fields.length; i++)
for (let i = 0; i < fields.length; i++)
dest[fields[i]] = source[fields[i]];

@@ -253,3 +244,3 @@ };

exports.getErrorMessage = (err) => {
if (typeof err === 'string')
if (typeof err === "string")
return err;

@@ -260,3 +251,3 @@ if (err instanceof Error)

// if not string or Error, we try to stringify it
var str;
let str;
try {

@@ -271,31 +262,36 @@ str = JSON.stringify(err);

exports.concatUrl = (url, rest) => {
if (typeof rest === 'string' || rest instanceof String) {
rest = [rest];
let validRest;
if (typeof rest === "string" || rest instanceof String) {
validRest = [rest];
} else {
validRest = rest;
}
if (!url) {
return `?${rest.join('&')}`;
return `?${validRest.join("&")}`;
}
var hasParam = url.indexOf('?') !== -1;
return rest ? url.concat(hasParam ? '&' : '?').concat(rest.join('&')) : url;
const hasParam = url.indexOf("?") !== -1;
return validRest ? url.concat(hasParam ? "&" : "?").concat(validRest.join("&")) : url;
};
// This is a list maintained by the AAD server team, will need to keep an eye on this
// as things change. Not ideal, but it is what it is
// in general, a change like this (adding a new cookie attribute)
// should be backward compatible as RFC 6265 specifies that browsers should
// ignore unknown cookie attributes. However, for the specific case of the
// SameSite attribute, some browsers incorrectly implemented the attribute or
// implemented an earlier draft which had contradictory behavior.
// For these browsers which attempted to support SameSite,
// but have bugs in their support, we want to omit the SameSite=None attribute.
// See Chromium official guidance here: https://www.chromium.org/updates/same-site/incompatible-clients
/*
* This is a list maintained by the AAD server team.
* In general, a change like this (adding a new cookie attribute)
* should be backward compatible as RFC 6265 specifies that browsers should
* ignore unknown cookie attributes. However, for the specific case of the
* SameSite attribute, some browsers incorrectly implemented the attribute or
* implemented an earlier draft which had contradictory behavior.
* For these browsers which attempted to support SameSite,
* but have bugs in their support, we want to omit the SameSite=None attribute.
* See Chromium official guidance here: https://www.chromium.org/updates/same-site/incompatible-clients
*/
exports.sameSiteNotAllowed = userAgent => {
// Cover all iOS based browsers here. This includes:
// - Safari on iOS 12 for iPhone, iPod Touch, iPad
// - WkWebview on iOS 12 for iPhone, iPod Touch, iPad
// - Chrome on iOS 12 for iPhone, iPod Touch, iPad
// All of which are broken by SameSite=None, because they use the iOS networking stack
/*
* Cover all iOS based browsers here. This includes:
* - Safari on iOS 12 for iPhone, iPod Touch, iPad
* - WkWebview on iOS 12 for iPhone, iPod Touch, iPad
* - Chrome on iOS 12 for iPhone, iPod Touch, iPad
* All of which are broken by SameSite=None, because they use the iOS networking stack
*/
if (userAgent.includes("CPU iPhone OS 12") || userAgent.includes("iPad; CPU OS 12")) {

@@ -305,9 +301,11 @@ return true;

// Cover Mac OS X based browsers that use the Mac OS networking stack. This includes:
// - Safari on Mac OS X
// - Internal browser on Mac OS X
// This does not include:
// - Chrome on Mac OS X
// - Chromium on Mac OS X
// Because they do not use the Mac OS networking stack.
/*
* Cover Mac OS X based browsers that use the Mac OS networking stack. This includes:
* - Safari on Mac OS X
* - Internal browser on Mac OS X
* This does not include:
* - Chrome on Mac OS X
* - Chromium on Mac OS X
* Because they do not use the Mac OS networking stack.
*/
if (userAgent.includes("Macintosh; Intel Mac OS X 10_14") && !userAgent.includes("Chrome/") && !userAgent.includes("Chromium")) {

@@ -317,5 +315,7 @@ return true;

// Cover Chrome 50-69, because some versions are broken by SameSite=None, and none in this range require it.
// Note: this covers some pre-Chromium Edge versions, but pre-Chromim Edge does not require SameSite=None, so this is fine.
// Note: this regex applies to Windows, Mac OS X, and Linux, deliberately.
/*
* Cover Chrome 50-69, because some versions are broken by SameSite=None, and none in this range require it.
* Note: this covers some pre-Chromium Edge versions, but pre-Chromim Edge does not require SameSite=None, so this is fine.
* Note: this regex applies to Windows, Mac OS X, and Linux, deliberately.
*/
if (userAgent.includes("Chrome/5") || userAgent.includes("Chrome/6")) {

@@ -325,4 +325,6 @@ return true;

// Unreal Engine runs Chromium 59, but does not advertise as Chrome until 4.23. Treat versions of Unreal
// that don't specify their Chrome version as lacking support for SameSite=None.
/*
* Unreal Engine runs Chromium 59, but does not advertise as Chrome until 4.23. Treat versions of Unreal
* that don't specify their Chrome version as lacking support for SameSite=None.
*/
if (userAgent.includes("UnrealEngine") && !userAgent.includes("Chrome")) {

@@ -332,5 +334,7 @@ return true;

// UCBrowser < 12.13.2 ignores Set-Cookie headers with SameSite=None.
// NB: this rule isn't complete - you need regex to make a complete rule.
// See: https://www.chromium.org/updates/same-site/incompatible-clients
/*
* UCBrowser < 12.13.2 ignores Set-Cookie headers with SameSite=None.
* NB: this rule isn't complete - you need regex to make a complete rule.
* See: https://www.chromium.org/updates/same-site/incompatible-clients
*/
if (userAgent.includes("UCBrowser/12") || userAgent.includes("UCBrowser/11")) {

@@ -337,0 +341,0 @@ return true;

@@ -1,42 +0,25 @@

/**
* Copyright (c) Microsoft Corporation
* All Rights Reserved
* MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this
* software and associated documentation files (the 'Software'), to deal in the Software
* without restriction, including without limitation the rights to use, copy, modify,
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
* OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT
* OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/
'use strict';
"use strict";
/* eslint no-underscore-dangle: 0 */
const async = require('async');
const cacheManager = require('cache-manager');
const jws = require('jws');
const passport = require('passport');
const util = require('util');
const async = require("async");
const cacheManager = require("cache-manager");
const jws = require("jws");
const passport = require("passport");
const util = require("util");
const aadutils = require('./aadutils');
const CONSTANTS = require('./constants');
const jwt = require('./jsonWebToken');
const Metadata = require('./metadata').Metadata;
const Log = require('./logging').getLogger;
const UrlValidator = require('valid-url');
const aadutils = require("./aadutils");
const CONSTANTS = require("./constants");
const jwt = require("./jsonWebToken");
const Metadata = require("./metadata").Metadata;
const Log = require("./logging").getLogger;
const UrlValidator = require("valid-url");
const log = new Log('AzureAD: Bearer Strategy');
const memoryCache = cacheManager.caching({ store: 'memory', max: 3600, ttl: 1800 /* seconds */ });
const log = new Log("AzureAD: Bearer Strategy");
const memoryCache = cacheManager.caching({ store: "memory", max: 3600, ttl: 1800 /* seconds */ });
const ttl = 1800; // 30 minutes cache

@@ -69,3 +52,2 @@

*
*
* Options:

@@ -184,8 +166,8 @@ *

passport.Strategy.call(this);
this.name = 'oauth-bearer'; // Me, a name I call myself.
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');
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");

@@ -195,9 +177,9 @@ this._verify = verifyFn;

//---------------------------------------------------------------------------
// Set up the default values
//---------------------------------------------------------------------------
/*
* Set up the default values
*/
// clock skew. Must be a postive integer
if (options.clockSkew && (typeof options.clockSkew !== 'number' || options.clockSkew <= 0 || options.clockSkew % 1 !== 0))
throw new Error('clockSkew must be a positive integer');
if (options.clockSkew && (typeof options.clockSkew !== "number" || options.clockSkew <= 0 || options.clockSkew % 1 !== 0))
throw new Error("clockSkew must be a positive integer");
if (!options.clockSkew)

@@ -218,8 +200,10 @@ options.clockSkew = CONSTANTS.CLOCK_SKEW;

// if options.audience is a string or an array of string, then we use it;
// otherwise we use the clientID
if (options.audience && typeof options.audience === 'string')
/*
* if options.audience is a string or an array of string, then we use it;
* otherwise we use the clientID
*/
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];
options.audience = [options.clientID, "spn:" + options.clientID];

@@ -231,3 +215,3 @@ // default value of isB2C is false

// turn issuer into an array
if (options.issuer === '')
if (options.issuer === "")
options.issuer = null;

@@ -239,34 +223,34 @@ if (options.issuer && Array.isArray(options.issuer) && options.issuer.length === 0)

//---------------------------------------------------------------------------
// validate the things in options
//---------------------------------------------------------------------------
/*
* validate the things in options
*/
// clientID should not be empty
if (!options.clientID || options.clientID === '')
throw new Error('In BearerStrategy constructor: clientID cannot be empty');
if (!options.clientID || options.clientID === "")
throw new Error("In BearerStrategy constructor: clientID cannot be empty");
// identityMetadata must be https url
if (!options.identityMetadata || !UrlValidator.isHttpsUri(options.identityMetadata))
throw new Error('In BearerStrategy constructor: identityMetadata must be provided and must be a https url');
throw new Error("In BearerStrategy constructor: identityMetadata must be provided and must be a https url");
// if scope is provided, it must be an array
if (options.scope && (!Array.isArray(options.scope) || options.scope.length === 0))
throw new Error('In BearerStrategy constructor: scope must be a non-empty array');
throw new Error("In BearerStrategy constructor: scope must be a non-empty array");
//---------------------------------------------------------------------------
// treatment of common endpoint and issuer
//---------------------------------------------------------------------------
/*
* treatment of common endpoint and issuer
*/
// check if we are using the common endpoint
options._isCommonEndpoint = (options.identityMetadata.indexOf('/common/') != -1);
options._isCommonEndpoint = (options.identityMetadata.indexOf("/common/") !== -1);
// give a warning if user is not validating issuer
if (!options.validateIssuer)
log.warn(`Production environments should always validate the issuer.`);
log.warn("Production environments should always validate the issuer.");
//---------------------------------------------------------------------------
// B2C.
// (1) policy must be provided and must have the valid prefix
// (2) common endpoint is not supported
//---------------------------------------------------------------------------
/*
* B2C.
* (1) policy must be provided and must have the valid prefix
* (2) common endpoint is not supported
*/

@@ -276,13 +260,13 @@ // for B2C,

if (!options.policyName || !CONSTANTS.POLICY_REGEX.test(options.policyName))
throw new Error('In BearerStrategy constructor: invalid policy for B2C');
throw new Error("In BearerStrategy constructor: invalid policy for B2C");
}
// if logging level specified, switch to it.
if (options.loggingLevel) { log.levels('console', options.loggingLevel); }
if (options.loggingLevel) { log.levels("console", options.loggingLevel); }
if (options.loggingNoPII != false)
if (options.loggingNoPII !== false)
options.loggingNoPII = true;
if (options.loggingNoPII)
log.info('In BearerStrategy constructor: strategy created');
log.info("In BearerStrategy constructor: strategy created");
else

@@ -306,14 +290,16 @@ log.info(`In BearerStrategy constructor: created strategy with options ${JSON.stringify(options)}`);

if (decoded == null) {
return done(null, false, 'In Strategy.prototype.jwtVerify: Invalid JWT token.');
if (decoded === null) {
return done(null, false, "In Strategy.prototype.jwtVerify: Invalid JWT token.");
}
if (self._options.loggingNoPII)
log.info('In Strategy.prototype.jwtVerify: token is decoded');
log.info("In Strategy.prototype.jwtVerify: token is decoded");
else
log.info('In Strategy.prototype.jwtVerify: token decoded: ', decoded);
log.info("In Strategy.prototype.jwtVerify: token decoded: ", decoded);
// When we generate the PEMkey, there are two different types of token signatures
// we have to validate here. One provides x5t and the other a kid. We need to call
// the right one.
/*
* When we generate the PEMkey, there are two different types of token signatures
* we have to validate here. One provides x5t and the other a kid. We need to call
* the right one.
*/
try {

@@ -325,12 +311,12 @@ if (decoded.header.x5t) {

} else {
return self.failWithLog('In Strategy.prototype.jwtVerify: We did not receive a token we know how to validate');
return self.failWithLog("In Strategy.prototype.jwtVerify: We did not receive a token we know how to validate");
}
} catch (error) {
return self.failWithLog('In Strategy.prototype.jwtVerify: We did not receive a token we know how to validate');
return self.failWithLog("In Strategy.prototype.jwtVerify: We did not receive a token we know how to validate");
}
if (self._options.loggingNoPII)
log.info('PEMkey generated');
log.info("PEMkey generated");
else
log.info('PEMkey generated: ' + PEMkey);
log.info("PEMkey generated: " + PEMkey);

@@ -342,3 +328,3 @@ jwt.verify(token, PEMkey, optionsToValidate, (err, verifiedToken) => {

else
return self.failWithLog('In Strategy.prototype.jwtVerify: cannot verify token');
return self.failWithLog("In Strategy.prototype.jwtVerify: cannot verify token");
}

@@ -349,8 +335,8 @@

if (!verifiedToken.scp)
return self.failWithLog('In Strategy.prototype.jwtVerify: scope is not found in token');
return self.failWithLog("In Strategy.prototype.jwtVerify: scope is not found in token");
// split scope by blanks and remove empty elements in the array
var scopesInToken = verifiedToken.scp.split(/[ ]+/).filter(Boolean);
var hasValidScopeInToken = false;
for (var i = 0; i < scopesInToken.length; i++) {
const scopesInToken = verifiedToken.scp.split(/[ ]+/).filter(Boolean);
let hasValidScopeInToken = false;
for (let i = 0; i < scopesInToken.length; i++) {
if (optionsToValidate.scope.indexOf(scopesInToken[i]) !== -1) {

@@ -364,3 +350,3 @@ hasValidScopeInToken = true;

if (self._options.loggingNoPII)
return self.failWithLog('In Strategy.prototype.jwtVerify: none of the scopes in token is accepted');
return self.failWithLog("In Strategy.prototype.jwtVerify: none of the scopes in token is accepted");
else

@@ -372,11 +358,11 @@ return self.failWithLog(`In Strategy.prototype.jwtVerify: none of the scopes '${verifiedToken.scp}' in token is accepted`);

if (self._options.loggingNoPII)
log.info('In Strategy.prototype.jwtVerify: token is verified');
log.info("In Strategy.prototype.jwtVerify: token is verified");
else
log.info('In Strategy.prototype.jwtVerify: VerifiedToken: ', verifiedToken);
log.info("In Strategy.prototype.jwtVerify: VerifiedToken: ", verifiedToken);
if (self._options.passReqToCallback) {
log.info('In Strategy.prototype.jwtVerify: We did pass Req back to Callback');
log.info("In Strategy.prototype.jwtVerify: We did pass Req back to Callback");
return self._verify(req, verifiedToken, done);
} else {
log.info('In Strategy.prototype.jwtVerify: We did not pass Req back to Callback');
log.info("In Strategy.prototype.jwtVerify: We did not pass Req back to Callback");
return self._verify(verifiedToken, done);

@@ -393,10 +379,11 @@ }

const self = this;
var params = {};
var optionsToValidate = {};
var tenantIdOrName = options && options.tenantIdOrName;
const params = {};
const optionsToValidate = {};
let tenantIdOrName = options && options.tenantIdOrName;
/* Some introduction to async.waterfall (from the following link):
/*
* Some introduction to async.waterfall (from the following link):
* http://stackoverflow.com/questions/28908180/what-is-a-simple-implementation-of-async-waterfall
*
* Runs the tasks array of functions in series, each passing their results
* Runs the tasks array of functions in series, each passing their results
* to the next in the array. However, if any of the tasks pass an error to

@@ -438,3 +425,3 @@ * their own callback, the next function is not executed, and the main callback

if (self._options.loggingNoPII)
log.info('identityMetadata is tenant-specific, so we ignore the provided tenantIdOrName');
log.info("identityMetadata is tenant-specific, so we ignore the provided tenantIdOrName");
else

@@ -447,5 +434,5 @@ log.info(`identityMetadata is tenant-specific, so we ignore the tenantIdOrName '${tenantIdOrName}'`);

if (self._options._isCommonEndpoint && tenantIdOrName) {
params.metadataURL = params.metadataURL.replace('/common/', `/${tenantIdOrName}/`);
params.metadataURL = params.metadataURL.replace("/common/", `/${tenantIdOrName}/`);
if (self._options.loggingNoPII)
log.info(`We are replacing 'common' with the provided tenantIdOrName`);
log.info("We are replacing 'common' with the provided tenantIdOrName");
else

@@ -455,12 +442,14 @@ log.info(`we are replacing 'common' with the tenantIdOrName ${tenantIdOrName}`);

// if we are using the common endpoint and we want to validate issuer, then user has to
// provide issuer in config, or provide tenant id or name using tenantIdOrName option in
// passport.authenticate. Otherwise we won't know the issuer.
/*
* if we are using the common endpoint and we want to validate issuer, then user has to
* provide issuer in config, or provide tenant id or name using tenantIdOrName option in
* passport.authenticate. Otherwise we won't know the issuer.
*/
if (self._options._isCommonEndpoint && self._options.validateIssuer &&
(!self._options.issuer && !tenantIdOrName))
return next(new Error('In passport.authenticate: issuer or tenantIdOrName must be provided in order to validate issuer on common endpoint'));
return next(new Error("In passport.authenticate: issuer or tenantIdOrName must be provided in order to validate issuer on common endpoint"));
// for B2C, if we are using common endpoint, we must have tenantIdOrName provided
if (self._options.isB2C && self._options._isCommonEndpoint && !tenantIdOrName)
return next(new Error('In passport.authenticate: we are using common endpoint for B2C but tenantIdOrName is not provided'));
return next(new Error("In passport.authenticate: we are using common endpoint for B2C but tenantIdOrName is not provided"));

@@ -487,3 +476,3 @@ if (self._options.isB2C)

if (self._options.loggingNoPII)
log.info('In Strategy.prototype.authenticate: received metadata');
log.info("In Strategy.prototype.authenticate: received metadata");
else

@@ -518,3 +507,3 @@ log.info(`In Strategy.prototype.authenticate: received metadata: ${JSON.stringify(metadata)}`);

if (self._options.loggingNoPII)
log.info(`In Strategy.prototype.authenticate: we will validate the options`);
log.info("In Strategy.prototype.authenticate: we will validate the options");
else

@@ -526,13 +515,15 @@ log.info(`In Strategy.prototype.authenticate: we will validate the following options: ${JSON.stringify(optionsToValidate)}`);

// extract the access token from the request, after getting the token, it
// will call `jwtVerify` to verify the token. If token is verified, `jwtVerify`
// will provide the token payload to self._verify function. self._verify is
// provided by the developer, it's up to the developer to decide if the token
// payload is considered authenticated. If authenticated, self._verify will
// provide `user` object (developer's decision of its content) to `verified`
// function here, and the `verified` function does the final work of stuffing
// the `user` obejct into req.user, so the following middleware can use it.
// This is basically how bearerStrategy works.
(next) => {
var token;
/*
* extract the access token from the request, after getting the token, it
* will call `jwtVerify` to verify the token. If token is verified, `jwtVerify`
* will provide the token payload to self._verify function. self._verify is
* provided by the developer, it's up to the developer to decide if the token
* payload is considered authenticated. If authenticated, self._verify will
* provide `user` object (developer's decision of its content) to `verified`
* function here, and the `verified` function does the final work of stuffing
* the `user` obejct into req.user, so the following middleware can use it.
* This is basically how bearerStrategy works.
*/
(next) => { // eslint-disable-line no-unused-vars -- Next used in async.waterfall
let token;

@@ -542,11 +533,11 @@ // token could be in header or body. query is not supported.

if (req.query && req.query.access_token)
return self.failWithLog('In Strategy.prototype.authenticate: access_token should be passed in request header or body. query is unsupported');
return self.failWithLog("In Strategy.prototype.authenticate: access_token should be passed in request header or body. query is unsupported");
if (req.headers && req.headers.authorization) {
var auth_components = req.headers.authorization.split(' ');
if (auth_components.length == 2 &&auth_components[0].toLowerCase() === 'bearer') {
const auth_components = req.headers.authorization.split(" ");
if (auth_components.length === 2 && auth_components[0].toLowerCase() === "bearer") {
token = auth_components[1];
if (token !== '') {
if (token !== "") { // eslint-disable-line security/detect-possible-timing-attacks -- Timing for comparison to empty string should be the same every time
if (self._options.loggingNoPII)
log.info('In Strategy.prototype.authenticate: access_token is received from request header');
log.info("In Strategy.prototype.authenticate: access_token is received from request header");
else

@@ -556,3 +547,3 @@ log.info(`In Strategy.prototype.authenticate: received access_token from request header: ${token}`);

else
return self.failWithLog('In Strategy.prototype.authenticate: missing access_token in the header');
return self.failWithLog("In Strategy.prototype.authenticate: missing access_token in the header");
}

@@ -563,7 +554,7 @@ }

if (token)
return self.failWithLog('In Strategy.prototype.authenticate: access_token cannot be passed in both request header and body');
return self.failWithLog("In Strategy.prototype.authenticate: access_token cannot be passed in both request header and body");
token = req.body.access_token;
if (token) {
if (self._options.loggingNoPII)
log.info('In Strategy.prototype.authenticate: access_token is received from request body');
log.info("In Strategy.prototype.authenticate: access_token is received from request body");
else

@@ -575,3 +566,3 @@ log.info(`In Strategy.prototype.authenticate: received access_token from request body: ${token}`);

if (!token)
return self.failWithLog('token is not found');
return self.failWithLog("token is not found");

@@ -583,10 +574,10 @@ function verified(err, user, info) {

if (!user) {
var err_message = 'error: invalid_token';
if (info && typeof info == 'string')
err_message += ', error description: ' + info;
let err_message = "error: invalid_token";
if (info && typeof info === "string")
err_message += ", error description: " + info;
else if (info)
err_message += ', error description: ' + JSON.stringify(info);
err_message += ", error description: " + JSON.stringify(info);
if (self._options.loggingNoPII)
return self.failWithLog('error: invalid_token');
return self.failWithLog("error: invalid_token");
else

@@ -613,3 +604,3 @@ return self.failWithLog(err_message);

const self = this;
var metadata = new Metadata(params.metadataURL, 'oidc', self._options);
const metadata = new Metadata(params.metadataURL, "oidc", self._options);

@@ -616,0 +607,0 @@ // fetch metadata

@@ -1,29 +0,11 @@

/**
* Copyright (c) Microsoft Corporation
* All Rights Reserved
* MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this
* software and associated documentation files (the 'Software'), to deal in the Software
* without restriction, including without limitation the rights to use, copy, modify,
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
* OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT
* OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/
'use strict';
"use strict";
// constants that are not strategy specific
var CONSTANTS = {};
const CONSTANTS = {};

@@ -35,4 +17,4 @@ CONSTANTS.POLICY_REGEX = /^b2c_1a?_[0-9a-z._-]+$/i; // policy is case insensitive

CONSTANTS.CLIENT_ASSERTION_JWT_LIFETIME = 600; // 10 minutes
CONSTANTS.CLIENT_ASSERTION_TYPE = 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer';
CONSTANTS.CLIENT_ASSERTION_TYPE = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer";
module.exports = CONSTANTS;

@@ -1,29 +0,11 @@

/**
* Copyright (c) Microsoft Corporation
* All Rights Reserved
* MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this
* software and associated documentation files (the "Software"), to deal in the Software
* without restriction, including without limitation the rights to use, copy, modify,
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
* OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT
* OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/
'use restrict';
"use restrict";
var crypto = require('crypto');
var createBuffer = require('./jwe').createBuffer;
var sameSiteNotAllowed = require('./aadutils').sameSiteNotAllowed;
const crypto = require("crypto");
const createBuffer = require("./jwe").createBuffer;
const sameSiteNotAllowed = require("./aadutils").sameSiteNotAllowed;

@@ -39,20 +21,20 @@ /*

function CookieContentHandler(maxAmount, maxAge, cookieEncryptionKeys, domain, cookieSameSite) {
if (!maxAge || (typeof maxAge !== 'number' || maxAge <= 0))
throw new Error('CookieContentHandler: maxAge must be a positive number');
if (!maxAge || (typeof maxAge !== "number" || maxAge <= 0))
throw new Error("CookieContentHandler: maxAge must be a positive number");
this.maxAge = maxAge; // seconds
if (!maxAmount || (typeof maxAmount !== 'number' || maxAmount <= 0 || maxAmount % 1 !== 0))
throw new Error('CookieContentHandler: maxAmount must be a positive integer');
if (!maxAmount || (typeof maxAmount !== "number" || maxAmount <= 0 || maxAmount % 1 !== 0))
throw new Error("CookieContentHandler: maxAmount must be a positive integer");
this.maxAmount = maxAmount;
if (!cookieEncryptionKeys || !Array.isArray(cookieEncryptionKeys) || cookieEncryptionKeys.length === 0)
throw new Error('CookieContentHandler: cookieEncryptionKeys must be a non-emptry array');
throw new Error("CookieContentHandler: cookieEncryptionKeys must be a non-emptry array");
if (typeof cookieSameSite !== 'boolean') {
throw new Error('CookieContentHandler: cookieSameSite must be a boolean');
if (typeof cookieSameSite !== "boolean") {
throw new Error("CookieContentHandler: cookieSameSite must be a boolean");
}
this.cookieSameSite = cookieSameSite
for (var i = 0; i < cookieEncryptionKeys.length; i++) {
var item = cookieEncryptionKeys[i];
for (let i = 0; i < cookieEncryptionKeys.length; i++) {
const item = cookieEncryptionKeys[i];
if (!item.key || !item.iv)

@@ -73,20 +55,20 @@ throw new Error(`CookieContentHandler: array item ${i+1} in cookieEncryptionKeys must have the form { key: , iv: }`);

if (!req.cookies)
throw new Error('Cookie is not found in request. Did you forget to use cookie parsing middleware such as cookie-parser?');
throw new Error("Cookie is not found in request. Did you forget to use cookie parsing middleware such as cookie-parser?");
var cookieEncryptionKeys = this.cookieEncryptionKeys;
const cookieEncryptionKeys = this.cookieEncryptionKeys;
var tuple = null;
let tuple = null;
// try every key and every cookie
for (var i = 0; i < cookieEncryptionKeys.length; i++) {
var item = cookieEncryptionKeys[i];
var key = createBuffer(item.key);
var iv = createBuffer(item.iv);
for (let i = 0; i < cookieEncryptionKeys.length; i++) {
const item = cookieEncryptionKeys[i];
const key = createBuffer(item.key);
const iv = createBuffer(item.iv);
for (var cookie in req.cookies) {
if (req.cookies.hasOwnProperty(cookie) && cookie.startsWith('passport-aad.')) {
var encrypted = cookie.substring(13);
for (const cookie in req.cookies) {
if (Object.prototype.hasOwnProperty.call(req.cookies, cookie) && cookie.startsWith("passport-aad.")) {
const encrypted = cookie.substring(13);
try {
var decrypted = decryptCookie(encrypted, key, iv);
const decrypted = decryptCookie(encrypted, key, iv);
tuple = JSON.parse(decrypted);

@@ -109,7 +91,7 @@ } catch (ex) {

CookieContentHandler.prototype.add = function(req, res, tupleToAdd) {
var cookies = [];
const cookies = [];
// collect the related cookies
for (var cookie in req.cookies) {
if (req.cookies.hasOwnProperty(cookie) && cookie.startsWith('passport-aad.'))
for (const cookie in req.cookies) {
if (Object.prototype.hasOwnProperty.call(req.cookies, cookie) && cookie.startsWith("passport-aad."))
cookies.push(cookie);

@@ -122,5 +104,5 @@ }

var numberToRemove = cookies.length - (this.maxAmount - 1);
const numberToRemove = cookies.length - (this.maxAmount - 1);
for (var i = 0; i < numberToRemove; i++) {
for (let i = 0; i < numberToRemove; i++) {
res.clearCookie(cookies[0]);

@@ -133,11 +115,11 @@ cookies.shift();

var tupleString = JSON.stringify(tupleToAdd);
const tupleString = JSON.stringify(tupleToAdd);
var item = this.cookieEncryptionKeys[0];
var key = createBuffer(item.key);
var iv = createBuffer(item.iv);
const item = this.cookieEncryptionKeys[0];
const key = createBuffer(item.key);
const iv = createBuffer(item.iv);
var encrypted = encryptCookie(tupleString, key, iv);
const encrypted = encryptCookie(tupleString, key, iv);
let options = { maxAge: this.maxAge * 1000, httpOnly: true }
const options = { maxAge: this.maxAge * 1000, httpOnly: true }
if (this.domain) {

@@ -147,33 +129,33 @@ options.domain = this.domain;

if (this.cookieSameSite && !sameSiteNotAllowed(req.get('User-Agent'))) {
options.sameSite = 'none';
if (this.cookieSameSite && !sameSiteNotAllowed(req.get("User-Agent"))) {
options.sameSite = "none";
options.secure = true;
}
res.cookie('passport-aad.' + Date.now() + '.' + encrypted, 0, options);
res.cookie("passport-aad." + Date.now() + "." + encrypted, 0, options);
};
var encryptCookie = function(content, key, iv) {
var cipher = crypto.createCipheriv('aes-256-gcm', key, iv);
const encryptCookie = function(content, key, iv) {
const cipher = crypto.createCipheriv("aes-256-gcm", key, iv);
var encrypted = cipher.update(content, 'utf8', 'hex');
encrypted += cipher.final('hex');
var authTag = cipher.getAuthTag().toString('hex');
let encrypted = cipher.update(content, "utf8", "hex");
encrypted += cipher.final("hex");
const authTag = cipher.getAuthTag().toString("hex");
return encrypted + '.' + authTag;
return encrypted + "." + authTag;
};
var decryptCookie = function(encrypted, key, iv) {
var parts = encrypted.split('.');
const decryptCookie = function(encrypted, key, iv) {
const parts = encrypted.split(".");
if (parts.length !== 3)
throw new Error('invalid cookie');
throw new Error("invalid cookie");
// the first part is timestamp, ignore it
var content = createBuffer(parts[1], 'hex');
var authTag = createBuffer(parts[2], 'hex');
const content = createBuffer(parts[1], "hex");
const authTag = createBuffer(parts[2], "hex");
var decipher = crypto.createDecipheriv('aes-256-gcm', key, iv);
const decipher = crypto.createDecipheriv("aes-256-gcm", key, iv);
decipher.setAuthTag(authTag);
var decrypted = decipher.update(content, 'hex', 'utf8');
decrypted += decipher.final('utf8');
let decrypted = decipher.update(content, "hex", "utf8");
decrypted += decipher.final("utf8");

@@ -180,0 +162,0 @@ return decrypted;

@@ -1,3 +0,8 @@

'use strict';
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/
"use strict";
/**

@@ -11,3 +16,3 @@ * `BadRequestError` error.

Error.captureStackTrace(this, BadRequestError);
this.name = 'BadRequestError';
this.name = "BadRequestError";
this.message = message || null;

@@ -14,0 +19,0 @@ }

@@ -1,3 +0,8 @@

'use strict';
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/
"use strict";
/**

@@ -15,3 +20,3 @@ * `InternalOAuthError` error.

Error.captureStackTrace(this, InternalOAuthError);
this.name = 'InternalOAuthError';
this.name = "InternalOAuthError";
this.message = message;

@@ -18,0 +23,0 @@ this.oauthError = err;

@@ -1,3 +0,8 @@

'use strict';
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/
"use strict";
/**

@@ -15,3 +20,3 @@ * `InternalOpenIDError` error.

Error.captureStackTrace(this, InternalOpenIDError);
this.name = 'InternalOpenIDError';
this.name = "InternalOpenIDError";
this.message = message;

@@ -18,0 +23,0 @@ this.openidError = err;

@@ -1,25 +0,7 @@

/**
* Copyright (c) Microsoft Corporation
* All Rights Reserved
* MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this
* software and associated documentation files (the 'Software'), to deal in the Software
* without restriction, including without limitation the rights to use, copy, modify,
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
* OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT
* OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/
'use strict';
"use strict";

@@ -29,3 +11,3 @@ /**

*/
module.exports.BearerStrategy = require('./bearerstrategy');
module.exports.OIDCStrategy = require('./oidcstrategy');
module.exports.BearerStrategy = require("./bearerstrategy");
module.exports.OIDCStrategy = require("./oidcstrategy");

@@ -1,33 +0,15 @@

/**
* Copyright (c) Microsoft Corporation
* All Rights Reserved
* MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this
* software and associated documentation files (the 'Software'), to deal in the Software
* without restriction, including without limitation the rights to use, copy, modify,
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
* OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT
* OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/
'use restrict';
"use restrict";
const aadutils = require('./aadutils');
const CONSTANTS = require('./constants');
const jws = require('jws');
const aadutils = require("./aadutils");
const CONSTANTS = require("./constants");
const jws = require("jws");
// check if two arrays have common elements
var hasCommonElem = (array1, array2) => {
for (var i = 0; i < array1.length; i++) {
const hasCommonElem = (array1, array2) => {
for (let i = 0; i < array1.length; i++) {
if (array2.indexOf(array1[i]) !== -1)

@@ -39,3 +21,4 @@ return true;

/* Verify the token and return the payload
/*
* Verify the token and return the payload
*

@@ -57,25 +40,29 @@ * @jwtString

/*********************************************************************
/**
* Checking parameters
********************************************************************/
*/
// check the existence of callback function and options, if we don't have them, that means we have
// less than 4 parameters passed. This is a server (code) error, we should throw.
/*
* check the existence of callback function and options, if we don't have them, that means we have
* less than 4 parameters passed. This is a server (code) error, we should throw.
*/
if (!callback)
throw new Error('callback must be provided in jsonWebToken.verify');
if (typeof callback !== 'function')
throw new Error('callback in jsonWebToken.verify must be a function');
throw new Error("callback must be provided in jsonWebToken.verify");
if (typeof callback !== "function")
throw new Error("callback in jsonWebToken.verify must be a function");
if (!options)
throw new Error('options must be provided in jsonWebToken.verify');
throw new Error("options must be provided in jsonWebToken.verify");
// check jwtString and PEMKey are provided. Since jwtString and PEMKey are generated, this is
// a non-server error, we shouldn't throw, we just give the error back and let authentication fail.
if (!jwtString || jwtString === '')
return done(new Error('jwtString must be provided in jsonWebToken.verify'));
if (!PEMKey || PEMKey === '')
return done(new Error('PEMKey must be provided in jsonWebToken.verify'));
/*
* check jwtString and PEMKey are provided. Since jwtString and PEMKey are generated, this is
* a non-server error, we shouldn't throw, we just give the error back and let authentication fail.
*/
if (!jwtString || jwtString === "")
return done(new Error("jwtString must be provided in jsonWebToken.verify"));
if (!PEMKey || PEMKey === "")
return done(new Error("PEMKey must be provided in jsonWebToken.verify"));
// asynchronous wrapper for callback
var done = function() {
var args = Array.prototype.slice.call(arguments, 0);
const done = function() {
const args = Array.prototype.slice.call(arguments, 0);
return process.nextTick(function() {

@@ -87,60 +74,60 @@ callback.apply(null, args);

// make sure we have the required fields in options
if (!(options.audience && (typeof options.audience === 'string' ||
if (!(options.audience && (typeof options.audience === "string" ||
(Array.isArray(options.audience) && options.audience.length > 0))))
return done(new Error('invalid options.audience value is provided in jsonWebToken.verify'));
return done(new Error("invalid options.audience value is provided in jsonWebToken.verify"));
if (!options.algorithms)
return done(new Error('options.algorithms is missing in jsonWebToken.verify'));
if (!Array.isArray(options.algorithms) || options.algorithms.length == 0 ||
(options.algorithms.length === 1 && options.algorithms[0] === 'none'))
return done(new Error('options.algorithms must be an array containing at least one algorithm'));
return done(new Error("options.algorithms is missing in jsonWebToken.verify"));
if (!Array.isArray(options.algorithms) || options.algorithms.length === 0 ||
(options.algorithms.length === 1 && options.algorithms[0] === "none"))
return done(new Error("options.algorithms must be an array containing at least one algorithm"));
/*********************************************************************
/**
* Checking jwtString structure, getting header, payload and signature
********************************************************************/
*/
// split jwtString, make sure we have three parts and we have signature
var parts = jwtString.split('.');
const parts = jwtString.split(".");
if (parts.length !== 3)
return done(new Error('jwtString is malformed'));
if (parts[2] === '')
return done(new Error('signature is missing in jwtString'));
return done(new Error("jwtString is malformed"));
if (parts[2] === "")
return done(new Error("signature is missing in jwtString"));
// decode jwsString
var decodedToken;
let decodedToken;
try {
decodedToken = jws.decode(jwtString);
} catch(err) {
return done(new Error('failed to decode the token'));
return done(new Error("failed to decode the token"));
}
if (!decodedToken) {
return done(new Error('invalid token'));
return done(new Error("invalid token"));
}
// get header, payload and signature
var header = decodedToken.header;
var payload = decodedToken.payload;
var signature = decodedToken.signature;
const header = decodedToken.header;
const payload = decodedToken.payload;
const signature = decodedToken.signature;
if (!header)
return done(new Error('missing header in the token'));
return done(new Error("missing header in the token"));
if (!payload)
return done(new Error('missing payload in the token'));
return done(new Error("missing payload in the token"));
if (!signature)
return done(new Error('missing signature in the token'));
return done(new Error("missing signature in the token"));
/*********************************************************************
/**
* validate algorithm and signature
********************************************************************/
*/
// header.alg should be one of the algorithms provided in options.algorithms
if (typeof header.alg !== 'string' || header.alg === '' || header.alg === 'none' ||
options.algorithms.indexOf(header.alg) == -1) {
return done(new Error('invalid algorithm'));
if (typeof header.alg !== "string" || header.alg === "" || header.alg === "none" ||
options.algorithms.indexOf(header.alg) === -1) {
return done(new Error("invalid algorithm"));
}
try {
var valid = jws.verify(jwtString, header.alg, PEMKey);
const valid = jws.verify(jwtString, header.alg, PEMKey);
if (!valid)
return done(new Error('invalid signature'));
return done(new Error("invalid signature"));
} catch (e) {

@@ -150,15 +137,17 @@ return done(e);

/*********************************************************************
/**
* validate payload content
********************************************************************/
*/
// (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 done(new Error('invalid iss value in payload'));
/*
* (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 done(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 done(new Error('options.issuer is missing'));
var valid = false;
if (!options.issuer || options.issuer === "" || (Array.isArray(options.issuer) && options.issuer.length === 0))
return done(new Error("options.issuer is missing"));
let valid = false;
if (Array.isArray(options.issuer))

@@ -169,58 +158,65 @@ valid = (options.issuer.indexOf(payload.iss) !== -1);

if (!valid)
return done(new Error('jwt issuer is invalid'));
return done(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
/*
* (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 done(new Error('invalid sub value in payload'));
if (typeof payload.sub !== "string" || payload.sub === "")
return done(new Error("invalid sub value in payload"));
if (options.subject && options.subject !== payload.sub)
return done(new Error('jwt subject is invalid. expected'));
return done(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];
/*
* (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 done(new Error('mulitple audience in token is not allowed'));
var payload_audience = Array.isArray(payload.aud) ? payload.aud : [payload.aud];
return done(new Error("mulitple audience in token is not allowed"));
const payload_audience = Array.isArray(payload.aud) ? payload.aud : [payload.aud];
if (!hasCommonElem(options.audience, payload_audience))
return done(new Error('jwt audience is invalid'));
return done(new Error("jwt audience is invalid"));
if (payload_audience.length > 1) {
if (typeof payload.azp !== 'string' || payload.azp !== options.clientID)
return done(new Error('jwt azp is invalid'));
if (typeof payload.azp !== "string" || payload.azp !== options.clientID)
return done(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 done(new Error('invalid exp value in payload'));
/*
* (4) expiration
* - check the existence and the format of payload.exp
* - validate unless options.ignoreExpiration is set true
*/
if (typeof payload.exp !== "number")
return done(new Error("invalid exp value in payload"));
if (!options.ignoreExpiration) {
if (Math.floor(Date.now() / 1000) >= payload.exp + options.clockSkew) {
return done(new Error('jwt is expired'));
return done(new Error("jwt is expired"));
}
}
// (5) nbf
// - check if it exists
/*
* (5) nbf
* - check if it exists
*/
if (payload.nbf) {
if (typeof payload.nbf !== 'number')
return done(new Error('nbf value in payload is not a number'));
if (typeof payload.nbf !== "number")
return done(new Error("nbf value in payload is not a number"));
if (payload.nbf >= payload.exp)
return done(new Error('nbf value in payload is not before the exp value'));
return done(new Error("nbf value in payload is not before the exp value"));
if (payload.nbf >= Math.floor(Date.now() / 1000) + options.clockSkew)
return done(new Error('jwt is not active'));
return done(new Error("jwt is not active"));
}
/*********************************************************************
/**
* return the payload content
********************************************************************/
*/

@@ -230,3 +226,4 @@ return done(null, payload);

/* Generate client assertion
/*
* Generate client assertion
*

@@ -240,4 +237,4 @@ * @params {String} clientID

exports.generateClientAssertion = function(clientID, token_endpoint, privatePEMKey, thumbprint, callback) {
var header = { 'x5t': thumbprint, 'alg': 'RS256', 'typ': 'JWT' };
var payload = {
const header = { "x5t": thumbprint, "alg": "RS256", "typ": "JWT" };
const payload = {
sub: clientID,

@@ -251,4 +248,4 @@ iss: clientID,

var clientAssertion;
var exception = null;
let clientAssertion;
let exception = null;

@@ -255,0 +252,0 @@ try {

@@ -1,37 +0,17 @@

/**
* Copyright (c) Microsoft Corporation
* All Rights Reserved
* MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this
* software and associated documentation files (the "Software"), to deal in the Software
* without restriction, including without limitation the rights to use, copy, modify,
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
* OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT
* OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/
/* eslint-disable no-new */
'use strict';
"use strict";
var crypto = require('crypto');
var constants = require('constants');
const base64url = require('base64url');
const crypto = require("crypto");
const constants = require("constants");
const base64url = require("base64url");
var jose = require('node-jose');
const jose = require("node-jose");
/******************************************************************************
/**
* utility functions
*****************************************************************************/
*/

@@ -45,10 +25,10 @@ /**

*/
var createBuffer = (data, encoding) => {
if (!Buffer.isBuffer(data) && typeof data !== 'string' && typeof data !== 'number')
throw new Error('in createBuffer, data must be a buffer, string or number');
const createBuffer = (data, encoding) => {
if (!Buffer.isBuffer(data) && typeof data !== "string" && typeof data !== "number")
throw new Error("in createBuffer, data must be a buffer, string or number");
if (process.version >= 'v6') {
if (typeof data === 'string')
if (process.version >= "v6") {
if (typeof data === "string")
return Buffer.from(data, encoding);
else if (typeof data === 'number')
else if (typeof data === "number")
return Buffer.alloc(data);

@@ -58,6 +38,6 @@ else

} else {
if (typeof data === 'string')
return new Buffer(data, encoding);
if (typeof data === "string")
return new Buffer(data, encoding); // eslint-disable-line security/detect-new-buffer -- Legacy code, Buffer needed
else
return new Buffer(data);
return new Buffer(data); // eslint-disable-line security/detect-new-buffer -- Legacy code, Buffer needed
}

@@ -73,4 +53,4 @@ };

*/
var xor = (a, b) => {
var c1, c2;
const xor = (a, b) => {
let c1, c2;
if (a.length > b.length) {

@@ -81,4 +61,4 @@ c1 = a; c2 = b;

}
var c = createBuffer(c1);
for (var i = 1; i <= c2.length; i++)
const c = createBuffer(c1);
for (let i = 1; i <= c2.length; i++)
c[c1.length-i] = c[c1.length-i] ^ c2[c2.length-i];

@@ -88,5 +68,5 @@ return c;

/******************************************************************************
/**
* AES key unwrap algorithms
*****************************************************************************/
*/

@@ -101,14 +81,14 @@ /**

*/
var AESKeyUnWrap = (algorithm, wrapped_cek, kek) => {
const AESKeyUnWrap = (algorithm, wrapped_cek, kek) => {
/****************************************************************************
/**
* Inputs: CipherText, (n+1) 64-bit values {C0, C1, ..., Cn}, and
* Key, K (the KEK)
* Outputs: Plaintext, n 64-bit values {P0, P1, K, Pn}
***************************************************************************/
var C = wrapped_cek;
var n = C.length/8-1;
var K = kek;
*/
const C = wrapped_cek;
const n = C.length/8-1;
const K = kek;
/****************************************************************************
/**
* 1) Initialize variables

@@ -118,9 +98,9 @@ * Set A = C[0]

* R[i] = C[i]
***************************************************************************/
var A = C.slice(0,8);
var R = [createBuffer(1)];
for (var i = 1; i <= n; i++)
*/
let A = C.slice(0,8);
const R = [createBuffer(1)];
for (let i = 1; i <= n; i++)
R.push(C.slice(8*i, 8*i+8));
/****************************************************************************
/**
* 2) compute intermediate values

@@ -132,17 +112,17 @@ * For j = 5 to 0

* R[i] = LSB(64, B)
***************************************************************************/
for(var j=5; j >= 0; j--) {
for(var i=n; i >= 1; i--) {
*/
for(let j=5; j >= 0; j--) {
for(let i=n; i >= 1; i--) {
// turn t = n*j+i into buffer
var str = (n*j+i).toString(16);
let str = (n*j+i).toString(16);
if (str.length %2 !== 0)
str = '0' + str;
var t = createBuffer(str, 'hex');
str = "0" + str;
const t = createBuffer(str, "hex");
// B = AES-1(K, (A^t) | R[i])
var aes = crypto.createDecipheriv(algorithm, K, '');
const aes = crypto.createDecipheriv(algorithm, K, "");
aes.setAutoPadding(false);
var B = aes.update(Buffer.concat([xor(A, t), R[i]]), null, 'hex');
B += aes.final('hex');
B = createBuffer(B, 'hex');
let B = aes.update(Buffer.concat([xor(A, t), R[i]]), null, "hex");
B += aes.final("hex");
B = createBuffer(B, "hex");

@@ -157,3 +137,3 @@ // A = MSB(64, B)

/****************************************************************************
/**
* 3) Output results.

@@ -166,8 +146,8 @@ * If A is an appropriate initial value

* Return an error
***************************************************************************/
*/
// check A
if (A.toString('hex').toUpperCase() === 'A6A6A6A6A6A6A6A6') {
var result = R[1];
for (var i = 2; i <= n; i++)
if (A.toString("hex").toUpperCase() === "A6A6A6A6A6A6A6A6") {
let result = R[1];
for (let i = 2; i <= n; i++)
result = Buffer.concat([result, R[i]]);

@@ -177,9 +157,9 @@

} else {
throw new Error('aes decryption failed: invalid key');
throw new Error("aes decryption failed: invalid key");
}
};
/******************************************************************************
/**
* AES-CBC-HMAC-SHA2 decryption
*****************************************************************************/
*/

@@ -197,20 +177,22 @@ /**

*/
var decrypt_AES_CBC_HMAC_SHA2 = (algorithm, key, cipherText, iv, aad, authTag) => {
// algorithm information
// note ENC_KEY_LEN = MAC_KEY_LEN = T_LEN = len
var algInfo = {
'aes-128-cbc-hmac-sha-256': {
'cipher_algo': 'aes-128-cbc',
'hash_algo': 'sha256',
'len': 16
const decrypt_AES_CBC_HMAC_SHA2 = (algorithm, key, cipherText, iv, aad, authTag) => {
/*
* algorithm information
* note ENC_KEY_LEN = MAC_KEY_LEN = T_LEN = len
*/
const algInfo = {
"aes-128-cbc-hmac-sha-256": {
"cipher_algo": "aes-128-cbc",
"hash_algo": "sha256",
"len": 16
},
'aes-192-cbc-hmac-sha-384': {
'cipher_algo': 'aes-192-cbc',
'hash_algo': 'sha384',
'len': 24
"aes-192-cbc-hmac-sha-384": {
"cipher_algo": "aes-192-cbc",
"hash_algo": "sha384",
"len": 24
},
'aes-256-cbc-hmac-sha-512': {
'cipher_algo': 'aes-256-cbc',
'hash_algo': 'sha512',
'len': 32
"aes-256-cbc-hmac-sha-512": {
"cipher_algo": "aes-256-cbc",
"hash_algo": "sha512",
"len": 32
}

@@ -223,29 +205,29 @@ };

if (!algorithm)
throw new Error('In decrypt_AES_CBC_HMAC_SHA2: algorithm is not provided');
throw new Error("In decrypt_AES_CBC_HMAC_SHA2: algorithm is not provided");
if (!algInfo[algorithm])
throw new Error('In decrypt_AES_CBC_HMAC_SHA2: unsupported algorithm: ' + algorithm);
throw new Error("In decrypt_AES_CBC_HMAC_SHA2: unsupported algorithm: " + algorithm);
var algo = algInfo[algorithm];
const algo = algInfo[algorithm];
// check the size of key
if (!key)
throw new Error('In decrypt_AES_CBC_HMAC_SHA2: key is not provided');
throw new Error("In decrypt_AES_CBC_HMAC_SHA2: key is not provided");
if (!(key instanceof Buffer))
throw new Error('In decrypt_AES_CBC_HMAC_SHA2: key must be a buffer');
throw new Error("In decrypt_AES_CBC_HMAC_SHA2: key must be a buffer");
if (key.length !== algInfo[algorithm].len * 2)
throw new Error('In decrypt_AES_CBC_HMAC_SHA2: key has size ' + key.length + ', it must have size ' + algo.len * 2);
throw new Error("In decrypt_AES_CBC_HMAC_SHA2: key has size " + key.length + ", it must have size " + algo.len * 2);
// check the size of iv
if (!iv)
throw new Error('In decrypt_AES_CBC_HMAC_SHA2: iv is not provided');
throw new Error("In decrypt_AES_CBC_HMAC_SHA2: iv is not provided");
if (!(iv instanceof Buffer))
throw new Error('In decrypt_AES_CBC_HMAC_SHA2: iv must be a buffer');
throw new Error("In decrypt_AES_CBC_HMAC_SHA2: iv must be a buffer");
if (iv.length !== 16)
throw new Error('In decrypt_AES_CBC_HMAC_SHA2: iv has size ' + iv.length + ', it must have size 16');
throw new Error("In decrypt_AES_CBC_HMAC_SHA2: iv has size " + iv.length + ", it must have size 16");
// check the existence of aad
if (!aad)
throw new Error('In decrypt_AES_CBC_HMAC_SHA2: aad is not provided');
throw new Error("In decrypt_AES_CBC_HMAC_SHA2: aad is not provided");
if (!(aad instanceof Buffer))
throw new Error('In decrypt_AES_CBC_HMAC_SHA2: aad must be a buffer');
throw new Error("In decrypt_AES_CBC_HMAC_SHA2: aad must be a buffer");

@@ -255,12 +237,14 @@ // 2. Split key

// first half is mac_key and the second half is enc_key
var mac_key = key.slice(0, algo.len);
var enc_key = key.slice(algo.len);
const mac_key = key.slice(0, algo.len);
const enc_key = key.slice(algo.len);
// 3. Verify tag
// tag should be: hash(aad + iv + cipherText + aad_len) where aad_len is the
// number of bits in aad expressed as a 64-bit unsigned big-endian integer
/*
* tag should be: hash(aad + iv + cipherText + aad_len) where aad_len is the
* number of bits in aad expressed as a 64-bit unsigned big-endian integer
*/
// 3.1 compute aad_len
var aad_len = createBuffer(8);
const aad_len = createBuffer(8);
aad_len.writeUInt32BE(aad.length * 8, 4);

@@ -270,3 +254,3 @@ aad_len.writeUInt32BE(0, 0);

// 3.2 create hmac algorithm
var hmac = crypto.createHmac(algo.hash_algo, mac_key);
const hmac = crypto.createHmac(algo.hash_algo, mac_key);
hmac.write(aad);

@@ -276,11 +260,11 @@ hmac.write(iv);

hmac.update(aad_len);
var computed_authTag = hmac.digest().slice(0, algo.len);
const computed_authTag = hmac.digest().slice(0, algo.len);
// 3.3 verify the tag
if (!authTag || !computed_authTag || (authTag.toString('hex') !== computed_authTag.toString('hex')))
throw new Error('In decrypt_AES_CBC_HMAC_SHA2: invalid authentication tag');
if (!authTag || !computed_authTag || (authTag.toString("hex") !== computed_authTag.toString("hex")))
throw new Error("In decrypt_AES_CBC_HMAC_SHA2: invalid authentication tag");
// 4. Decrypt cipherText
var decipher = crypto.createDecipheriv(algo.cipher_algo, enc_key, iv);
var plainText = decipher.update(cipherText);
const decipher = crypto.createDecipheriv(algo.cipher_algo, enc_key, iv);
let plainText = decipher.update(cipherText);
plainText = Buffer.concat([plainText, decipher.final()]);

@@ -291,6 +275,5 @@

/******************************************************************************
/**
* JWE decryption
*****************************************************************************/
*/

@@ -305,33 +288,33 @@ /**

*/
var decryptCEKHelper = (alg, encrypted_cek, key, log) => {
var error = null;
var cek = null;
const decryptCEKHelper = (alg, encrypted_cek, key, log) => {
let error = null;
let cek = null;
try {
var key_to_use;
let key_to_use;
if (alg === 'RSA1_5' || alg === 'RSA-OAEP') {
if (key['privatePemKey']) {
log.info('using RSA privatePemKey to decrypt cek');
key_to_use = key['privatePemKey'];
if (alg === "RSA1_5" || alg === "RSA-OAEP") {
if (key["privatePemKey"]) {
log.info("using RSA privatePemKey to decrypt cek");
key_to_use = key["privatePemKey"];
} else {
log.info('converting jwk to RSA privatePemKey to decrypt cek');
log.info("converting jwk to RSA privatePemKey to decrypt cek");
return { "error": "converting jwk to RSA privatePemKey to decrypt cek", cek: key };
}
} else if (alg === 'A128KW' || alg === 'A256KW') {
log.info('using symmetric key to decrypt cek');
} else if (alg === "A128KW" || alg === "A256KW") {
log.info("using symmetric key to decrypt cek");
key_to_use = base64url.toBuffer(key.k);
} else {
log.info('unsupported alg: ' + alg);
return {'error': error, 'cek': null};
log.info("unsupported alg: " + alg);
return {"error": error, "cek": null};
}
if (alg === 'RSA1_5')
if (alg === "RSA1_5")
cek = crypto.privateDecrypt({ key: key_to_use, padding: constants.RSA_PKCS1_PADDING }, encrypted_cek);
else if (alg === 'RSA-OAEP')
else if (alg === "RSA-OAEP")
cek = crypto.privateDecrypt({ key: key_to_use, padding: constants.RSA_PKCS1_OAEP_PADDING }, encrypted_cek);
else if (alg === 'A128KW')
cek = AESKeyUnWrap('aes-128-ecb', encrypted_cek, key_to_use);
else if (alg === 'A256KW')
cek = AESKeyUnWrap('aes-256-ecb', encrypted_cek, key_to_use);
else if (alg === "A128KW")
cek = AESKeyUnWrap("aes-128-ecb", encrypted_cek, key_to_use);
else if (alg === "A256KW")
cek = AESKeyUnWrap("aes-256-ecb", encrypted_cek, key_to_use);
else

@@ -343,3 +326,3 @@ cek = key_to_use; // dir

return {'error': error, 'cek': cek};
return {"error": error, "cek": cek};
};

@@ -355,17 +338,17 @@

*/
var decryptCEK = (header, encrypted_cek, jweKeyStore, log) => {
var algKtyMapper = { 'RSA1_5': 'RSA', 'RSA-OAEP': 'RSA', 'A128KW': 'oct', 'A256KW': 'oct'};
const decryptCEK = (header, encrypted_cek, jweKeyStore, log) => {
const algKtyMapper = { "RSA1_5": "RSA", "RSA-OAEP": "RSA", "A128KW": "oct", "A256KW": "oct"};
if (!header.alg)
return { 'error': new Error('alg is missing in JWE header'), 'cek': null };
if(['RSA1_5', 'RSA-OAEP', 'dir', 'A128KW', 'A256KW'].indexOf(header.alg) === -1)
return { 'error' : new Error('Unsupported alg in JWE header: ' + header.alg), 'cek': null };
return { "error": new Error("alg is missing in JWE header"), "cek": null };
if(["RSA1_5", "RSA-OAEP", "dir", "A128KW", "A256KW"].indexOf(header.alg) === -1)
return { "error" : new Error("Unsupported alg in JWE header: " + header.alg), "cek": null };
var key = null;
let key = null;
if (header.kid) {
for (var i = 0; i < jweKeyStore.length; i++) {
for (let i = 0; i < jweKeyStore.length; i++) {
if (header.kid === jweKeyStore[i].kid && algKtyMapper[header.alg] === jweKeyStore[i].kty) {
key = jweKeyStore[i];
log.info('found a key matching kid: ' + header.kid);
log.info("found a key matching kid: " + header.kid);
return decryptCEKHelper(header.alg, encrypted_cek, key, log);

@@ -375,11 +358,11 @@ }

return { 'error': new Error('cannot find a key matching kid: ' + header.kid), 'cek': null };
return { "error": new Error("cannot find a key matching kid: " + header.kid), "cek": null };
}
log.info('kid is not provided in JWE header, now we try all the possible keys');
log.info("kid is not provided in JWE header, now we try all the possible keys");
// kid matching failed, now we try every possible key
for(var i = 0; i < jweKeyStore.length; i++) {
for(let i = 0; i < jweKeyStore.length; i++) {
if (jweKeyStore[i].kty === algKtyMapper[header.alg]) {
var result = decryptCEKHelper(header.alg, encrypted_cek, jweKeyStore[i], log);
const result = decryptCEKHelper(header.alg, encrypted_cek, jweKeyStore[i], log);
if (result.cek) {

@@ -391,3 +374,3 @@ return result;

return { 'error': new Error('tried all keys to decrypt cek but none of them works'), 'cek': null };
return { "error": new Error("tried all keys to decrypt cek but none of them works"), "cek": null };
};

@@ -406,21 +389,21 @@

*/
var decryptContent = (header, cek, cipherText, iv, authTag, aad, log) => {
const decryptContent = (header, cek, cipherText, iv, authTag, aad) => {
if (!header.enc)
return { 'error': new Error('enc is missing in JWE header'), 'content': null };
if (['A128GCM', 'A256GCM', 'A128CBC-HS256', 'A192CBC-HS384', 'A256CBC-HS512'].indexOf(header.enc) === -1)
return { 'error' : new Error('Unsupported enc in JWE header: ' + header.enc), 'content': null };
return { "error": new Error("enc is missing in JWE header"), "content": null };
if (["A128GCM", "A256GCM", "A128CBC-HS256", "A192CBC-HS384", "A256CBC-HS512"].indexOf(header.enc) === -1)
return { "error" : new Error("Unsupported enc in JWE header: " + header.enc), "content": null };
var mapper = {
'A128GCM': 'aes-128-gcm',
'A256GCM': 'aes-256-gcm',
'A128CBC-HS256': 'aes-128-cbc-hmac-sha-256',
'A192CBC-HS384': 'aes-192-cbc-hmac-sha-384',
'A256CBC-HS512': 'aes-256-cbc-hmac-sha-512'
const mapper = {
"A128GCM": "aes-128-gcm",
"A256GCM": "aes-256-gcm",
"A128CBC-HS256": "aes-128-cbc-hmac-sha-256",
"A192CBC-HS384": "aes-192-cbc-hmac-sha-384",
"A256CBC-HS512": "aes-256-cbc-hmac-sha-512"
};
try {
var content = null;
let content = null;
if (header.enc === 'A128GCM' || header.enc === 'A256GCM') {
var decipher = crypto.createDecipheriv(mapper[header.enc], cek, iv);
if (header.enc === "A128GCM" || header.enc === "A256GCM") {
const decipher = crypto.createDecipheriv(mapper[header.enc], cek, iv);
decipher.setAAD(aad);

@@ -434,5 +417,5 @@ decipher.setAuthTag(authTag);

return { 'error': null, 'content': content.toString() };
return { "error": null, "content": content.toString() };
} catch (ex) {
return { 'error': ex, 'content': null };
return { "error": ex, "content": null };
}

@@ -452,11 +435,11 @@ };

*/
var decryptContentForDir = (header, jweKeyStore, cipherText, iv, authTag, aad, log) => {
var key = null;
const decryptContentForDir = (header, jweKeyStore, cipherText, iv, authTag, aad, log) => {
let key = null;
// try the key with the corresponding kid
if (header.kid) {
for (var i = 0; i < jweKeyStore.length; i++) {
if (header.kid === jweKeyStore[i].kid && jweKeyStore[i].kty === 'oct') {
for (let i = 0; i < jweKeyStore.length; i++) {
if (header.kid === jweKeyStore[i].kid && jweKeyStore[i].kty === "oct") {
key = jweKeyStore[i];
log.info('Decrypting JWE content, header.alg == dir, found a key matching kid: ' + header.kid);
log.info("Decrypting JWE content, header.alg == dir, found a key matching kid: " + header.kid);
break;

@@ -469,11 +452,11 @@ }

else
return { 'error': new Error('cannot find a key matching kid: ' + header.kid), 'cek': null };
return { "error": new Error("cannot find a key matching kid: " + header.kid), "cek": null };
}
log.info('In decryptContentForDir: kid is not provided in JWE header, now we try all the possible keys');
log.info("In decryptContentForDir: kid is not provided in JWE header, now we try all the possible keys");
// kid matching failed, now we try every possible key
for(var i = 0; i < jweKeyStore.length; i++) {
if (jweKeyStore[i].kty === 'oct') {
var result = decryptContent(header, base64url.toBuffer(jweKeyStore[i].k), cipherText, iv, authTag, aad, log);
for(let i = 0; i < jweKeyStore.length; i++) {
if (jweKeyStore[i].kty === "oct") {
const result = decryptContent(header, base64url.toBuffer(jweKeyStore[i].k), cipherText, iv, authTag, aad, log);
if (!result.error)

@@ -484,4 +467,4 @@ return result;

log.info('In decryptContentForDir: tried all keys to decrypt the content but all failed');
return { error: new Error('In decryptContentForDir: tried all keys to decrypt the content but all failed'), content_result: null };
log.info("In decryptContentForDir: tried all keys to decrypt the content but all failed");
return { error: new Error("In decryptContentForDir: tried all keys to decrypt the content but all failed"), content_result: null };
};

@@ -498,5 +481,5 @@

exports.decrypt = (jweString, jweKeyStore, log, callback) => {
/****************************************************************************
* JWE compact format structure
****************************************************************************
/**
* JWE compact format structure
*
* BASE64URL(UTF8(JWE Protected Header)) || '.' ||

@@ -507,33 +490,33 @@ * BASE64URL(JWE Encrypted Key) || '.' ||

* BASE64URL(JWE Authentication Tag)
***************************************************************************/
var parts = jweString.split('.');
*/
const parts = jweString.split(".");
if (parts.length !== 5)
return callback(new Error('In jwe.decrypt: invalid JWE string, it has ' + parts.length + ' parts instead of 5'), null);
return callback(new Error("In jwe.decrypt: invalid JWE string, it has " + parts.length + " parts instead of 5"), null);
var header;
let header;
try {
header = JSON.parse(base64url.decode(parts[0], 'binary'));
header = JSON.parse(base64url.decode(parts[0], "binary"));
} catch (ex) {
return callback(new Error('In jwe.decrypt: failed to parse JWE header'), null);
return callback(new Error("In jwe.decrypt: failed to parse JWE header"), null);
}
var aad = createBuffer(parts[0]);
var encrypted_cek = base64url.toBuffer(parts[1]);
var iv = base64url.toBuffer(parts[2]);
var cipherText = base64url.toBuffer(parts[3]);
var authTag = base64url.toBuffer(parts[4]);
const aad = createBuffer(parts[0]);
const encrypted_cek = base64url.toBuffer(parts[1]);
const iv = base64url.toBuffer(parts[2]);
const cipherText = base64url.toBuffer(parts[3]);
const authTag = base64url.toBuffer(parts[4]);
log.info('In jwe.decrypt: the header is ' + JSON.stringify(header));
log.info("In jwe.decrypt: the header is " + JSON.stringify(header));
var cek_result;
var content_result;
let cek_result;
let content_result;
// special treatment of 'dir'
if (header.alg === 'dir') {
if (header.alg === "dir") {
content_result = decryptContentForDir(header, jweKeyStore, cipherText, iv, authTag, aad, log);
} else {
/****************************************************************************
/**
* cek decryption
***************************************************************************/
var cek_result = decryptCEK(header, encrypted_cek, jweKeyStore, log);
*/
cek_result = decryptCEK(header, encrypted_cek, jweKeyStore, log);
if (cek_result.error) {

@@ -543,9 +526,9 @@ if (cek_result.error === "converting jwk to RSA privatePemKey to decrypt cek") {

let cek;
if (header.alg === 'RSA1_5')
if (header.alg === "RSA1_5")
cek = crypto.privateDecrypt({ key: pemKey.toPEM(true), padding: constants.RSA_PKCS1_PADDING }, encrypted_cek);
else if (header.alg === 'RSA-OAEP')
else if (header.alg === "RSA-OAEP")
cek = crypto.privateDecrypt({ key: pemKey.toPEM(true), padding: constants.RSA_PKCS1_OAEP_PADDING }, encrypted_cek);
var decryptedPemResult = decryptContent(header, cek, cipherText, iv, authTag, aad, log);
const decryptedPemResult = decryptContent(header, cek, cipherText, iv, authTag, aad, log);
if (!decryptedPemResult.error) {
log.info('In jwe.decrypt: successfully decrypted id_token');
log.info("In jwe.decrypt: successfully decrypted id_token");
}

@@ -559,10 +542,10 @@ return callback(decryptedPemResult.error, decryptedPemResult.content);

/****************************************************************************
/**
* content decryption
***************************************************************************/
var content_result = decryptContent(header, cek_result.cek, cipherText, iv, authTag, aad, log);
*/
content_result = decryptContent(header, cek_result.cek, cipherText, iv, authTag, aad, log);
}
if (!content_result.error) {
log.info('In jwe.decrypt: successfully decrypted id_token');
log.info("In jwe.decrypt: successfully decrypted id_token");
}

@@ -575,3 +558,1 @@

@@ -1,27 +0,10 @@

/**
* Copyright (c) Microsoft Corporation
* All Rights Reserved
* MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this
* software and associated documentation files (the 'Software'), to deal in the Software
* without restriction, including without limitation the rights to use, copy, modify,
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
* OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT
* OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/
'use strict';
const bunyan = require('bunyan');
"use strict";
const bunyan = require("bunyan");
function getLogger(name) {

@@ -32,8 +15,8 @@ const log = bunyan.createLogger({

stream: process.stderr,
level: 'error',
name: 'error',
level: "error",
name: "error",
}, {
stream: process.stdout,
level: 'warn',
name: 'console',
level: "warn",
name: "console",
}],

@@ -40,0 +23,0 @@ });

@@ -1,44 +0,26 @@

/**
* Copyright (c) Microsoft Corporation
* All Rights Reserved
* MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this
* software and associated documentation files (the 'Software'), to deal in the Software
* without restriction, including without limitation the rights to use, copy, modify,
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
* OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT
* OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/
'use strict';
"use strict";
const request = require('request');
const async = require('async');
const aadutils = require('./aadutils');
const HttpsProxyAgent = require('https-proxy-agent');
const Log = require('./logging').getLogger;
const request = require("request");
const async = require("async");
const aadutils = require("./aadutils");
const HttpsProxyAgent = require("https-proxy-agent");
const Log = require("./logging").getLogger;
const log = new Log('AzureAD: Metadata Parser');
const log = new Log("AzureAD: Metadata Parser");
function Metadata(url, authtype, options) {
if (!url) {
throw new Error('Metadata: url is a required argument');
throw new Error("Metadata: url is a required argument");
}
if (!authtype || authtype !== 'oidc') {
throw new Error(`Invalid authtype. authtype must be 'oidc'`);
if (!authtype || authtype !== "oidc") {
throw new Error("Invalid authtype. authtype must be 'oidc'");
}
// if logging level specified, switch to it.
if (options.loggingLevel) { log.levels('console', options.loggingLevel); }
if (options.loggingLevel) { log.levels("console", options.loggingLevel); }

@@ -55,3 +37,3 @@ this.url = url;

Object.defineProperty(Metadata, 'url', {
Object.defineProperty(Metadata, "url", {
get: function getUrl() {

@@ -62,3 +44,3 @@ return this.url;

Object.defineProperty(Metadata, 'oidc', {
Object.defineProperty(Metadata, "oidc", {
get: function getOidc() {

@@ -69,3 +51,3 @@ return this.oidc;

Object.defineProperty(Metadata, 'metadata', {
Object.defineProperty(Metadata, "metadata", {
get: function getMetadata() {

@@ -76,3 +58,3 @@ return this.metadata;

Object.defineProperty(Metadata, 'httpsProxyAgent', {
Object.defineProperty(Metadata, "httpsProxyAgent", {
get: function getHttpsProxyAgent() {

@@ -84,7 +66,7 @@ return this.httpsProxyAgent;

Metadata.prototype.updateOidcMetadata = function updateOidcMetadata(doc, next) {
log.info('Request to update the Open ID Connect Metadata');
log.info("Request to update the Open ID Connect Metadata");
const self = this;
var oidc = {};
const oidc = {};
oidc.algorithms = doc.id_token_signing_alg_values_supported;

@@ -101,9 +83,9 @@ oidc.authorization_endpoint = doc.authorization_endpoint;

if (!self.loggingNoPII) {
log.info('Algorithm retrieved was: ', self.oidc.algorithms);
log.info('Issuer we are using is: ', self.oidc.issuer);
log.info('Key Endpoint we will use is: ', jwksUri);
log.info('Authentication endpoint we will use is: ', self.oidc.authorization_endpoint);
log.info('Token endpoint we will use is: ', self.oidc.token_endpoint);
log.info('User info endpoint we will use is: ', self.oidc.userinfo_endpoint);
log.info('The logout endpoint we will use is: ', self.oidc.end_session_endpoint);
log.info("Algorithm retrieved was: ", self.oidc.algorithms);
log.info("Issuer we are using is: ", self.oidc.issuer);
log.info("Key Endpoint we will use is: ", jwksUri);
log.info("Authentication endpoint we will use is: ", self.oidc.authorization_endpoint);
log.info("Token endpoint we will use is: ", self.oidc.token_endpoint);
log.info("User info endpoint we will use is: ", self.oidc.userinfo_endpoint);
log.info("The logout endpoint we will use is: ", self.oidc.end_session_endpoint);
}

@@ -131,7 +113,7 @@

if (!kid) {
throw new Error('kid is missing');
throw new Error("kid is missing");
}
if (!keys) {
throw new Error('keys is missing');
throw new Error("keys is missing");
}

@@ -141,5 +123,5 @@

if (self.loggingNoPII)
log.info('working on key');
log.info("working on key");
else
log.info('working on key:', key);
log.info("working on key:", key);

@@ -154,5 +136,5 @@ // are we working on the right key?

if (self.loggingNoPII)
log.warn('modulus is empty; corrupt key');
log.warn("modulus is empty; corrupt key");
else
log.warn('modulus is empty; corrupt key', key);
log.warn("modulus is empty; corrupt key", key);
return false;

@@ -164,5 +146,5 @@ }

if (self.loggingNoPII)
log.warn('exponent is empty; corrupt key');
log.warn("exponent is empty; corrupt key");
else
log.warn('exponent is empty; corrupt key', key);
log.warn("exponent is empty; corrupt key", key);
return false;

@@ -180,5 +162,5 @@ }

if (self.loggingNoPII)
throw new Error('a key with the specific kid cannot be found');
throw new Error("a key with the specific kid cannot be found");
else
throw new Error(`a key with kid %s cannot be found`, kid);
throw new Error("a key with kid %s cannot be found", kid);
}

@@ -188,5 +170,5 @@

if (self.loggingNoPII)
throw new Error('generating public key pem failed');
throw new Error("generating public key pem failed");
else
throw new Error(`generating public key pem failed for kid: %s`, kid);
throw new Error("generating public key pem failed for kid: %s", kid);
}

@@ -209,6 +191,6 @@

if (self.loggingNoPII) {
log.error('cannot get AAD Federation metadata from endpoint you specified');
return next(new Error('Cannot get AAD Federation metadata'));
log.error("cannot get AAD Federation metadata from endpoint you specified");
return next(new Error("Cannot get AAD Federation metadata"));
} else {
log.error('Cannot get AAD Federation metadata from endpoint you specified', self.url);
log.error("Cannot get AAD Federation metadata from endpoint you specified", self.url);
return next(new Error(`Error: ${response.statusCode} Cannot get AAD Federation metadata

@@ -224,3 +206,3 @@ from ${self.url}`));

// use json parser for oidc authType
log.info('Parsing JSON retreived from the endpoint');
log.info("Parsing JSON retreived from the endpoint");
self.metadata = JSON.parse(body);

@@ -227,0 +209,0 @@ return next(null);

@@ -1,25 +0,7 @@

/**
* Copyright (c) Microsoft Corporation
* All Rights Reserved
* MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this
* software and associated documentation files (the "Software"), to deal in the Software
* without restriction, including without limitation the rights to use, copy, modify,
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
* OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT
* OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/
'use strict';
"use strict";

@@ -31,3 +13,2 @@ /**

/*

@@ -47,17 +28,19 @@ * certificate string and private key string have the following format:

/* eslint-disable max-len */
/* eslint-disable max-len, no-useless-escape*/
const CERT_REGEX = /-----BEGIN\sCERTIFICATE-----\s+([a-zA-Z0-9+\/=\r\n]+)\s+-----END\sCERTIFICATE-----/;
const PRIVATE_KEY_REGEX = /-----BEGIN\sPRIVATE\sKEY-----\s+([a-zA-Z0-9+\/=\r\n]+)\s+-----END\sPRIVATE\sKEY-----/;
/* eslint-enable max-len */
/* eslint-enable max-len, no-useless-escape*/
function getFirstCapturingGroup(str, regex) {
if (typeof str !== 'string') {
if (typeof str !== "string") {
throw new Error(`'str' must be of type "String", actually is ${typeof str}`);
}
const matches = str.match(regex);
// if str matches the regex (either CERT_REGEX or PRIVATE_KEY_REGEX), then
// matches will always have two groups:
// matches[0]: this is always the entire match, in this case, the certificate/key str
// matches[1]: this is the first (and the only) group we want to capture in the regex,
// which is the certifcate/key content
/*
* if str matches the regex (either CERT_REGEX or PRIVATE_KEY_REGEX), then
* matches will always have two groups:
* matches[0]: this is always the entire match, in this case, the certificate/key str
* matches[1]: this is the first (and the only) group we want to capture in the regex,
* which is the certifcate/key content
*/
if (!Array.isArray(matches) || matches.length !== 2) {

@@ -70,6 +53,6 @@ return null;

function removeRN(str) {
if (typeof str !== 'string') {
if (typeof str !== "string") {
throw new Error(`'str' must be of type "String", actually is ${typeof str}`);
}
return str.replace(/[\r\n]/g, '');
return str.replace(/[\r\n]/g, "");
}

@@ -86,8 +69,8 @@

exports.certToPEM = function certToPEM(cert) {
const BEGIN_CERT = '-----BEGIN CERTIFICATE-----';
const END_CERT = '-----END CERTIFICATE-----';
const BEGIN_CERT = "-----BEGIN CERTIFICATE-----";
const END_CERT = "-----END CERTIFICATE-----";
return [BEGIN_CERT]
.concat(cert.match(/.{1,64}/g))
.concat([END_CERT])
.join('\n');
.join("\n");
};

@@ -1,27 +0,9 @@

/**
* Copyright (c) Microsoft Corporation
* All Rights Reserved
* MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this
* software and associated documentation files (the "Software"), to deal in the Software
* without restriction, including without limitation the rights to use, copy, modify,
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
* OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT
* OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/
'use restrict';
"use restrict";
const aadutils = require('./aadutils');
const aadutils = require("./aadutils");

@@ -34,6 +16,6 @@ /*

function SessionContentHandler(maxAmount, maxAge) {
if (!maxAmount || (typeof maxAmount !== 'number' || maxAmount <= 0 || maxAmount % 1 !== 0))
throw new Error('SessionContentHandler: maxAmount must be a positive integer');
if (!maxAge || (typeof maxAge !== 'number' || maxAge <= 0))
throw new Error('SessionContentHandler: maxAge must be a positive number');
if (!maxAmount || (typeof maxAmount !== "number" || maxAmount <= 0 || maxAmount % 1 !== 0))
throw new Error("SessionContentHandler: maxAmount must be a positive integer");
if (!maxAge || (typeof maxAge !== "number" || maxAge <= 0))
throw new Error("SessionContentHandler: maxAge must be a positive number");
this.maxAge = maxAge; // seconds

@@ -45,6 +27,6 @@ this.maxAmount = maxAmount;

if (!req.session)
throw new Error('OIDC strategy requires session support. Did you forget to use session middleware such as express-session?');
throw new Error("OIDC strategy requires session support. Did you forget to use session middleware such as express-session?");
// the array in session
var array = req.session[sessionKey] && req.session[sessionKey]['content'];
let array = req.session[sessionKey] && req.session[sessionKey]["content"];
if (!array)

@@ -57,7 +39,7 @@ array = [];

// find the tuple by state value
var tuple = aadutils.findAndDeleteTupleByState(array, stateToFind);
const tuple = aadutils.findAndDeleteTupleByState(array, stateToFind);
// clear empty array, and clear the session if there is nothing inside
if (req.session[sessionKey] && array.length === 0)
delete req.session[sessionKey]['content'];
delete req.session[sessionKey]["content"];
if (req.session[sessionKey] && Object.keys(req.session[sessionKey]).length === 0)

@@ -74,6 +56,6 @@ delete req.session[sessionKey];

req.session[sessionKey] = {};
if (!req.session[sessionKey]['content'])
req.session[sessionKey]['content'] = [];
if (!req.session[sessionKey]["content"])
req.session[sessionKey]["content"] = [];
var array = req.session[sessionKey]['content'];
const array = req.session[sessionKey]["content"];
aadutils.processArray(array, this.maxAmount-1, this.maxAge);

@@ -80,0 +62,0 @@

@@ -1,27 +0,9 @@

/**
* Copyright (c) Microsoft Corporation
* All Rights Reserved
* MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this
* software and associated documentation files (the "Software"), to deal in the Software
* without restriction, including without limitation the rights to use, copy, modify,
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
* OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT
* OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/
'use strict';
"use strict";
const UrlValidator = require('valid-url');
const UrlValidator = require("valid-url");

@@ -38,3 +20,3 @@ const types = {};

Object.keys(this.config).forEach((item) => {
if (!this.config.hasOwnProperty(item)) {
if (!Object.prototype.hasOwnProperty.call(this.config, item)) {
throw new TypeError(`Missing value for ${item}`);

@@ -57,27 +39,27 @@ }

Validator.isNonEmpty = 'isNonEmpty';
Validator.isNonEmpty = "isNonEmpty";
types.isNonEmpty = {
validate: (value) => {
return value !== '' && value !== undefined && value !== null;
return value !== "" && value !== undefined && value !== null;
},
error: 'The value cannot be empty',
error: "The value cannot be empty",
};
Validator.isTypeLegal = 'isTypeLegal';
Validator.isTypeLegal = "isTypeLegal";
types.isTypeLegal = {
validate: (value) => {
return value === 'id_token' || value === 'id_token code' || value === 'code id_token' || value === 'code';
return value === "id_token" || value === "id_token code" || value === "code id_token" || value === "code";
},
error: 'The responseType: must be either id_token, id_token code, code id_token or code.',
error: "The responseType: must be either id_token, id_token code, code id_token or code.",
};
Validator.isModeLegal = 'isModeLegal';
Validator.isModeLegal = "isModeLegal";
types.isModeLegal = {
validate: (value) => {
return value === 'query' || value === 'form_post';
return value === "query" || value === "form_post";
},
error: 'The responseMode: must be either query or form_post.',
error: "The responseMode: must be either query or form_post.",
};
Validator.isURL = 'isURL';
Validator.isURL = "isURL";
types.isURL = {

@@ -87,6 +69,6 @@ validate: (value) => {

},
error: 'The URL must be valid and be https:// or http://',
error: "The URL must be valid and be https:// or http://",
};
Validator.isHttpURL = 'isHttpURL';
Validator.isHttpURL = "isHttpURL";
types.isHttpURL = {

@@ -96,6 +78,6 @@ validate: (value) => {

},
error: 'The URL must be valid and be http://',
error: "The URL must be valid and be http://",
};
Validator.isHttpsURL = 'isHttpsURL';
Validator.isHttpsURL = "isHttpsURL";
types.isHttpsURL = {

@@ -105,6 +87,6 @@ validate: (value) => {

},
error: 'The URL must be valid and be https://',
error: "The URL must be valid and be https://",
};
Validator.isHttpsURLIfExists = 'isHttpsURLIfExists';
Validator.isHttpsURLIfExists = "isHttpsURLIfExists";
types.isHttpsURLIfExists = {

@@ -117,5 +99,5 @@ validate: (value) => {

},
error: 'The URL must be valid and be https://',
error: "The URL must be valid and be https://",
};
exports.Validator = Validator;
{
"name": "passport-azure-ad",
"version": "4.3.1-beta.0",
"version": "4.3.1",
"license": "MIT",

@@ -28,4 +28,7 @@ "keywords": [

"chai-passport-strategy": "1.x.x",
"eslint": "^7.32.0",
"eslint-plugin-header": "^3.1.1",
"eslint-plugin-security": "^1.4.0",
"grunt": "^1.0.1",
"grunt-contrib-nodeunit": "^2.1.0",
"grunt-contrib-nodeunit": "^3.0.0",
"grunt-mocha-test": "^0.12.7",

@@ -40,3 +43,3 @@ "mocha": "^5.2.0",

"cache-manager": "2.10.2",
"https-proxy-agent": "^2.2.2",
"https-proxy-agent": "^5.0.0",
"jws": "^3.1.3",

@@ -46,3 +49,3 @@ "lodash": "^4.11.2",

"oauth": "0.9.15",
"passport": "^0.3.2",
"passport": "^0.4.1",
"request": "^2.72.0",

@@ -52,3 +55,4 @@ "valid-url": "^1.0.6"

"scripts": {
"test": "grunt run_tests"
"test": "grunt run_tests",
"lint": "./node_modules/.bin/eslint lib/**"
},

@@ -55,0 +59,0 @@ "engines": {

Sorry, the diff of this file is too big to display

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc