diff --git a/README.md b/README.md index dbbd5142d..f30298d35 100644 --- a/README.md +++ b/README.md @@ -346,6 +346,7 @@ var options = { - **prefill {Object}**: Allows you to set the initial value for the _email_ and/or _username_ inputs, e.g. `{prefill: {email: "someone@auth0.com", username: "someone"}}`. When omitted no initial value will be provided. - **signUpLink {String}**: URL for a page that allows the user to sign up. When set to a non-empty string, the user will be linked to the provided URL when clicking the _sign up_ tab in the _login screen_. - **usernameStyle {String}**: Determines what will be used to identify the user for a Database connection that has the `requires_username` flag set, otherwise it will be ignored. Possible values are `"username"` and `"email"` and by default both `username` and `email` are allowed. +- **signUpHideUsernameField {Boolean}**: When set to `true` hides the _username_ input during sign up for a Database connection that has the `requires_username` flag set. Defaults to `false`. #### Enterprise options diff --git a/src/__tests__/engine/classic/__snapshots__/sign_up_pane.test.jsx.snap b/src/__tests__/engine/classic/__snapshots__/sign_up_pane.test.jsx.snap index d584e570b..48acda30b 100644 --- a/src/__tests__/engine/classic/__snapshots__/sign_up_pane.test.jsx.snap +++ b/src/__tests__/engine/classic/__snapshots__/sign_up_pane.test.jsx.snap @@ -1,5 +1,22 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`SignUpPane onlyEmail is false hide UsernamePane when databaseConnectionRequiresUsername is true and signUpHideUsernameField is true 1`] = ` +
+
+
+`; + exports[`SignUpPane onlyEmail is false shows PasswordPane 1`] = `
`; -exports[`SignUpPane onlyEmail is false shows UsernamePane when databaseConnectionRequiresUsername is true 1`] = ` +exports[`SignUpPane onlyEmail is false shows UsernamePane when databaseConnectionRequiresUsername is true and signUpHideUsernameField is false 1`] = `
{ expectComponent().toMatchSnapshot(); }); - it('shows UsernamePane when databaseConnectionRequiresUsername is true', () => { + it('shows UsernamePane when databaseConnectionRequiresUsername is true and signUpHideUsernameField is false', () => { require('connection/database/index').databaseConnectionRequiresUsername = () => true; + require('connection/database/index').signUpHideUsernameField = () => false; const Component = getComponent(); expectComponent().toMatchSnapshot(); }); + it('hide UsernamePane when databaseConnectionRequiresUsername is true and signUpHideUsernameField is true', () => { + require('connection/database/index').databaseConnectionRequiresUsername = () => true; + require('connection/database/index').signUpHideUsernameField = () => true; + const Component = getComponent(); + + expectComponent().toMatchSnapshot(); + }); }); }); diff --git a/src/connection/database/actions.js b/src/connection/database/actions.js index 4e4365b30..88619784d 100644 --- a/src/connection/database/actions.js +++ b/src/connection/database/actions.js @@ -5,6 +5,7 @@ import { closeLock, logIn as coreLogIn, logInSuccess, validateAndSubmit } from ' import * as l from '../../core/index'; import * as c from '../../field/index'; import { + databaseConnection, databaseConnectionName, databaseConnectionRequiresUsername, databaseLogInWithEmail, @@ -12,7 +13,8 @@ import { setScreen, shouldAutoLogin, toggleTermsAcceptance as internalToggleTermsAcceptance, - additionalSignUpFields + additionalSignUpFields, + signUpHideUsernameField } from './index'; import * as i18n from '../../i18n'; @@ -53,10 +55,24 @@ export function logIn(id, needsMFA = false) { }); } +function generateRandomUsername(length) { + let result = ''; + const characters = 'abcdefghijklmnopqrstuvwxyz0123456789'; + const charactersLength = characters.length; + for (let i = 0; i < length; i++) { + result += characters.charAt(Math.floor(Math.random() * charactersLength)); + } + return result; +} + export function signUp(id) { const m = read(getEntity, 'lock', id); const fields = ['email', 'password']; - if (databaseConnectionRequiresUsername(m)) fields.push('username'); + + // Skip the username validation if signUpHideUsernameField option is enabled. + // We will generate a random username to avoid name collusion before we make the signup API call. + if (databaseConnectionRequiresUsername(m) && !signUpHideUsernameField(m)) fields.push('username'); + additionalSignUpFields(m).forEach(x => fields.push(x.get('name'))); validateAndSubmit(id, fields, m => { @@ -73,7 +89,13 @@ export function signUp(id) { } if (databaseConnectionRequiresUsername(m)) { - params.username = c.getFieldValue(m, 'username'); + if (signUpHideUsernameField(m)) { + const usernameValidation = databaseConnection(m).getIn(['validation', 'username']); + const range = usernameValidation ? usernameValidation.toJS() : { max: 15 }; + params.username = generateRandomUsername(range.max); + } else { + params.username = c.getFieldValue(m, 'username'); + } } if (!additionalSignUpFields(m).isEmpty()) { diff --git a/src/connection/database/index.js b/src/connection/database/index.js index f9dc9686b..1e794ae75 100644 --- a/src/connection/database/index.js +++ b/src/connection/database/index.js @@ -63,7 +63,8 @@ function processDatabaseOptions(opts) { showTerms, signUpLink, usernameStyle, - signUpFieldsStrictValidation + signUpFieldsStrictValidation, + signUpHideUsernameField } = opts; let { initialScreen, screens } = processScreenOptions(opts); @@ -96,6 +97,10 @@ function processDatabaseOptions(opts) { signUpFieldsStrictValidation = false; } + if (!assertMaybeBoolean(opts, 'signUpHideUsernameField')) { + signUpHideUsernameField = false; + } + if (!assertMaybeArray(opts, 'additionalSignUpFields')) { additionalSignUpFields = undefined; } else if (additionalSignUpFields) { @@ -259,7 +264,8 @@ function processDatabaseOptions(opts) { screens, signUpLink, usernameStyle, - signUpFieldsStrictValidation + signUpFieldsStrictValidation, + signUpHideUsernameField }) .filter(x => typeof x !== 'undefined') .toJS(); @@ -456,6 +462,10 @@ export function signUpFieldsStrictValidation(m) { return get(m, 'signUpFieldsStrictValidation', false); } +export function signUpHideUsernameField(m) { + return get(m, 'signUpHideUsernameField', false); +} + export function mustAcceptTerms(m) { return get(m, 'mustAcceptTerms', false); } diff --git a/src/engine/classic/sign_up_pane.jsx b/src/engine/classic/sign_up_pane.jsx index ec049545a..62f6411b9 100644 --- a/src/engine/classic/sign_up_pane.jsx +++ b/src/engine/classic/sign_up_pane.jsx @@ -7,7 +7,8 @@ import { additionalSignUpFields, databaseConnectionRequiresUsername, passwordStrengthPolicy, - signUpFieldsStrictValidation + signUpFieldsStrictValidation, + signUpHideUsernameField } from '../../connection/database/index'; import CaptchaPane from '../../field/captcha/captcha_pane'; import * as l from '../../core/index'; @@ -30,7 +31,7 @@ export default class SignUpPane extends React.Component { const header = headerText &&

{headerText}

; const usernamePane = - !onlyEmail && databaseConnectionRequiresUsername(model) ? ( + !onlyEmail && databaseConnectionRequiresUsername(model) && !signUpHideUsernameField(model) ? (