Fixed Portal honeypot field (#20825)

ref INC-97
ref https://github.com/TryGhost/Ghost/issues/20767
- finishes wiring up the honeypot fied
- updates state handing to properly set the value
- maintains honeypot field across page changes within portal

There isn't a single previous commit to point to here since they didn't
get squashed. We added a honeypot field to help mitigate bot signup
activity. It's hidden, and if filled out, we can anticipate it's a bot.
Right now this just logs to Ghost while we collect data.
This commit is contained in:
Steve Larson 2024-08-26 17:51:57 -05:00 committed by GitHub
parent cfc4c08200
commit 22fcd21fbb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 17 additions and 13 deletions

View File

@ -109,8 +109,8 @@ export default function NewsletterSelectionPage({pageData, onBack}) {
id: d.id id: d.id
}; };
}); });
const {name, email, plan, offerId} = pageData; const {name, email, plan, phonenumber, offerId} = pageData;
onAction('signup', {name, email, plan, newsletters, offerId}); onAction('signup', {name, email, plan, phonenumber, newsletters, offerId});
}} }}
brandColor={brandColor} brandColor={brandColor}
label={label} label={label}

View File

@ -282,12 +282,15 @@ export default class OfferPage extends React.Component {
}; };
}, () => { }, () => {
const {onAction} = this.context; const {onAction} = this.context;
const {name, email, errors} = this.state; const {name, email, phonenumber, errors} = this.state;
const hasFormErrors = (errors && Object.values(errors).filter(d => !!d).length > 0); const hasFormErrors = (errors && Object.values(errors).filter(d => !!d).length > 0);
if (!hasFormErrors) { if (!hasFormErrors) {
const signupData = { const signupData = {
name, email, plan: price?.id, name,
offerId: offer?.id email,
plan: price?.id,
offerId: offer?.id,
phonenumber
}; };
if (hasMultipleNewsletters({site})) { if (hasMultipleNewsletters({site})) {
this.setState({ this.setState({

View File

@ -34,11 +34,11 @@ export default class SigninPage extends React.Component {
errors: ValidateInputForm({fields: this.getInputFields({state})}) errors: ValidateInputForm({fields: this.getInputFields({state})})
}; };
}, async () => { }, async () => {
const {email, honeypot, errors} = this.state; const {email, phonenumber, errors} = this.state;
const {redirect} = this.context.pageData ?? {}; const {redirect} = this.context.pageData ?? {};
const hasFormErrors = (errors && Object.values(errors).filter(d => !!d).length > 0); const hasFormErrors = (errors && Object.values(errors).filter(d => !!d).length > 0);
if (!hasFormErrors) { if (!hasFormErrors) {
this.context.onAction('signin', {email, honeypot, redirect}); this.context.onAction('signin', {email, phonenumber, redirect});
} }
}); });
} }
@ -74,7 +74,7 @@ export default class SigninPage extends React.Component {
}, },
{ {
type: 'text', type: 'text',
value: state.honeypot, value: state.phonenumber,
placeholder: '+1 (123) 456-7890', placeholder: '+1 (123) 456-7890',
// Doesn't need translation, hidden field // Doesn't need translation, hidden field
label: 'Phone number', label: 'Phone number',

View File

@ -397,20 +397,20 @@ class SignupPage extends React.Component {
}; };
}, () => { }, () => {
const {site, onAction} = this.context; const {site, onAction} = this.context;
const {name, email, plan, honeypot, errors} = this.state; const {name, email, plan, phonenumber, errors} = this.state;
const hasFormErrors = (errors && Object.values(errors).filter(d => !!d).length > 0); const hasFormErrors = (errors && Object.values(errors).filter(d => !!d).length > 0);
if (!hasFormErrors) { if (!hasFormErrors) {
if (hasMultipleNewsletters({site})) { if (hasMultipleNewsletters({site})) {
this.setState({ this.setState({
showNewsletterSelection: true, showNewsletterSelection: true,
pageData: {name, email, plan}, pageData: {name, email, plan, phonenumber},
errors: {} errors: {}
}); });
} else { } else {
this.setState({ this.setState({
errors: {} errors: {}
}); });
onAction('signup', {name, email, honeypot, plan}); onAction('signup', {name, email, phonenumber, plan});
} }
} }
}); });
@ -487,7 +487,7 @@ class SignupPage extends React.Component {
}, },
{ {
type: 'text', type: 'text',
value: state.honeypot, value: state.phonenumber,
placeholder: '+1 (123) 456-7890', placeholder: '+1 (123) 456-7890',
// Doesn't need translation, hidden field // Doesn't need translation, hidden field
label: 'Phone number', label: 'Phone number',

View File

@ -258,7 +258,7 @@ function setupGhostApi({siteUrl = window.location.origin, apiUrl, apiKey}) {
} }
}, },
async sendMagicLink({email, emailType, labels, name, oldEmail, newsletters, redirect, integrityToken, customUrlHistory, autoRedirect = true}) { async sendMagicLink({email, emailType, labels, name, oldEmail, newsletters, redirect, integrityToken, phonenumber, customUrlHistory, autoRedirect = true}) {
const url = endpointFor({type: 'members', resource: 'send-magic-link'}); const url = endpointFor({type: 'members', resource: 'send-magic-link'});
const body = { const body = {
name, name,
@ -270,6 +270,7 @@ function setupGhostApi({siteUrl = window.location.origin, apiUrl, apiKey}) {
requestSrc: 'portal', requestSrc: 'portal',
redirect, redirect,
integrityToken, integrityToken,
honeypot: phonenumber, // we don't actually use a phone #, this is from a hidden field to prevent bot activity
autoRedirect autoRedirect
}; };
const urlHistory = customUrlHistory ?? getUrlHistory(); const urlHistory = customUrlHistory ?? getUrlHistory();