The redwood way to make sure emails as usernames are unique

Howdy!

So I am making an app using Redwoods dbAuth at the moment. I noticed that if a user changes the capitalization of an email the user can end up with a duplicated email.

I am wondering there a “Redwood way” that i should be trying to deal with this situation so when the user enters an email, no matter what case it will not be duplicated in the DB. Would I reach for a directive for example? What’s the communities advice? Or should I just be “to lowercasing” everything that goes in

Thanks
Shannon

1 Like

Hi @shansmith01

Have you thought about Redwood Records ?

I have them installed but I haven’t worked on such scenario just yet.
However, the idea would be that any modification of your model should pass through a Record class, which I believe would, with appropriate implementation, make sure your email field would always be lowercased / checked for deduplication.

Worth a try, plus you can install those without being forced of converting your entire application.

HI @shansmith01 I think there are a few options:

  1. Use the “citext” data type in Postgres (and an extensions) so that the “email” is a “case-insensitive text” field. See: PostgreSQL: Documentation: 12: F.8. citext

  2. Update the user find in dbAuth to be insensitive, See: Case sensitivity (Reference) | Prisma Docs

const users = await prisma.user.findMany({
  where: {
    email: {
      endsWith: 'prisma.io',
      mode: 'insensitive', // Default value: default
    },
  },
})

For example:

I think the latter makes most sense. @rob what do you think?

1 Like

Yeah that should probably just be a case-insensitive search. Do you know if that works for all DB providers @dthyresson ? Prisma seems to famously love throwing errors if you try to do something that a certain DB doesn’t support <cough SQLite cough>. As long as mode: 'insensitive' works all the time we should add that.

Looks like SQLite is an issue - Case sensitivity (Reference) | Prisma Docs

See, I don’t know why they don’t just ignore that option since it’s the default in SQLite, for some reason they feel like they have to throw an error. UGH

@shansmith01

I’m very new to RedwoodJS but wondering the same thing. I’ve also noticed that if there are any spaces around the email it will also create a duplicate.

I wonder if there is a way to hook into a custom service for the dbAuth package? That would seem like the logical place to sanitize and validate all user info on signup and login, e.g., remove extra spaces, lowercase emails, validate email, etc. I realize there are the signup and login handlers in api/src/functions/auth.js but in the case of the signup handler, it only calls it after checking if the username already exists. Would obviously like to do this beforehand.

Just thinking out loud.

There is a handler for login and signup that you can define. See redwood/DbAuthHandler.ts at 242f3f0d753a07e357ea50a915a18389d1eecfd4 · redwoodjs/redwood · GitHub

/**
 * Whatever you want to happen to your data on new user signup. Redwood will
 * check for duplicate usernames before calling this handler. At a minimum
 * you need to save the `username`, `hashedPassword` and `salt` to your
 * user table. `userAttributes` contains any additional object members that
 * were included in the object given to the `signUp()` function you got
 * from `useAuth()`
 */

You could:

  • trim
  • lowercase

An example can be found in the docs here: Authentication | RedwoodJS Docs

And that should help with duplications

But I agree that this likely should be done by default. If you think so, please write up an issue or happy to review a PR. Thanks!

1 Like

Services are just plain functions, you could create an uber-email validation to be used within your services, but also import it into /api/src/functions/auth.js and use it in the signup handler!

1 Like

But I agree that this likely should be done by default.

My only issue with this is that signup was meant to be generic and not require any one format, which is why it’s just called “username” and not “email”. Many services will use an email as their username, but not always (most forums and chat apps, for example). I don’t want to enforce that that field must be an email.

If we did provide some kind of email validation I think it should be opt-in (or maybe it defaults to on but you can opt-out if you decide that your username isn’t an email address).

Agree about it being generic and not enforcing username over email or vice versa.

Do you see any drawback as to what @dthyresson was saying about just “.trim().toLowerCase()” being built into the dbAuth package by default? I think in either case, be it username or email, you would want to trim whitespace and normalize the uniqueness check to lowercase. Even with a username, I don’t think you’d ever want to allow for example “ASampleUsername” and “asampleusername” to both exist in the database.

1 Like