Skip to content

Instantly share code, notes, and snippets.

@jatinvaidya
Last active December 19, 2022 00:36
Show Gist options
  • Save jatinvaidya/24347fd64342b4bbe8ebbe3b16bd2944 to your computer and use it in GitHub Desktop.
Save jatinvaidya/24347fd64342b4bbe8ebbe3b16bd2944 to your computer and use it in GitHub Desktop.
Auth0 M2M Hook: Access Token Exchange Controls
/**
@param {object} client - information about the client
@param {string} client.name - name of client
@param {string} client.id - client id
@param {string} client.tenant - Auth0 tenant name
@param {object} client.metadata - client metadata
@param {array|undefined} scope - array of strings representing the scope claim or undefined
@param {string} audience - token's audience claim
@param {object} context - additional authorization context
@param {object} context.webtask - webtask context
@param {function} cb - function (error, accessTokenClaims)
*/
////////// DRAFT CODE ////////////////
////////// 2B improved ///////////////
module.exports = function(client, scope, audience, context, cb) {
// extract AT from the on_behalf_of field in the body
let sourceAccessToken = context.body.subject_token;
console.debug(`sourceAccessToken: ${sourceAccessToken}`);
let scopeMapping = JSON.parse(client.metadata[audience]);
console.info(`scopeMapping: ${JSON.stringify(scopeMapping)}`);
// Verify the Source Access Token (aka subject_token)
if (!!scopeMapping) {
// validate sourceAccessToken as per usual best practices (not shown)
// Verify using getKey callback
// Example uses https://github.com/auth0/node-jwks-rsa as a way to fetch the keys.
var jwt = require("jsonwebtoken");
var jwksClient = require("jwks-rsa");
var client = jwksClient({
// hardcoded here, but we can use JWKS URI for other "trusted" Authorization Servers
// to verify their JWT Access Tokens
jwksUri: "https://jv-delegation.au.auth0.com/.well-known/jwks.json",
});
function getKey(header, callback) {
client.getSigningKey(header.kid, function (err, key) {
var signingKey = key.publicKey || key.rsaPublicKey;
callback(null, signingKey);
});
}
jwt.verify(
sourceAccessToken,
getKey,
{ algorithms: ["RS256"] },
function (err, decodedSourceAccessToken) {
if (!!err) {
// bad or untrusted sibject_token
return cb(new InvalidRequestError("Invalid subject_token"));
}
// get the target resource server identifier
let targetResourceServerIdentifier = audience;
console.info(
`targetResourceServerIdentifier: ${targetResourceServerIdentifier}`
);
// get the target scopes requested
let targetScopesRequested = scope || [];
console.info(`targetScopesRequested: ${targetScopesRequested}`);
// get access control matrix for this resource server
// sample:
/*{
"read:bills": [
"read:reports",
"create:reports"
],
"update:bills": [
"update:reports"
]
} */
let sourceScopesGranted = decodedSourceAccessToken.scope || "";
sourceScopesGranted = sourceScopesGranted.split(" ");
console.info(`sourceScopesConsented: ${sourceScopesGranted}`);
// delegated call from this source-api to target-api is allowed,
// now do scope related checks
let targetScopesAllowed = [];
sourceScopesGranted.forEach((sourceScope) =>
targetScopesAllowed.push(...scopeMapping[sourceScope])
);
console.info(`targetScopesAllowed: ${targetScopesAllowed}`);
// intersection of requested and allowed scopes
let targetScopesResultant = targetScopesRequested.filter(
(targetScope) => targetScopesAllowed.includes(targetScope)
);
console.info(`targetScopesResultant: ${targetScopesResultant}`);
let accessToken = {
scope: targetScopesResultant,
on_behalf_of: decodedSourceAccessToken.sub,
};
cb(null, accessToken);
}
);
} else {
return cb(new InvalidRequestError("Unauthorized to get an access token to call target api"));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment