Last active
June 18, 2019 20:45
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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