diff --git a/package-lock.json b/package-lock.json index 01bffb2c..f34685ad 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "rules-templates", - "version": "0.15.0", + "version": "0.16.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 0e809066..eacf5fc7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rules-templates", - "version": "0.15.0", + "version": "0.16.0", "description": "Auth0 Rules Repository", "main": "./rules", "scripts": { diff --git a/rules.json b/rules.json index f5635170..145c6afe 100644 --- a/rules.json +++ b/rules.json @@ -217,7 +217,7 @@ "multifactor" ], "description": "
This rule will challenge for a second authentication factor on request (step up) when\nacr_values = 'http://schemas.openid.net/pape/policies/2007/06/multi-factor' is sent in\nthe request. Before the challenge is made, 'context.authentication.methods' is checked\nto determine when the user has already successfully completed a challenge in the\ncurrent session.
", - "code": "function guardianMultifactorStepUpAuthentication(user, context, callback) {\n // This rule initiates multi-factor authenticaiton as a second factor\n // whenever the request contains the following value:\n //\n // acr_values = 'http://schemas.openid.net/pape/policies/2007/06/multi-factor'\n //\n // and multi-factor authentication has not already been completed in the\n // current session/\n\n if (\n context.request.query.acr_values ===\n 'http://schemas.openid.net/pape/policies/2007/06/multi-factor' &&\n !context.authentication.methods.some((method) => method.name === 'mfa')\n ) {\n context.multifactor = {\n provider: 'any',\n allowRememberBrowser: false\n };\n }\n\n callback(null, user, context);\n}" + "code": "function guardianMultifactorStepUpAuthentication(user, context, callback) {\n // This rule initiates multi-factor authenticaiton as a second factor\n // whenever the request contains the following value:\n //\n // acr_values = 'http://schemas.openid.net/pape/policies/2007/06/multi-factor'\n //\n // and multi-factor authentication has not already been completed in the\n // current session/\n\n const isMfa =\n context.request.query.acr_values ===\n 'http://schemas.openid.net/pape/policies/2007/06/multi-factor';\n\n let authMethods = [];\n if (context.authentication && Array.isArray(context.authentication.methods)) {\n authMethods = context.authentication.methods;\n }\n\n if (isMfa && !authMethods.some((method) => method.name === 'mfa')) {\n context.multifactor = {\n provider: 'any',\n allowRememberBrowser: false\n };\n }\n\n callback(null, user, context);\n}" }, { "id": "guardian-multifactor", @@ -258,7 +258,7 @@ "multifactor" ], "description": "This rule can be used to avoid prompting a user for multifactor authentication if they have successfully completed MFA in their current session.
\nThis is particularly useful when performing silent authentication (prompt=none
) to renew short-lived access tokens in a SPA (Single Page Application) during the duration of a user's session without having to rely on setting allowRememberBrowser
to true
.
Please see the Caisson integration for more information and detailed installation instructions.
\nRequired configuration (this Rule will be skipped if any of the below are not defined):
\nCAISSON_PUBLIC_KEY
Found on the Caisson Developer tab aboveCAISSON_PRIVATE_KEY
Found on the Caisson Developer tab aboveCAISSON_LOGIN_FREQUENCY_DAYS
Set to \"-1\" to check ID on registration only, \"0\" to check on all logins, and another positive integer for a minimum number of days between ID checksOptional configuration:
\nCAISSON_DEBUG
Set to \"true\" to log errors in the consoleall configuration items are optional:\nAURAYA_URL = optional. EVA endpoint, typically: https://eva-web.mydomain.com/server/oauth\nAURAYA_CLIENT_ID = optional. JWT client id on the EVA server (and this server)\nAURAYA_CLIENT_SECRET = optional. JWT client secret on the EVA server (and this server)\nAURAYA_ISSUER = optional. this app (or \"issuer\")\n\nAURAYA_RANDOM_DIGITS = optional. true|false whether to prompt for random digits\nAURAYA_COMMON_DIGITS = optional. true|false whether to prompt for common digits\nAURAYA_PERSONAL_DIGITS = optional. a user.user_metadata property that contains digits such as phone_number\nAURAYA_COMMON_DIGITS_PROMPT = optional. a digit string to prompt for common digits (e.g '987654321')\nAURAYA_PERSONAL_DIGITS_PROMPT = optional. a string to prompt for personal digits (e.g 'your cell number')\n\nAURAYA_DEBUG = optional. if set, controls detailed debug output\n
",
+ "code": "function evaVoiceBiometric(user, context, callback) {\n const debug = typeof configuration.AURAYA_DEBUG !== 'undefined';\n if (debug) {\n console.log(user);\n console.log(context);\n console.log(configuration);\n }\n\n const eva_url =\n configuration.AURAYA_URL ||\n 'https://eval-eva-web.aurayasystems.com/server/oauth';\n const clientSecret =\n configuration.AURAYA_CLIENT_SECRET ||\n 'o4X0LFKi2caP5ipUwaF4B27cZmfOIh0JXnqmfiC4mHkVskSzbp72Emk3AB6';\n const clientId = configuration.AURAYA_CLIENT_ID || 'auraya';\n const issuer = configuration.AURAYA_ISSUER || 'issuer';\n\n // Prepare user's enrolment status\n user.user_metadata = user.user_metadata || {};\n user.user_metadata.auraya_eva = user.user_metadata.auraya_eva || {};\n\n // User has initiated a login and is prompted to use voice biometrics\n // Send user's information and query params in a JWT to avoid tampering\n function createToken(user) {\n const options = {\n expiresInMinutes: 2,\n audience: clientId,\n issuer: issuer\n };\n\n return jwt.sign(user, clientSecret, options);\n }\n\n if (context.protocol === 'redirect-callback') {\n // user was redirected to the /continue endpoint with correct state parameter value\n\n var options = {\n //subject: user.user_id, // validating the subject is nice to have but not strictly necessary\n jwtid: user.jti // unlike state, this value can't be spoofed by DNS hacking or inspecting the payload\n };\n\n const payload = jwt.verify(\n context.request.body.token,\n clientSecret,\n options\n );\n if (debug) {\n console.log(payload);\n }\n\n if (payload.reason === 'enrolment_succeeded') {\n user.user_metadata.auraya_eva.status = 'enrolled';\n\n console.log('Biometric user successfully enrolled');\n // persist the user_metadata update\n auth0.users\n .updateUserMetadata(user.user_id, user.user_metadata)\n .then(function () {\n callback(null, user, context);\n })\n .catch(function (err) {\n callback(err);\n });\n\n return;\n }\n\n if (payload.reason !== 'verification_accepted') {\n // logic to detect repeatedly rejected attempts could go here\n // and update the eva.status accordingly (perhaps with 'blocked')\n console.log(`Biometric rejection reason: ${payload.reason}`);\n return callback(new UnauthorizedError(payload.reason), user, context);\n }\n\n // verification accepted\n console.log('Biometric verification accepted');\n return callback(null, user, context);\n }\n\n const url = require('url@0.10.3');\n user.jti = uuid.v4();\n user.user_metadata.auraya_eva.status =\n user.user_metadata.auraya_eva.status || 'initial';\n const mode =\n user.user_metadata.auraya_eva.status === 'initial' ? 'enrol' : 'verify';\n\n // returns property of the user.user_metadata object, typically \"phone_number\"\n // default is '', (server skips this prompt)\n const personalDigits =\n typeof configuration.AURAYA_PERSONAL_DIGITS === 'undefined'\n ? ''\n : user.user_metadata[configuration.AURAYA_PERSONAL_DIGITS];\n\n // default value for these is 'true'\n const commonDigits = configuration.AURAYA_COMMON_DIGITS || 'true';\n const randomDigits = configuration.AURAYA_RANDOM_DIGITS || 'true';\n\n // default value for these is '' (the server default)\n const commonDigitsPrompt = configuration.AURAYA_COMMON_DIGITS_PROMPT || ''; // 123456789\n const personalDigitsPrompt =\n configuration.AURAYA_PERSONAL_DIGITS_PROMPT || ''; // 'your phone number'\n\n const token = createToken({\n sub: user.user_id,\n jti: user.jti,\n oauth: {\n state: '', // not used in token, only in the GET request\n callbackURL: url.format({\n protocol: 'https',\n hostname: context.request.hostname,\n pathname: '/continue'\n }),\n nonce: user.jti // performs same function as jti\n },\n biometric: {\n id: user.user_id, // email - can be used for identities that cross IdP boundaries\n mode: mode,\n personalDigits: personalDigits,\n personalDigitsPrompt: personalDigitsPrompt,\n commonDigits: commonDigits,\n commonDigitsPrompt: commonDigitsPrompt,\n randomDigits: randomDigits\n }\n });\n\n context.redirect = {\n url: `${eva_url}?token=${token}`\n };\n\n return callback(null, user, context);\n}"
+ },
{
"id": "iddataweb-verification-workflow",
"title": "ID DataWeb Verification Workflow",
diff --git a/src/rules/eva-voice-biometric.js b/src/rules/eva-voice-biometric.js
index 3e1f7fe8..959c6c59 100644
--- a/src/rules/eva-voice-biometric.js
+++ b/src/rules/eva-voice-biometric.js
@@ -20,7 +20,6 @@
* AURAYA_DEBUG = optional. if set, controls detailed debug output
*/
function evaVoiceBiometric(user, context, callback) {
-
const debug = typeof configuration.AURAYA_DEBUG !== 'undefined';
if (debug) {
console.log(user);
@@ -28,12 +27,15 @@ function evaVoiceBiometric(user, context, callback) {
console.log(configuration);
}
- const eva_url = configuration.AURAYA_URL || 'https://eval-eva-web.aurayasystems.com/server/oauth';
- const clientSecret = configuration.AURAYA_CLIENT_SECRET || 'o4X0LFKi2caP5ipUwaF4B27cZmfOIh0JXnqmfiC4mHkVskSzbp72Emk3AB6';
+ const eva_url =
+ configuration.AURAYA_URL ||
+ 'https://eval-eva-web.aurayasystems.com/server/oauth';
+ const clientSecret =
+ configuration.AURAYA_CLIENT_SECRET ||
+ 'o4X0LFKi2caP5ipUwaF4B27cZmfOIh0JXnqmfiC4mHkVskSzbp72Emk3AB6';
const clientId = configuration.AURAYA_CLIENT_ID || 'auraya';
const issuer = configuration.AURAYA_ISSUER || 'issuer';
-
-
+
// Prepare user's enrolment status
user.user_metadata = user.user_metadata || {};
user.user_metadata.auraya_eva = user.user_metadata.auraya_eva || {};
@@ -98,22 +100,26 @@ function evaVoiceBiometric(user, context, callback) {
const url = require('url@0.10.3');
user.jti = uuid.v4();
- user.user_metadata.auraya_eva.status = user.user_metadata.auraya_eva.status || 'initial';
- const mode = user.user_metadata.auraya_eva.status === 'initial' ? 'enrol' : 'verify';
+ user.user_metadata.auraya_eva.status =
+ user.user_metadata.auraya_eva.status || 'initial';
+ const mode =
+ user.user_metadata.auraya_eva.status === 'initial' ? 'enrol' : 'verify';
// returns property of the user.user_metadata object, typically "phone_number"
// default is '', (server skips this prompt)
- const personalDigits =
- typeof configuration.AURAYA_PERSONAL_DIGITS === 'undefined'? ''
+ const personalDigits =
+ typeof configuration.AURAYA_PERSONAL_DIGITS === 'undefined'
+ ? ''
: user.user_metadata[configuration.AURAYA_PERSONAL_DIGITS];
// default value for these is 'true'
const commonDigits = configuration.AURAYA_COMMON_DIGITS || 'true';
const randomDigits = configuration.AURAYA_RANDOM_DIGITS || 'true';
-
+
// default value for these is '' (the server default)
const commonDigitsPrompt = configuration.AURAYA_COMMON_DIGITS_PROMPT || ''; // 123456789
- const personalDigitsPrompt = configuration.AURAYA_PERSONAL_DIGITS_PROMPT || ''; // 'your phone number'
+ const personalDigitsPrompt =
+ configuration.AURAYA_PERSONAL_DIGITS_PROMPT || ''; // 'your phone number'
const token = createToken({
sub: user.user_id,
@@ -135,7 +141,6 @@ function evaVoiceBiometric(user, context, callback) {
commonDigits: commonDigits,
commonDigitsPrompt: commonDigitsPrompt,
randomDigits: randomDigits
-
}
});