Skip to content

Instantly share code, notes, and snippets.

@jeff-auth0
Last active November 24, 2022 05:59
Show Gist options
  • Save jeff-auth0/13f9643abb9a473635dba48965bb9075 to your computer and use it in GitHub Desktop.
Save jeff-auth0/13f9643abb9a473635dba48965bb9075 to your computer and use it in GitHub Desktop.
This login script will be used to generate auth0 tokens in return of AAD id_token generated for user logged in to their Windows 10 devices.
/*
*
* Purpose of this script:
* This login script will be used to generate auth0 tokens in return of AAD id_token generated
* for user logged in to their Windows 10 devices.
*
* Why:
* This is similar to native SIWA, that a desktop windows application performs API based request to OS
* to authenticate current user, user sees a consent page, and after consent, application receives
* id_token of current user’s Azure AD account associated with Windows 11 machine.
*
*
* @param {string} email - Email of the user
* @param {string} password - Microsoft azure ad id_token
*/
function login(email, token, callback) {
const jwksClient = require("jwks-rsa");
const jwt = require("jsonwebtoken");
const validator = require("validator");
// Key set is cached for 24 hours
// as per https://learn.microsoft.com/en-us/azure/active-directory/develop/access-tokens
global.cache = global.cache || {};
global.cache.utiCache = global.cache.utiCache || [];
const options = {
audience: configuration.client_id,
issuer:
"https://login.microsoftonline.com/9188040d-6c67-4c5b-b112-36a304b66dad/v2.0",
algorithms: ["RS256"],
};
const getKey = (token, callback) => {
// get the decoded payload and header
var decoded = jwt.decode(token, { complete: true });
try {
const currentTime = new Date().getTime();
const kid = decoded.header.kid;
if (
global.cache.jwks &&
global.cache.jwks.kid === kid &&
global.cache.jwks.exp > currentTime
) {
return callback(null, global.cache.jwks.signingKey);
} else {
const client = jwksClient({
jwksUri: `https://login.microsoftonline.com/9188040d-6c67-4c5b-b112-36a304b66dad/discovery/v2.0/keys`,
});
client.getSigningKey(kid, function (err, key) {
var signingKey = key.publicKey || key.rsaPublicKey;
global.cache.jwks = {
signingKey: signingKey,
exp: currentTime + 60 * 60 * 24 * 1000, // Key expires in 24 hours
kid,
};
return callback(null, signingKey);
});
}
} catch (e) {
throw new Error(e.message);
}
};
function checkJwtReuse(decodedToken) {
if (!decodedToken.uti) {
return callback(
new Error(
`This token does not have valid uti claim. Refresh your id_token from aad.`,
),
);
}
if (global.cache.utiCache.indexOf(decodedToken.uti) === -1) {
global.cache.utiCache.push(decodedToken.uti);
return;
}
// If decodedToken.uti is not present OR global.cache.utiCache.indexOf(decodedToken.uti) !== -1
return callback(
new Error(
`This token was previously used with uti claim: ${decodedToken.uti}. Refresh your id_token from aad`,
),
);
}
getKey(token, (error, key) => {
if (error) {
return callback(new Error(`Unable to get the key. Error: ${error}`));
}
jwt.verify(token, key, options, function (err, decoded) {
if (err) {
return callback(
new Error(`Unable to verify Azure AD token. Error: ${err}`),
);
}
checkJwtReuse(decoded);
const decodedEmailOrUsername = decoded.email
? decoded.email
: decoded.preferred_username;
const emailVerified = validator.isEmail(decodedEmailOrUsername);
if (decodedEmailOrUsername && decodedEmailOrUsername === email) {
return callback(null, {
user_id: `${decoded.sub}`,
email: decodedEmailOrUsername,
email_verified: emailVerified,
});
} else {
return callback(new Error("Unable to match email with token subject."));
}
});
});
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment