diff --git a/web-assets/auth0/dev-tenant/database/create.js b/web-assets/auth0/dev-tenant/database/create.js new file mode 100644 index 0000000..c178112 --- /dev/null +++ b/web-assets/auth0/dev-tenant/database/create.js @@ -0,0 +1,124 @@ +/*function create(user, callback) { + // This script should create a user entry in your existing database. It will + // be executed when a user attempts to sign up, or when a user is created + // through the Auth0 dashboard or API. + // When this script has finished executing, the Login script will be + // executed immediately afterwards, to verify that the user was created + // successfully. + // + // The user object will always contain the following properties: + // * email: the user's email + // * password: the password entered by the user, in plain text + // * tenant: the name of this Auth0 account + // * client_id: the client ID of the application where the user signed up, or + // API key if created through the API or Auth0 dashboard + // * connection: the name of this database connection + // + // There are three ways this script can finish: + // 1. A user was successfully created + // callback(null); + // 2. This user already exists in your database + // callback(new ValidationError("user_exists", "my error message")); + // 3. Something went wrong while trying to reach your database + // callback(new Error("my error message")); + + const msg = 'Please implement the Create script for this database connection ' + + 'at https://manage.auth0.com/#/connections/database'; + return callback(new Error(msg)); */ +function create(user, callback) { + //console.log("landed here..................................."); + var countryObj = JSON.parse(user.user_metadata.country); + var regSource = user.user_metadata.regSource; + var utmSource = user.user_metadata.utmSource; + var utmMedium = user.user_metadata.utmMedium; + var utmCampaign = user.user_metadata.utmCampaign; + var retUrl = user.user_metadata.returnUrl; + var afterActivationURL = retUrl != null ? retUrl : "https://" + configuration.DOMAIN + "/home"; + if (regSource === configuration.REG_BUSINESS) { + afterActivationURL = "https://connect." + configuration.DOMAIN; + } + var data = { + "param": { + "handle": user.username, + "email": user.email, + "credential": { + "password": user.password + }, + "firstName": user.user_metadata.firstName, + "lastName": user.user_metadata.lastName, + "country": { + "code": countryObj.code, + "isoAlpha3Code": countryObj.alpha3, + "isoAlpha2Code": countryObj.alpha2 + }, + "regSource": regSource, + "utmSource": utmSource, + "utmMedium": utmMedium, + "utmCampaign": utmCampaign, + }, + "options": { + "afterActivationURL": encodeURIComponent(afterActivationURL) + } + }; + //console.log("SignUp....", user, data); + request.post({ + url: "https://api." + configuration.DOMAIN + "/v3/users", + json: data + //for more options check: + //https://github.com/mikeal/request#requestoptions-callback + }, function (err, response, body) { + + // console.log(err); + // console.log(response.statusCode); + // console.log(body.result.content); + + if (err) return callback(err); + console.log(body.result.content); + if (response.statusCode !== 200) { + //return callback(new ValidationError("lock.fallback",body.result.content)); + const error_message = body.result.content; + let code = "lock.fallback"; + + if (error_message.search("Handle may not contain a space") !== -1) { + code = "handle_invalid_space"; + } else if (error_message.search("Length of Handle in character should be between 2 and 15") !== -1) { + code = "handle_invalid_length"; + } else if (error_message.search("Please choose another handle, not starting with admin") !== -1) { + code = "handle_invalid_startwith_admin"; + } else if (error_message.search('Handle may contain only letters, numbers and') !== -1) { + code = "handle_invalid_constains_forbidden_char"; + } else if (error_message.search("Handle may not contain only punctuation") !== -1) { + code = "handle_invalid_conatins_only_punctuation"; + } else if (error_message.search("The user already exists") !== -1) { + code = "user_exists"; + } else if (error_message.search("has already been taken") !== -1) { + code = "user_exists"; + } + + + + return callback(new ValidationError(code, error_message)); + + //return callback(new Error(body.result.content)); + } + //if (response.statusCode === 401) return callback(); + /* const Analytics = require('analytics-node'); + const _ = require('lodash'); + var analytics = new Analytics('bkPtWMUMTYDhww2zsJluzxtdhtmSsyd9'); + analytics.identify({ + anonymousId: 'signup', + traits: { + user: _.omit(user, ['credential', 'password']) + } + }); + analytics.track({ + anonymousId: 'BXWXUWnilVUPdN01t2Se29Tw2ZYNGZvH', + event: 'signUp', + properties: _.omit(user, ['credential', 'password']) + });*/ + callback(null); + }); //end post request + //callback(null); +} + +//} diff --git a/web-assets/auth0/dev-tenant/database/login.js b/web-assets/auth0/dev-tenant/database/login.js new file mode 100644 index 0000000..29fe999 --- /dev/null +++ b/web-assets/auth0/dev-tenant/database/login.js @@ -0,0 +1,61 @@ +function login(handleOrEmail, password, callback) { + // This script should authenticate a user against the credentials stored in + // your database. + // It is executed when a user attempts to log in or immediately after signing + // up (as a verification that the user was successfully signed up). + // + // Everything returned by this script will be set as part of the user profile + // and will be visible by any of the tenant admins. Avoid adding attributes + // with values such as passwords, keys, secrets, etc. + // + // The `password` parameter of this function is in plain text. It must be + // hashed/salted to match whatever is stored in your database. For example: + // + // var bcrypt = require('bcrypt@0.8.5'); + // bcrypt.compare(password, dbPasswordHash, function(err, res)) { ... } + // + // There are three ways this script can finish: + // 1. The user's credentials are valid. The returned user profile should be in + // the following format: https://auth0.com/docs/users/normalized/auth0/normalized-user-profile-schema + // var profile = { + // user_id: ..., // user_id is mandatory + // email: ..., + // [...] + // }; + // callback(null, profile); + // 2. The user's credentials are invalid + // callback(new WrongUsernameOrPasswordError(email, "my error message")); + // 3. Something went wrong while trying to reach your database + // callback(new Error("my error message")); + // + // A list of Node.js modules which can be referenced is available here: + // + // https://tehsis.github.io/webtaskio-canirequire/ + request.post({ + url: "https://api."+configuration.DOMAIN+"/v3/users/login", + form: { + handleOrEmail: handleOrEmail, + password: password + } + //for more options check: https://github.com/mikeal/request#requestoptions-callback + }, function (err, response, body) { + console.log("response..............", err,response.statusCode); + if (err) return callback(err); + if (response.statusCode === 401) return callback(); + var user = JSON.parse(body); + user.result.content.roles = user.result.content.roles.map(function(role) { + return role.roleName; + }); + + callback(null, { + user_id: user.result.content.id, + nickname: user.result.content.handle, + email: user.result.content.email, + handle:user.result.content.handle, + roles: user.result.content.roles, + email_verified: user.result.content.emailActive, + created_at: user.result.content.createdAt + }); + }); + } + \ No newline at end of file diff --git a/web-assets/auth0/dev-tenant/rules/custom.js b/web-assets/auth0/dev-tenant/rules/custom.js new file mode 100644 index 0000000..899cf76 --- /dev/null +++ b/web-assets/auth0/dev-tenant/rules/custom.js @@ -0,0 +1,78 @@ + +function (user, context, callback) { + if (context.clientID === configuration.CLIENT_ACCOUNTS_LOGIN) { // + const _ = require('lodash'); + + // TODO: implement your rule + // if (context.protocol === "redirect-callback") { + // User was redirected to the /continue endpoint + if (context.redirect) { + return callback(null, user, context); + // returnning from here no need to check further + } + // otherwise to nothing + + console.log("Enter Rule: Custom-Claims"); + let handle = _.get(user, "handle", null); + const provider = _.get(user, "identities[0].provider", null); + if (!handle && provider === "auth0") { + handle = _.get(user, "nickname", null); + } + console.log("Fetch roles for email/handle: ", user.email, handle, provider); + + global.AUTH0_CLAIM_NAMESPACE = "https://" + configuration.DOMAIN + "/"; + try { + request.post({ + url: 'https://api.' + configuration.DOMAIN + '/v3/users/roles', + form: { + email: user.email, + handle: handle + } + }, function (err, response, body) { + console.log("called topcoder api for role: response status - ", response.statusCode); + if (err) return callback(err, user, context); + if (response.statusCode !== 200) { + return callback('Login Error: Whoops! Something went wrong. Looks like your registered email has discrepancy with Authentication. Please connect to our support support@topcoder.com. Back to application ', user, context); + } + + let res = JSON.parse(body); + // TODO need to double sure about multiple result or no result + let userId = res.result.content.id; + let handle = res.result.content.handle; + let roles = res.result.content.roles.map(function (role) { + return role.roleName; + }); + let userStatus = res.result.content.active; // true/false + + // TEMP + let tcsso = res.result.content.regSource || ''; + + context.idToken[global.AUTH0_CLAIM_NAMESPACE + 'roles'] = roles; + context.idToken[global.AUTH0_CLAIM_NAMESPACE + 'userId'] = userId; + context.idToken[global.AUTH0_CLAIM_NAMESPACE + 'handle'] = handle; + context.idToken[global.AUTH0_CLAIM_NAMESPACE + 'user_id'] = user.identities[0].provider + "|" + userId; + context.idToken[global.AUTH0_CLAIM_NAMESPACE + 'tcsso'] = tcsso; + context.idToken[global.AUTH0_CLAIM_NAMESPACE + 'active'] = userStatus; + context.idToken.nickname = handle; + //console.log(user, context); + if (!userStatus) { + context.redirect = { + url: `https://accounts-auth0.${configuration.DOMAIN}/check_email.html` + }; + return callback(null, user, context); + } + if (!userStatus && context.login_counts > 1) { + return callback('Login Alert: Please verify your email first! Please connect to our support support@topcoder.com. Back to application ', user, context); + } + return callback(null, user, context); + } + ); + } catch (e) { + console.log("Error in calling user roles" + e); + return callback("Something went worng!. Please retry.", user, context); + } + } else { + // for other apps do nothing + return callback(null, user, context); + } +} \ No newline at end of file diff --git a/web-assets/auth0/dev-tenant/rules/enterprise.js b/web-assets/auth0/dev-tenant/rules/enterprise.js new file mode 100644 index 0000000..cc31fe1 --- /dev/null +++ b/web-assets/auth0/dev-tenant/rules/enterprise.js @@ -0,0 +1,163 @@ + +function (user, context, callback) { + if (context.clientID === configuration.CLIENT_ACCOUNTS_LOGIN) { // client/application specific + + const _ = require('lodash'); + console.log("Enter Rule: Enterprise-User-Registration"); + + const baseApiUrl = "https://api." + configuration.DOMAIN + "/v3"; + //console.log("register user rule executed- user", user); + //console.log("register user rule executed - context", context); + + const isEnterprise = (_.get(user, "identities[0].provider") !== 'auth0') && + !(_.get(user, "identities[0].isSocial")) ? true : false; + + console.log("Is enterprise login: ", isEnterprise); + if (isEnterprise) { + let provider = _.get(user, "identities[0].connection"); + const providerType = _.get(user, "identities[0].provider"); + let userId = _.get(user, "identities[0].user_id"); + userId = userId.substring(userId.lastIndexOf('|') + 1); + + let handle = _.get(user, "nickname", ""); + const lastName = _.get(user, "family_name"); + const firstName = _.get(user, "given_name"); + const email = _.get(user, "email"); + //const emailVerified = _.get(user, "email_verified", true); + const name = _.get(user, "name"); + + let isoAlpha2Code = _.get(context, "request.geoip.country_code"); + let isoAlpha3Code = _.get(context, "request.geoip.country_code3"); + let countryCode = _.get(context, "request.geoip.country_name"); + let regSource = _.get(context, "request.query.regSource", null); + let retUrl = _.get(context, "request.query.returnUrl", null); + let utmSource = _.get(context, "request.query.utmSource", null); + let utmMedium = _.get(context, "request.query.utmMedium", null); + let utmCampaign = _.get(context, "request.query.utmCampaign", null); + + const resourcePath = '/identityproviders?filter=handle=' + email; + const afterActivationURL = configuration.DEFAULT_AFTER_ACTIVATION_URL; + const hostName = _.get(context, "request.hostname", null); + const registrationCompletetUrl = "https://" + hostName + "/continue"; + //const userHandleRedirectUrl = configuration.CUSTOM_PAGES_BASE_URL + '/signup.html?source='+ utmSource + '&formAction=' + registrationCompletetUrl; + const userHandleRedirectUrl = configuration.CUSTOM_PAGES_BASE_URL + + "/signup.html?regSource=" + regSource + + "&firstName=" + encodeURIComponent(firstName) + + "&lastName=" + encodeURIComponent(lastName) + + "&utmSource=" + encodeURIComponent(utmSource) + + "&utmMedium=" + encodeURIComponent(utmMedium) + + "&utmCampaign=" + encodeURIComponent(utmCampaign) + + "&formAction=" + registrationCompletetUrl + + "&returnUrl=" + retUrl; + + console.log("provider", provider, email); + try { + request.get({ + url: baseApiUrl + resourcePath + }, function (err, response, body) { + console.log("Enterprise user check - responseBody", body); + + if (err) { + console.log("Enterprise validation error:", err); + } + + /** + * check if enterprise profile is valid for our TC database + */ + + /* + Aug 2021 adding new wipro-sso connection with name wipro_azuread + */ + + if (_.includes([configuration.WIPRO_SSO_AZURE_AD_CONNECTION_NAME], provider) + ) { + provider = configuration.WIPRO_SSO_ADFS_CONNECTION_NAME; + } + + let isSSOUserExist = (_.get(JSON.parse(body), "result.content.name") === provider) ? + true : false; + + console.log("Enterprise customer alreday available:", isSSOUserExist); + if (!isSSOUserExist) { + console.log("register enterprise user."); + if (context.protocol === "redirect-callback") { + // User was redirected to the /continue endpoint + console.log("print data", typeof context); + console.log("get user extra data from query param"); + handle = _.get(context, "request.query.handle", handle); + console.log("...Handle....", handle); + + const countryStr = _.get(context, "request.query.country", null); + const countryObj = JSON.parse(countryStr); + if (countryObj) { + countryCode = _.get(countryObj, "code", countryCode); + isoAlpha2Code = _.get(countryObj, "alpha2", isoAlpha2Code); + isoAlpha3Code = _.get(countryObj, "alpha3", isoAlpha3Code); + } + utmSource = _.get(context, "request.query.source", utmSource); + utmMedium = _.get(context, "request.query.utmMedium", utmMedium); + utmCampaign = _.get(context, "request.query.utmCampaign", utmCampaign); + } else { + console.log('Redirect to choose user handle page.'); + context.redirect = { + url: userHandleRedirectUrl + }; + return callback(null, user, context); + } + // Enterprise profile will be active default + let data = { + "param": { + "handle": handle, + "firstName": firstName, + "lastName": lastName, + "email": email, + "country": { + "code": countryCode, + "isoAlpha3Code": isoAlpha3Code, + "isoAlpha2Code": isoAlpha2Code + }, + "utmSource": utmSource, + "utmMedium": utmMedium, + "utmCampaign": utmCampaign, + "active": true, + "profile": { + "name": name, + "email": email, + "providerType": providerType, + "provider": provider, + "userId": userId + } + }, + "options": { + "afterActivationURL": encodeURIComponent( configuration.DEFAULT_AFTER_ACTIVATION_URL) + } + }; + console.log("Going to add enterprise", JSON.stringify(data)); + request.post({ + url: "https://api." + configuration.DOMAIN + "/v3/users", + json: data + }, function (error, response, body) { + if (response.statusCode !== 200) { + console.log("Enterprise registration error", error); + } + // on success + return callback(null, user, context); + //if (response.statusCode === 401) return callback(); + }); + } else { // valid social user if block end + return callback(null, user, context); + } + } + ); // end validatesocial request + } catch (e) { + console.log(`Error in calling validate enterprise user ${e}`); + return callback(null, user, context); + } + } else {// end isSocial if-block + console.log("existing from Enterprise-User-Registration rule."); + return callback(null, user, context); + } + } else { // END client-id check + return callback(null, user, context); + } +} diff --git a/web-assets/auth0/dev-tenant/rules/onboardingChecklist.js b/web-assets/auth0/dev-tenant/rules/onboardingChecklist.js new file mode 100644 index 0000000..a443ceb --- /dev/null +++ b/web-assets/auth0/dev-tenant/rules/onboardingChecklist.js @@ -0,0 +1,145 @@ +function (user, context, callback) { + if (context.clientID === configuration.CLIENT_ACCOUNTS_LOGIN) { + console.log("rule:onboarding-checklist:enter"); + console.log("rule:onboarding-checklist:context.request", context.request); + + if (context.redirect) { + console.log("rule:onboarding-checklist:exiting due to context being a redirect"); + return callback(null, user, context); + } + + const _ = require('lodash'); + const moment = require('moment'); + + let handle = _.get(user, "handle", null); + const provider = _.get(user, "identities[0].provider", null); + if (!handle && provider === "auth0") { + handle = _.get(user, "nickname", null); + } + + console.log("rule:onboarding-checklist: fetch onboarding_checklist for email/handle: ", user.email, handle, provider); + + const createdAt = _.get(user, "created_at", null); + const thresholdDate = moment(configuration.PROFILE_CREATION_DATE_THRESHOLD, "YYYY-MM-DD"); + + try { + // For users created before thresholdDate, we don't want to check onboarding_checklist + // This is because older profiles might not have onboarding_checklist data and they don't need to see the onboarding_wizard + if (createdAt && !thresholdDate.isBefore(moment(createdAt))) { + console.log("rule:onboarding-checklist: user created before threshold date. Not checking onboarding_checklist."); + return callback(null, user, context); + } + } catch (err) { + console.log("rule:onboarding-checklist: failed to compare userCreationDate", createdAt, " with threshold. Error", err); + } + + /** + * Returns M2M token needed to fetch onboarding_checklist + */ + const getToken = function(callback) { + if (global.M2MToken) { + console.log('rule:onboarding-checklist:M2M token is available'); + const jwt = require('jsonwebtoken'); + const decoded = jwt.decode(global.M2MToken); + const exp = moment.unix(decoded.exp); + + if (exp > new Date()) { + console.log('rule:onboarding-checklist:M2M token is valid. Reusing...'); + return callback(null, global.M2MToken); + } + } + + console.log('rule:onboarding-checklist:Fetching new M2M token'); + request.post({ + url: `https://auth0proxy.${configuration.DOMAIN}/token`, + headers: 'content-type: application/json', + json: { + client_id: configuration.M2M_CLIENT_ID, + client_secret: configuration.M2M_CLIENT_SECRET, + audience: configuration.M2M_AUDIENCE, + auth0_url: configuration.M2M_TOKEN_URL, + grant_type: 'client_credentials' + } + }, function (err, response, body) { + if (err) { + return callback(err); + } + + global.M2MToken = body.access_token; + console.log('rule:onboarding-checklist:setting the M2MToken globally', global.M2MToken); + return callback(null, global.M2MToken); + }); + }; + + getToken(function(err, token) { + if (err) { + console.log('rule:onboarding-checklist:failed to fetch M2M token.'); + return callback(null, user, context); + } + global.AUTH0_CLAIM_NAMESPACE = "https://" + configuration.DOMAIN + "/"; + const axios = require('axios@0.19.2'); + + const options = { + method: 'GET', + url: `https://api.${configuration.DOMAIN}/v5/members/${handle}/traits?traitIds=onboarding_checklist`, + headers: { + Authorization: `Bearer ${token}` + } + }; + + // Fetch onboarding_checklist using v5 member Api. + axios(options) + .then(result => { + try { + const data = result.data; + + if (data.length === 0) { + // User doesn't have any traits with traitId onboarding_checklist and should be shown the onboarding wizard + context.idToken[global.AUTH0_CLAIM_NAMESPACE + 'show_onboarding_wizard'] = true; + console.log('rule:onboarding-checklist:Setting show_onboarding_wizard to true', user); + return callback(null, user, context); + } + + const onboardingChecklistTrait = data.filter((item) => item.traitId === 'onboarding_checklist')[0].traits; + + for (let checklistTrait of onboardingChecklistTrait.data) { + if ( + checklistTrait.onboarding_wizard != null && + (checklistTrait.onboarding_wizard.status != null || // any valid status indicates user has already seen onboarding wizard and needn't be shown again. + checklistTrait.onboarding_wizard.skip) // for certain signup routes skip is set to true, and thus onboarding wizard needn't be shown + ) { + return callback(null, user, context); + } + } + + const profileCompletedData = onboardingChecklistTrait.data[0].profile_completed; + + if (profileCompletedData) { + if (profileCompletedData.status === "completed") { + return callback(null, user, context); + } + + for (const item in profileCompletedData.metadata) { + if (profileCompletedData.metadata[item]) { + return callback(null, user, context); + } + } + } + + // All checks failed - indicating user newly registered and needs to be shown the onboarding wizard + console.log('rule:onboarding-checklist: set show_onboarding_wizard', user); + context.idToken[global.AUTH0_CLAIM_NAMESPACE + 'show_onboarding_wizard'] = true; + return callback(null, user, context); + } catch (e) { + console.log("rule:onboarding-checklist:Error in fetching onboarding_checklist", e); + return callback(null, user, context); + } + }).catch(requestError => { + console.log("rule:onboarding-checklist:Failed fetching onboarding_checklist with error", requestError.response.status); + return callback(null, user, context); + }); + }); + } else { + return callback(null, user, context); + } +} diff --git a/web-assets/auth0/dev-tenant/rules/resendEmail.js b/web-assets/auth0/dev-tenant/rules/resendEmail.js new file mode 100644 index 0000000..02cc09a --- /dev/null +++ b/web-assets/auth0/dev-tenant/rules/resendEmail.js @@ -0,0 +1,49 @@ + +function (user, context, callback) { + if (context.clientID === configuration.CLIENT_ACCOUNTS_LOGIN) { // client/application specific + // TODO: implement your rule + const _ = require('lodash'); + + const resend = _.get(context, "request.query.resend", null); + + if (context.protocol === 'redirect-callback' && resend) { + console.log("----------:Entered Email Resend Rule:------------"); + let handle = _.get(user, "handle", null); + const provider = _.get(user, "identities[0].provider", null); + if (!handle && provider === "auth0") { + handle = _.get(user, "nickname", null); + } + + global.AUTH0_CLAIM_NAMESPACE = "https://" + configuration.DOMAIN + "/"; + // trigger resend email event at identity servcie + try { + request.post({ + url: 'https://api.' + configuration.DOMAIN + '/v3/users/resendEmail', + form: { + email: user.email, + handle: handle + } + }, function (err, response, body) { + console.log("called topcoder api for resend email: response status - ", response.statusCode); + if (err) return callback(err, user, context); + if (response.statusCode !== 200) { + //{"id":"2fb48e50:17a334870b1:-457c","result":{"success":true,"status":400,"metadata":null,"content":"User has been activated"},"version":"v3"} + + const error_message = _.get(JSON.parse(body), 'result.content', 'unknown error'); + return callback(`Resend email error: ${error_message}`, user, context); + } + return callback(null, user, context); + } + ); + } catch (e) { + return callback("Something went worng!. Please retry.", user, context); + } + // returnning from here no need to check further + } else { // if it is not redirect, do nothing + return callback(null, user, context); + } + } else { + // for other apps do nothing + return callback(null, user, context); + } +} \ No newline at end of file diff --git a/web-assets/auth0/dev-tenant/rules/social.js b/web-assets/auth0/dev-tenant/rules/social.js new file mode 100644 index 0000000..afdfb83 --- /dev/null +++ b/web-assets/auth0/dev-tenant/rules/social.js @@ -0,0 +1,183 @@ + +function (user, context, callback) { + if (context.clientID === configuration.CLIENT_ACCOUNTS_LOGIN) { // client/application specific + global.AUTH0_CLAIM_NAMESPACE = "https://" + configuration.DOMAIN + "/"; + const _ = require('lodash'); + console.log("Enter Rule: Social-User-Registration"); + const isSocial = _.get(user, "identities[0].isSocial"); + const connection = _.get(user, "identities[0].connection"); + console.log("is social login", isSocial); + if (isSocial) { + let provider = _.get(user, "identities[0].provider"); + let userId = _.get(user, "identities[0].user_id"); + // changes for github custom setup + if (connection.includes('github')) { + provider = 'github'; + if (typeof userId === 'string' || userId instanceof String) { + let resArray = userId.split('|'); + userId = resArray[1]; + } + } + + const accessToken = _.get(user, "identities[0].access_token"); + const email = _.get(user, "email"); + const emailVerified = _.get(user, "email_verified"); + const name = _.get(user, "name"); + const auth0UserId = _.get(user, "user_id"); + const hostName = _.get(context, "request.hostname", null); + const registrationCompletetUrl = "https://" + hostName + "/continue"; + + let handle = _.get(user, "nickname", null); + let lastName = _.get(user, "family_name"); + let firstName = _.get(user, "given_name"); + let isoAlpha2Code = _.get(context, "request.geoip.country_code"); + let isoAlpha3Code = _.get(context, "request.geoip.country_code3"); + let countryCode = _.get(context, "request.geoip.country_name"); + let regSource = _.get(context, "request.query.regSource", null); + let utmSource = _.get(context, "request.query.utmSource", null); + let utmMedium = _.get(context, "request.query.utmMedium", null); + let utmCampaign = _.get(context, "request.query.utmCampaign", null); + let retUrl = _.get(context, "request.query.returnUrl", null); + + //console.log("resource", regSource, _.get(context, "request.query")); + + const userHandleRedirectUrl = configuration.CUSTOM_PAGES_BASE_URL + + "/signup.html?regSource=" + regSource + + "&firstName=" + encodeURIComponent(firstName) + + "&lastName=" + encodeURIComponent(lastName) + + "&utmSource=" + encodeURIComponent(utmSource) + + "&utmMedium=" + encodeURIComponent(utmMedium) + + "&utmCampaign=" + encodeURIComponent(utmCampaign) + + "&formAction=" + registrationCompletetUrl + + "&returnUrl=" + retUrl; + + const baseApiUrl = "https://api." + configuration.DOMAIN + "/v3/users"; + const resourcePath = '/validateSocial?socialUserId=' + userId + '&socialProvider=' + provider; + let afterActivationURL = configuration.DEFAULT_AFTER_ACTIVATION_URL; + + console.log("provider", provider); + try { + request.get({ + url: baseApiUrl + resourcePath + }, function (err, response, body) { + //console.log("social user check - responseBody", body); + + if (err) { + console.log("Social validation error:", err); + } + + /** + * check if social profile is valid for our TC database + */ + let flag = _.get(JSON.parse(body), "result.content.valid"); + console.log("Is valid social profile: ", flag, response.statusCode); + if (response.statusCode !== 200) { + console.log(`Error in calling validate social user: ${body}`); + return callback(null, user, context); + } + if (flag) { + console.log("register social login"); + if (context.protocol === "redirect-callback") { + // User was redirected to the /continue endpoint + console.log("get user extra data from query param"); + handle = _.get(context, "request.query.handle", handle); + const countryStr = _.get(context, "request.query.country", null); + const countryObj = JSON.parse(countryStr); + if (countryObj) { + countryCode = _.get(countryObj, "code", countryCode); + isoAlpha2Code = _.get(countryObj, "alpha2", isoAlpha2Code); + isoAlpha3Code = _.get(countryObj, "alpha3", isoAlpha3Code); + } + regSource = _.get(context, "request.query.source", regSource); + firstName = _.get(context, "request.query.firstName", firstName); + lastName = _.get(context, "request.query.lastName", lastName); + utmSource = _.get(context, "request.query.source", utmSource); + utmMedium = _.get(context, "request.query.utmMedium", utmMedium); + utmCampaign = _.get(context, "request.query.utmCampaign", utmCampaign); + retUrl = _.get(context, "request.query.returnUrl", retUrl); + console.log("------HHHHHH-----", context.request); + afterActivationURL = retUrl ? retUrl : afterActivationURL; + if (regSource === configuration.REG_BUSINESS) { + afterActivationURL = "https://connect." + configuration.DOMAIN; + } + // workaround: to avoid activation email failure + if (!firstName) { + firstName = _.get(user, "nickname", firstName); + } + if (!lastName) { + lastName = _.get(user, "nickname", lastName); + } + // end workaround + + const data = { + "param": { + "handle": handle, + "firstName": firstName, + "lastName": lastName, + "email": email, + "country": { + "code": countryCode, + "isoAlpha3Code": isoAlpha3Code, + "isoAlpha2Code": isoAlpha2Code + }, + "utmSource": utmSource, + "utmMedium": utmMedium, + "utmCampaign": utmCampaign, + "regSource": regSource, + "profile": { + "userId": userId, + "name": name, + "providerType": provider, + "email": email, + "emailVerified": emailVerified, + "context": { + "handle": handle, + "accessToken": accessToken, + "auth0UserId": auth0UserId + } + } + }, + "options": { + "afterActivationURL": encodeURIComponent( configuration.DEFAULT_AFTER_ACTIVATION_URL) + } + }; + request.post({ + url: "https://api." + configuration.DOMAIN + "/v3/users", + json: data + }, function (error, response, body) { + if (response.statusCode !== 200) { + console.log("------- Social registration error occurred -------Http error status:", response.statusCode); + console.log("Error message:", body.result.content); + return callback( "Social Registration Error: " + body.result.content + " Please retry.", user, context); + } + // on success + return callback(null, user, context); + }); + } else { + console.log('Redirect to choose user handle page.', provider); + if (provider === "giveprovider") { + return callback("Github signup is temporarily disabled. You can signup directly using a username / password or another social signup.", user, context); + } else { + context.redirect = { + url: userHandleRedirectUrl + }; + return callback(null, user, context); + } + } + } else { // valid social user if block end + return callback(null, user, context); + } + } + ); // end validatesocial request + } catch (e) { + console.log(`Error in calling validate social user ${e}`); + return callback('Error in validating social user. Please rety.', user, context); + } + } else {// end isSocial if-block + console.log("existing from Social-User-Registration rule."); + return callback(null, user, context); + } + } else { + return callback(null, user, context); + } +} diff --git a/web-assets/auth0/dev-tenant/rules/user-privacy-policy.js b/web-assets/auth0/dev-tenant/rules/user-privacy-policy.js new file mode 100644 index 0000000..f4130c4 --- /dev/null +++ b/web-assets/auth0/dev-tenant/rules/user-privacy-policy.js @@ -0,0 +1,107 @@ +function (user, context, callback) { + if (context.clientID === configuration.CLIENT_ACCOUNTS_LOGIN) { // client/application specific + console.log("rule:user-privacy-policy:enter"); + + const _ = require('lodash'); + + const loginCount = _.get(context, "stats.loginsCount"); + const isAuth0 = (_.get(user, "identities[0].provider") === 'auth0') ? true : false; + + /** + * Note : Sachin Maheshwari + * This rule should be execute after Custom-Claims Rule, + * to get the userId for social and enterprise login in 'IdToken' + */ + var userId = _.get(context, `idToken['https://${configuration.DOMAIN}/userId']`, null); + + if (isAuth0) { + // no need to check futher as this rule is other than Auth0 i.e. social or enterprise + //return callback(null, user, context); + userId = _.get(user, "identities[0].user_id", null); + } + + console.log('rule:user-privacy-policy:logincount', loginCount); + console.log('rule:user-privacy-policy:userId', userId); + + if (!userId) { + // no need to check futher + console.log('rule:user-privacy-policy:error:user_id null'); + return callback(null, user, context); + } + + if (loginCount < 3) { + const getToken = function (tokenCB) { + if (global.M2MToken) { + console.log('rule:user-privacy-policy:a M2M token is present'); + const jwt = require('jsonwebtoken'); + const moment = require('moment'); + const decoded = jwt.decode(global.M2MToken); + const exp = moment.unix(decoded.exp); + + if (exp > new Date()) { + console.log('M2MToken is still valid. reusing token'); + tokenCB(null, global.M2MToken); + return; + } + } + console.log('rule:user-privacy-policy:fetching fresh m2m token'); + request.post({ + url: `https://auth0proxy.${configuration.DOMAIN}/token`, + headers: 'content-type: application/json', + json: { + client_id: configuration.M2M_CLIENT_ID, + client_secret: configuration.M2M_CLIENT_SECRET, + audience: configuration.M2M_AUDIENCE, + auth0_url: configuration.M2M_TOKEN_URL, + grant_type: 'client_credentials' + } + }, function (err, response, body) { + if (err) { + tokenCB(err); + return; + } + + global.M2MToken = body.access_token; + console.log('rule:user-privacy-policy:setting the M2MToken globally'); + tokenCB(null, global.M2MToken); + }); + }; + + const callTermApi = function (token) { + console.log("rule:user-privacy-policy:calling Term API"); + + var payload = { userId }; + + request.post({ + url: `https://api.${configuration.DOMAIN}/v5/terms/${configuration.TERMS_USER_PRIVACY_POLICY_UUID}/users`, + headers: { + 'Authorization': 'Bearer ' + token, + 'Content-Type': 'application/json' + }, + json: payload + }, + function (err, response, body) { + // swallow error + if (err) { + //console.log(err); + } else { + console.log('rule:user-privacy-policy:called term API!'); + console.log('rule:user-privacy-policy:response - ', body); + } + callback(null, user, context); + }); + }; + + getToken(function (err, token) { + if (err) { + console.log(err); + } else { + console.log('rule:user-privacy-policy:m2m token', token); + callTermApi(token); + } + }); + } // if login count + + } // if end + return callback(null, user, context); +} diff --git a/web-assets/js/setupAuth0WithRedirect.js b/web-assets/js/setupAuth0WithRedirect.js index 8b3aaf7..5e0d233 100644 --- a/web-assets/js/setupAuth0WithRedirect.js +++ b/web-assets/js/setupAuth0WithRedirect.js @@ -24,6 +24,8 @@ const qs = (function (a) { const authSetup = function () { let domain = 'auth.{{DOMAIN}}'; + + const onboardingWizardUrl = 'https://platform.{{DOMAIN}}/onboard'; const clientId = '{{AUTH0_CLIENT_ID}}'; const useLocalStorage = false; const useRefreshTokens = false; @@ -229,6 +231,11 @@ const authSetup = function () { return token ? !isTokenExpired(token) : false; }; + const redirectToOnboardingWizard = function () { + logger("redirect to onboarding wizard"); + window.location = onboardingWizardUrl; + } + const redirectToApp = function () { logger("redirect to app", appUrl); if (appUrl) { @@ -258,6 +265,21 @@ const authSetup = function () { const storeToken = function () { auth0.getIdTokenClaims().then(function (claims) { idToken = claims.__raw; + + logger('Claims', JSON.stringify(claims)); + + let showOnboardingWizard = false; + Object.keys(claims).forEach(key => { + logger('Checking key', key); + if (key.indexOf('show_onboarding_wizard') !== -1) { + if (claims[key]) { + showOnboardingWizard = true; + } + } + }); + + logger('Show Onboarding Wizard', showOnboardingWizard); + let userActive = false; Object.keys(claims).findIndex(function (key) { if (key.includes('active')) { @@ -265,7 +287,7 @@ const authSetup = function () { return true; } return false; - }); + }); if (userActive) { let tcsso = ''; Object.keys(claims).findIndex(function (key) { @@ -291,11 +313,16 @@ const authSetup = function () { logger('Error occured in fecthing token expiry time', e.message); } - // session still active, but app calling login - if (!appUrl && returnAppUrl) { - appUrl = returnAppUrl + if (showOnboardingWizard) { + logger('Take user to onboarding wizard'); + redirectToOnboardingWizard(); + } else { + // session still active, but app calling login + if (!appUrl && returnAppUrl) { + appUrl = returnAppUrl + } + redirectToApp(); } - redirectToApp(); } else { logger("User active ? ", userActive); host = registerSuccessUrl;