Skip to content

Commit 29e18ae

Browse files
committed
update rules add otp custom page
1 parent 3a301ea commit 29e18ae

File tree

9 files changed

+342
-19
lines changed

9 files changed

+342
-19
lines changed

build.sh

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ perl -pi -e "s/\{\{AUTH0DOMAIN\}\}/$AUTH0DOMAIN/g" $SIGNUPFILENAME
1515
CHECKEMAIL="./web-assets/static-pages/check_email.html"
1616
perl -pi -e "s/\{\{DOMAIN\}\}/$DOMAIN/g" $CHECKEMAIL
1717

18+
OTPFILENAME="./web-assets/js/otp.js"
19+
perl -pi -e "s/\{\{DOMAIN\}\}/$DOMAIN/g" $OTPFILENAME
20+
perl -pi -e "s/\{\{AUTH0DOMAIN\}\}/$AUTH0DOMAIN/g" $OTPFILENAME
21+
1822
mkdir dist
1923
cp -rv ./web-assets/css/* ./dist/
2024
cp -rv ./web-assets/js/* ./dist/

web-assets/auth0/dev-tenant/rules/DICE DID.js

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ function (user, context, callback) {
2121
// User was redirected to the /continue endpoint
2222
console.log("rule:DICE DID:User was redirected to the /continue endpoint");
2323
if (context.request.query.diceVerificationStatus === 'false') {
24-
return callback('Login Error: Whoops! Something went wrong. Please connect to DICE Platform Admin <a href="mailto:info@diceid.com">dice.wallet@wipro.com</a>.<br> Back to application ', user, context);
24+
return callback('Login Error: Credentials verification is failed.<br>Please contact with support <a href="mailto:support@topcoder.com">support@topcoder.com</a>.<br> Back to application ', user, context);
2525
} else if (context.request.query.otp) {
2626
request.post({
2727
url: 'https://api.' + configuration.DOMAIN + '/v3/users/checkOtp',
@@ -82,8 +82,14 @@ function (user, context, callback) {
8282
return callback('Login Error: Whoops! Something went wrong.', user, context);
8383
}
8484
console.log("rule:DICE DID: redirecting to OTP page");
85+
const hostName = _.get(context, "request.hostname", null);
86+
const otpCompletetUrl = "https://" + hostName + "/continue";
87+
const retUrl = _.get(context, "request.query.returnUrl", null);
88+
const otpRedirectUrl = configuration.CUSTOM_PAGES_BASE_URL +
89+
"/otp.html?formAction=" + otpCompletetUrl +
90+
"&returnUrl=" + retUrl;
8591
context.redirect = {
86-
url: `https://accounts-auth0.${configuration.DOMAIN}/check_email.html`
92+
url: otpRedirectUrl
8793
};
8894
return callback(null, user, context);
8995
});
Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
function login(handleOrEmail, password, callback) {
2-
request.post({
3-
url: "https://api."+configuration.DOMAIN+"/v3/users/login",
2+
request.post({
3+
url: "https://api." + configuration.DOMAIN + "/v3/users/login",
44
form: {
55
handleOrEmail: handleOrEmail,
66
password: password
@@ -12,16 +12,19 @@ request.post({
1212
if (err) return callback(err);
1313
if (response.statusCode === 401) return callback();
1414
var user = JSON.parse(body);
15-
user.result.content.roles = user.result.content.roles.map(function(role) {
15+
user.result.content.roles = user.result.content.roles.map(function (role) {
1616
return role.roleName;
1717
});
1818

19-
callback(null, {
19+
callback(null, {
2020
user_id: user.result.content.id,
2121
nickname: user.result.content.handle,
2222
email: user.result.content.email,
2323
roles: user.result.content.roles,
2424
email_verified: user.result.content.emailActive,
25+
created_at: user.result.content.createdAt,
26+
mfa_enabled: user.result.content.mfaEnabled,
27+
mfa_verified: user.result.content.mfaVerified
2528
});
2629
});
27-
}
30+
}
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
function (user, context, callback) {
2+
if (context.clientID === configuration.CLIENT_ACCOUNTS_LOGIN) {
3+
console.log("rule:DICE DID:enter");
4+
if (context.redirect) {
5+
console.log("rule:DICE DID:exiting due to context being a redirect");
6+
return callback(null, user, context);
7+
}
8+
const _ = require('lodash');
9+
const isAuth0 = (_.get(user, "identities[0].provider") === 'auth0') ? true : false;
10+
const isSocial = _.get(user, "identities[0].isSocial");
11+
const mfaEnabled = _.get(user, "mfa_enabled", false);
12+
const mfaVerified = _.get(user, "mfa_verified", false);
13+
if (!isAuth0 && !isSocial) {
14+
console.log("rule:DICE DID:exiting due to enterprise user");
15+
return callback(null, user, context);
16+
}
17+
if (mfaEnabled && mfaVerified) {
18+
if (context.protocol === "redirect-callback") {
19+
// User was redirected to the /continue endpoint
20+
console.log("rule:DICE DID:User was redirected to the /continue endpoint");
21+
if (context.request.query.diceVerificationStatus === 'false') {
22+
return callback('Login Error: Credentials verification is failed.<br>Please contact with support <a href="mailto:support@topcoder.com">support@topcoder.com</a>.<br> Back to application ', user, context);
23+
} else if (context.request.query.otp) {
24+
request.post({
25+
url: 'https://api.' + configuration.DOMAIN + '/v3/users/checkOtp',
26+
json: {
27+
"param": {
28+
"userId": user.userId,
29+
"otp": context.request.query.otp
30+
}
31+
}
32+
}, function (error, response, body) {
33+
if (error) return callback(error, user, context);
34+
if (response.statusCode !== 200) {
35+
return callback('Login Error: Whoops! Something went wrong.', user, context);
36+
}
37+
if (body.result.content.verified === true) {
38+
return callback(null, user, context);
39+
} else {
40+
return callback('Login Error: wrong OTP', user, context);
41+
}
42+
});
43+
} else {
44+
const jwt_decode = require('jwt-decode');
45+
request.post({
46+
url: 'https://tc-vcauth.diceid.com/vc/connect/token',
47+
form: {
48+
code: context.request.query.code,
49+
grant_type: 'authorization_code',
50+
client_id: 'topcoder'
51+
}
52+
}, function (error, response, body) {
53+
if (error) return callback(error, user, context);
54+
if (response.statusCode !== 200) {
55+
return callback('Login Error: Whoops! Something went wrong.', user, context);
56+
}
57+
const result = JSON.parse(body);
58+
const decoded = jwt_decode(result.id_token);
59+
console.log("Decoded: ", decoded);
60+
if (decoded.Email !== user.email) {
61+
return callback('Login Error: Credetials do not match', user, context);
62+
}
63+
console.log("rule:DICE DID:credentials approved");
64+
return callback(null, user, context);
65+
});
66+
}
67+
} else {
68+
const maxRetry = 2;
69+
const useOtp = function () {
70+
request.post({
71+
url: 'https://api.' + configuration.DOMAIN + '/v3/users/sendOtp',
72+
json: {
73+
"param": {
74+
"userId": user.userId
75+
}
76+
}
77+
}, function (error, response, body) {
78+
if (error) return callback(error, user, context);
79+
if (response.statusCode !== 200) {
80+
return callback('Login Error: Whoops! Something went wrong.', user, context);
81+
}
82+
console.log("rule:DICE DID: redirecting to OTP page");
83+
const hostName = _.get(context, "request.hostname", null);
84+
const otpCompletetUrl = "https://" + hostName + "/continue";
85+
const retUrl = _.get(context, "request.query.returnUrl", null);
86+
const otpRedirectUrl = configuration.CUSTOM_PAGES_BASE_URL +
87+
"/otp.html?formAction=" + otpCompletetUrl +
88+
"&returnUrl=" + retUrl;
89+
context.redirect = {
90+
url: otpRedirectUrl
91+
};
92+
return callback(null, user, context);
93+
});
94+
};
95+
const checkDiceHealth = function (attempt) {
96+
console.log("rule:DICE DID:checking dice health, attempt:" + attempt);
97+
request.get({
98+
url: 'https://tc-vcauth.diceid.com/.well-known/openid-configuration'
99+
}, function (error, response, body) {
100+
if (error || response.statusCode !== 200) {
101+
if (attempt >= maxRetry) {
102+
console.log("rule:DICE DID:dice services down, using otp flow...");
103+
useOtp();
104+
} else {
105+
checkDiceHealth(attempt + 1);
106+
}
107+
} else {
108+
console.log("rule:DICE DID:exiting with redirecting user to QR code page.");
109+
context.redirect = {
110+
url: `https://tc-vcauth.diceid.com/vc/connect/authorize?pres_req_conf_id=Topcoder_2FA&client_id=topcoder&redirect_uri=https%3A%2F%2Fauth.topcoder.com%2Fcontinue&response_type=code&scope=openid%20profile%20vc_authn`
111+
};
112+
return callback(null, user, context);
113+
}
114+
});
115+
};
116+
if (!global.ENABLE_2FA) {
117+
console.log("rule:DICE DID:dice switch disabled, using otp flow...");
118+
useOtp();
119+
} else {
120+
checkDiceHealth(1);
121+
}
122+
}
123+
} else {
124+
console.log("rule:DICE DID:exiting due to mfa is not enabled");
125+
return callback(null, user, context);
126+
}
127+
} else {
128+
return callback(null, user, context);
129+
}
130+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
function (user, context, callback) {
2+
// Set the Enable_2FA to false globally - safety switch to turn off
3+
global.ENABLE_2FA = false;
4+
return callback(null, user, context);
5+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
function (user, context, callback) {
2+
if (!global.init) {
3+
global.AUTH0_CLAIM_NAMESPACE = "https://topcoder.com/";
4+
global.CUSTOM_CLAIMS_PROTOCOLS = ['oauth2-refresh-token', 'oauth2-device-code', 'oidc-basic-profile'];
5+
global.ENABLE_2FA = true;
6+
global.init = true;
7+
}
8+
callback(null, user, context);
9+
}

web-assets/auth0/prod-tenant/rules/custom.js renamed to web-assets/auth0/prod-tenant/rules/New-Account-App-Custom-Claims.js

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,12 @@ function (user, context, callback) {
3131
}
3232

3333
let res = JSON.parse(body);
34+
// Assign MFA flags to user objects - PLAT-1420
35+
user.mfa_enabled = res.result.content.mfaEnabled;
36+
user.mfa_verified = res.result.content.mfaVerified;
3437
// TODO need to double sure about multiple result or no result
3538
let userId = res.result.content.id;
39+
user.userId = userId;
3640
let handle = res.result.content.handle;
3741
let roles = res.result.content.roles.map(function (role) {
3842
return role.roleName;
@@ -42,11 +46,11 @@ function (user, context, callback) {
4246
// TEMP
4347
let tcsso = res.result.content.regSource || '';
4448

45-
// block wipro/topgear contractor user
46-
const topgearBlockMessage = 'Topgear can be accessed only by Wipro Employees. If you are a Wipro employee and not able to access, drop an email to <a href="mailto:ask.topgear@wipro.com"> ask.topgear@wipro.com </a> with the error message.Back to application ';
47-
if (roles.indexOf(configuration.TOPGEAR_CONTRACTOR_ROLE) > -1) {
48-
return callback(topgearBlockMessage, user, context);
49-
}
49+
// block wipro/topgear contractor user
50+
const topgearBlockMessage = 'Topgear can be accessed only by Wipro Employees. If you are a Wipro employee and not able to access, drop an email to <a href="mailto:ask.topgear@wipro.com"> ask.topgear@wipro.com </a> with the error message.Back to application ';
51+
if (roles.indexOf(configuration.TOPGEAR_CONTRACTOR_ROLE) > -1) {
52+
return callback(topgearBlockMessage, user, context);
53+
}
5054

5155
context.idToken[global.AUTH0_CLAIM_NAMESPACE + 'roles'] = roles;
5256
context.idToken[global.AUTH0_CLAIM_NAMESPACE + 'userId'] = userId;
@@ -55,14 +59,14 @@ function (user, context, callback) {
5559
context.idToken[global.AUTH0_CLAIM_NAMESPACE + 'tcsso'] = tcsso;
5660
context.idToken[global.AUTH0_CLAIM_NAMESPACE + 'active'] = userStatus;
5761
context.idToken.nickname = handle;
58-
59-
if (!userStatus) {
60-
context.redirect = {
61-
url: `https://accounts-auth0.${configuration.DOMAIN}/check_email.html`
62-
};
63-
return callback(null, user, context);
62+
63+
if (!userStatus) {
64+
context.redirect = {
65+
url: `https://accounts-auth0.${configuration.DOMAIN}/check_email.html`
66+
};
67+
return callback(null, user, context);
6468
}
65-
69+
6670
//console.log(user, context);
6771
return callback(null, user, context);
6872
}

web-assets/js/otp.js

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
var qs = (function (a) {
2+
if (a == "") return {};
3+
var b = {};
4+
for (var i = 0; i < a.length; ++i) {
5+
var p = a[i].split("=", 2);
6+
if (p.length == 1) b[p[0]] = "";
7+
else b[p[0]] = decodeURIComponent(p[1].replace(/\+/g, " "));
8+
}
9+
return b;
10+
})(window.location.search.substr(1).split("&"));
11+
$(document).ready(function () {
12+
window.history.forward();
13+
$("#continueBtn").click(function () {
14+
var otp = $("#otp").val();
15+
if (!otp) {
16+
$("#error").html("Need Password");
17+
$("#error").closest(".message").fadeIn();
18+
return false;
19+
}
20+
$("#error").closest(".message").fadeOut();
21+
$("#error").html("");
22+
let formAction = qs["formAction"];
23+
console.log(formAction)
24+
const opt1 = 'https://auth.{{DOMAIN}}/continue';
25+
const opt2 = 'https://{{AUTH0DOMAIN}}/continue';
26+
if (!formAction.startsWith(opt1) && !formAction.startsWith(opt2)) {
27+
// looks like XSS attack
28+
console.log("err")
29+
formAction = "#";
30+
}
31+
$('#verifyOtp').attr('action', formAction);
32+
$("#state").val(qs["state"]);
33+
$("#returnUrl").val(qs["returnUrl"]);
34+
$("#verifyOtp").submit();
35+
return false;
36+
});
37+
38+
/**
39+
* Script for field placeholder
40+
**/
41+
$(".messages .close-error").on("click", function () {
42+
$(this).closest(".message").fadeOut();
43+
});
44+
var inputObj = $(".input-field .input-text"),
45+
continueBtnDisable = false;
46+
inputObj
47+
.on("focus", function () {
48+
$(this).parent().addClass("active focussed");
49+
})
50+
.on("blur", function () {
51+
var parentObj = $(this).parent();
52+
if ($(this).val() === "") {
53+
parentObj.removeClass("active");
54+
}
55+
parentObj.removeClass("focussed");
56+
})
57+
.on("change keydown paste input", function () {
58+
var disableStatus = false;
59+
inputObj.each(function (index, element) {
60+
if ($(element).val() === "") {
61+
disableStatus = true;
62+
return;
63+
} else {
64+
disableStatus = false;
65+
return;
66+
}
67+
});
68+
setContinueButtonDisabledStatus(disableStatus);
69+
})
70+
.each(function (index, element) {
71+
var parentObj = $(element).parent();
72+
if ($(element).val() !== "") {
73+
parentObj.addClass("active");
74+
} else {
75+
parentObj.removeClass("active");
76+
}
77+
78+
if ($(element).val() === "" && continueBtnDisable === false) {
79+
continueBtnDisable = true;
80+
}
81+
82+
setContinueButtonDisabledStatus(continueBtnDisable);
83+
});
84+
});
85+
function setContinueButtonDisabledStatus(status) {
86+
var continueBtnObj = $("#continueBtn");
87+
if (status) {
88+
continueBtnObj.attr("disabled", true);
89+
} else {
90+
continueBtnObj.removeAttr("disabled");
91+
}
92+
}

0 commit comments

Comments
 (0)