By default, AdonisJS Auth will be case-sensitive when checking your user’s UID value during login. This means, that if we allow our user’s to log in with their username, they’ll need to provide the username with the same casing they used when they signed up.
So long as you’re ensuring usernames within your application are case-insensitive unique, we can simplify things for our users by altering the Auth attempt call to be case-insensitive, allowing them to use their username with any casing combination. This comes in handy on mobile, tablets, and odd systems where the keyboard may default to the first character being capitalized.
If you’re new to AdonisJS authentication, you can get caught up in 15 minutes in our “AdonisJS Authentication in 15 Minutes” lesson.
Ensuring Usernames Are Unique Case-Insensitive
First, before we begin, let’s ensure we’re forcing all our usernames to be case-insensitive and unique within our database using the AdonisJS Validator.
const userSchema = schema.create({ username: schema.string( { trim: true }, [ rules.unique({ table: 'users', column: 'username', caseInsensitive: true // 👈 }) ] ), email: schema.string( { trim: true }, [ rules.email(), rules.unique({ table: 'users', column: 'email', caseInsensitive: true // 👈 }) ] ), password: schema.string({}, [rules.minLength(8)]) })
Copied!
Also, note that if you have a pre-existing database here, you’ll want to run a query to verify this won’t cause trouble for any existing users.
Making Auth Attempt Case-Insensitive
Now that we’ve verified that this change won’t impact any of our user’s abilities to log in or register, let’s go ahead and implement the ability to log in using a case-insensitive UID.
First, let’s head into our User model at App/Models/User.ts
. When authenticating a user, AdonisJS performs a check for a method on our User model, called findForAuth
, this check is shown below.
// AdonisJS' search for a findForAuth function if (typeof model.findForAuth === 'function') { const user = await model.findForAuth(this.config.uids, uidValue) return this.getUserFor(user) }
Copied!
If this method isn’t present on our User model, AdonisJS will perform an orWhere
for each UID defined within our config, shown below.
// the AdonisJS default const { query } = await this.getModelQuery() this.config.uids.forEach((uid) => query.orWhere(uid, uidValue)) return this.findUser(query)
Copied!
So, in order to make our UID search case-insensitive, we’ll want to define a method called findForAuth
on our User model. This method will be provided the uids
array from our config along with the login attempt’s uidValue
. then, inside our findForAuth
method we’ll want to create a query that’ll find our user.
public static async findForAuth(uids: string[], uidValue: string) { return this.query().where(query => uids.map(uid => query.orWhere(uid, 'ILIKE', uidValue)) ).firstOrFail() }
Copied!
By using an ILIKE
, we’re telling the query to perform a case-insensitive LIKE
check for the uidValue
. However, we aren’t adding any %
signs, so we’re essentially performing a case-insensitive equality check here which is exactly what we want.
With this in place, AdonisJS will now use the findForAuth
function we’ve defined on our User model when attempting to find a user during login. Which, we’ve defined to search for a UID match that’s case-insensitive. Job well done, everyone!
AdonisJS Lucid v18
If you’re running AdonisJS Lucid version 18 or later (the May 2022 release), there’s a new query builder method called whereILike
. You can swap the above query.orWhere(uid, 'ILIKE', uidValue)
for query.orWhereILike(uid, uidValue)
for a cleaner alternative.
TypeScript Is Complaining?
Though the above works just fine, you may run into TypeScript complaining about your findForAuth
method. There may be a better way to rectify this, however, I’ve found altering how we attach it to our User model resolves the TypeScript complaint.
First, switch your User model to export default at the end of the file.
class User extends BaseModel { // ... public static async findForAuth(uids, uidValue) { return this.query().where(query => uids.map(uid => query.orWhere(uid, 'ILIKE', uidValue)) ).firstOrFail() } } export default User
Copied!
Then, move your findForAuth
method declaration outside of the User
model.
class User extends BaseModel { // ... } User['findForAuth'] = function (uids, uidValue) { return this.query().where(query => uids.map(uid => query.orWhere(uid, 'ILIKE', uidValue)) ).firstOrFail() } export default User
Copied!
With that, TypeScript should no longer be complaining and our application will use a case-insensitive lookup on our user’s UID value when logging in.
Join The Discussion! (0 Comments)
Please sign in or sign up for free to join in on the dicussion.
Be the first to Comment!