Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions backend/src/middleware/email/emailMiddleware.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ if (!hasEmailConfig) {
const sendSignupMail = async (resolve, root, args, context, resolveInfo) => {
const response = await resolve(root, args, context, resolveInfo)
const { email, nonce } = response
await sendMail(signupTemplate({ email, nonce }))
if (nonce) await sendMail(signupTemplate({ email, nonce }))
delete response.nonce
return response
}
Expand All @@ -61,7 +61,7 @@ const sendPasswordResetMail = async (resolve, root, args, context, resolveInfo)
const sendEmailVerificationMail = async (resolve, root, args, context, resolveInfo) => {
const response = await resolve(root, args, context, resolveInfo)
const { email, nonce, name } = response
await sendMail(emailVerificationTemplate({ email, nonce, name }))
if (nonce) await sendMail(emailVerificationTemplate({ email, nonce, name }))
delete response.nonce
return response
}
Expand Down
8 changes: 6 additions & 2 deletions backend/src/schema/resolvers/emails.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,12 @@ export default {
throw new UserInputError('must be a valid email')
}

// check email does not belong to anybody
await existingEmailAddress({ args, context })
const emailAddress = await existingEmailAddress({
args,
context,
throwIfBelongsToUser: false,
})
if (emailAddress && emailAddress.belongsToUser) return { email: args.email }

const nonce = generateNonce()
const {
Expand Down
10 changes: 7 additions & 3 deletions backend/src/schema/resolvers/emails.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -126,11 +126,15 @@ describe('AddEmailAddress', () => {
})

describe('but if another user owns an `EmailAddress` already with that email', () => {
it('throws UserInputError because of unique constraints', async () => {
it('returns a neutral response without disclosing that the account exists', async () => {
await Factory.build('user', {}, { email: 'new-email@example.org' })
await expect(mutate({ mutation, variables })).resolves.toMatchObject({
data: { AddEmailAddress: null },
errors: [{ message: 'A user account with this email already exists.' }],
data: {
AddEmailAddress: {
email: 'new-email@example.org',
},
},
errors: undefined,
})
})
})
Expand Down
12 changes: 10 additions & 2 deletions backend/src/schema/resolvers/helpers/existingEmailAddress.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { UserInputError } from 'apollo-server'

export default async function alreadyExistingMail({ args, context }) {
export default async function alreadyExistingMail({ args, context, throwIfBelongsToUser = true }) {
const session = context.driver.session()
try {
const existingEmailAddressTxPromise = session.writeTransaction(async (transaction) => {
Expand All @@ -21,7 +21,15 @@ export default async function alreadyExistingMail({ args, context }) {
})
const [emailBelongsToUser] = await existingEmailAddressTxPromise
const { alreadyExistingEmail, user } = emailBelongsToUser || {}
if (user) throw new UserInputError('A user account with this email already exists.')
if (user && throwIfBelongsToUser) {
throw new UserInputError('A user account with this email already exists.')
}
if (alreadyExistingEmail) {
return {
...alreadyExistingEmail,
belongsToUser: !!user,
}
}
return alreadyExistingEmail
} finally {
session.close()
Expand Down
11 changes: 9 additions & 2 deletions backend/src/schema/resolvers/registration.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,15 @@ export default {
Signup: async (_parent, args, context) => {
args.nonce = generateNonce()
args.email = normalizeEmail(args.email)
let emailAddress = await existingEmailAddress({ args, context })
if (emailAddress) return emailAddress
let emailAddress = await existingEmailAddress({
args,
context,
throwIfBelongsToUser: false,
})
if (emailAddress) {
if (emailAddress.belongsToUser) return { email: args.email }
return emailAddress
}
try {
emailAddress = await neode.create('EmailAddress', args)
return emailAddress.toJson()
Expand Down
5 changes: 3 additions & 2 deletions backend/src/schema/resolvers/registration.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -108,9 +108,10 @@ describe('Signup', () => {
await emailAddress.relateTo(user, 'belongsTo')
})

it('throws UserInputError error because of unique constraint violation', async () => {
it('returns a neutral response without disclosing that the account exists', async () => {
await expect(mutate({ mutation, variables })).resolves.toMatchObject({
errors: [{ message: 'A user account with this email already exists.' }],
data: { Signup: { email: 'someuser@example.org' } },
errors: undefined,
})
})
})
Expand Down