Skip to content

Instantly share code, notes, and snippets.

@nicosabena
Last active June 18, 2019 20:45
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nicosabena/6e009c7cff13830891e49c16626d7cc6 to your computer and use it in GitHub Desktop.
Save nicosabena/6e009c7cff13830891e49c16626d7cc6 to your computer and use it in GitHub Desktop.
This rule delete all user grants on the next user token request after a password change
async function (user, context, callback) {
// this rule will run after a user changes their password and
// delete, for the user, either:
// - all grants (for OIDC-Conformant usage)
// - all device credentials (for non OIDC-Conformant apps)
// These actions will effectively invalidate all issued refresh tokens
// on the next token request (be it an interactive login
// or a refresh token flow).
// It compares a user's last_password_rest property
// against an "app_metadata.last_revoke" property used
// to note the last time the grant revoke was done.
// If they don't match, all grants are deleted and
// the property is updated to the last password reset date.
// Make sure to configure a valid APPLY_FOR_RESETS_AFTER date.
// All password resets after that date will cause this logic to
// execute, even if the resets happened before this rule is
// created.
// You'll need to provide a client ID and client secret
// of a non-interactive app that has been granted the following scopes:
// update:users, AND
// for OIDC-Conformant apps:
// read:grants, delete:grants
// for non OIDC-Conformant flows:
// read:device_credentials, delete:device_credentials
//
// Replace with proper configuration values
// https://auth0.com/docs/rules/guides/configuration
const REVOKE_GRANT_CLIENT_ID = configuration.REVOKE_GRANT_CLIENT_ID;
const REVOKE_GRANT_CLIENT_SECRET = configuration.REVOKE_GRANT_CLIENT_SECRET;
const APPLY_FOR_RESETS_AFTER = new Date("2016-06-10");
const CHECK_FOR_GRANTS = true;
const CHECK_FOR_DEVICE_CREDENTIALS = true;
var async = require("async@2.6.1");
user.app_metadata = user.app_metadata || {};
const last_revoke = user.app_metadata.last_revoke;
const password_reset_detected =
user.last_password_reset &&
user.last_password_reset !== last_revoke &&
// only for password changes after a certain date
new Date(user.last_password_reset) > APPLY_FOR_RESETS_AFTER;
if (!password_reset_detected) {
return callback(null, user, context);
}
// the last password change has not generated a
// token revoke yet. Let's process it.
var ManagementClient = require("auth0@2.17.0").ManagementClient;
console.log("Password change detected, will revoke all grants.");
console.log("Previous revoke on: " + last_revoke);
console.log("Last password change: " + user.last_password_reset);
var management = new ManagementClient({
clientId: REVOKE_GRANT_CLIENT_ID,
clientSecret: REVOKE_GRANT_CLIENT_SECRET,
domain: auth0.domain
});
var deleteAllGrants = async grants => {
console.log("Deleting all grants");
for (var grant of grants) {
console.log("Deleting grant " + grant.id);
try {
await management.deleteGrant({ id: grant.id, user_id: user.user_id });
} catch (err) {
console.log("Error deleting grant:" + err);
throw err;
}
}
};
var deleteAllDeviceCredentials = async deviceCredentials => {
console.log("Deleting all device credentials.");
for(var deviceCredential of deviceCredentials) {
console.log("Deleting device credential " + deviceCredential.id);
try {
await management.deleteDeviceCredential({ id: deviceCredential.id });
} catch (err) {
console.log("Error deleting device credential:" + err);
throw err;
}
}
};
try {
if (CHECK_FOR_GRANTS) {
console.log("Getting all grants");
var grants = await management.getGrants({
user_id: user.user_id
});
await deleteAllGrants(grants);
}
if (CHECK_FOR_DEVICE_CREDENTIALS) {
console.log("Getting all device credentials");
var deviceCredentials = await management.getDeviceCredentials({
user_id: user.user_id
});
await deleteAllDeviceCredentials(deviceCredentials);
}
console.log("Updating user metadata");
user.app_metadata.last_revoke = user.last_password_reset;
await management.users.updateAppMetadata(
{ id: user.user_id },
user.app_metadata
);
if (
context.protocol === "oauth2-refresh-token" ||
context.protocol === "delegation"
) {
// if the flow is refresh token, this is an attempt
// to use a token issued before the password change,
// so we'll deny it
console.log("Denying this authorization");
return callback(new UnauthorizedError("Invalid refresh token"));
}
console.log("Exiting rule with success.");
return callback(null, user, context);
} catch (err) {
console.log("Exiting rule with error: " + err);
return callback(err);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment