Laravel Documentation Series: Manually Attempting Authentication.
Laravel Documentation Explored: EP1.
This year, I set out to read the Laravel docs at least once a week. This week, I dived into authentication.
I learned something new, which I had not been much aware of.
Say we have a login implementation, but we only want users with admin roles to be able to get a successful login response. I have seen implementations like below in the past:
$email = $request->email;
$password = $request->password;
if (! Auth::attempt([
'email' => $email,
'password' => $password,
'role' => Role::ADMIN
])) {
// return error message
}
$user = User::where('email', $email)->first();
if (! $user?->isAdmin()) {
// return error message
}
// Start session or create token
Looking at the docs, instead of an implementation like above, there are 3 shorter ways to do this.
Option 1:
Say the user role to check for is in the same table (`users`), you can add a 3rd parameter to the `Auth::attempt` method like below, and that will be enough to make sure only admins will be authenticated.
if (Auth::attempt([
'email' => $email,
'password' => $password,
'role' => Role::ADMIN
])) {
// Authentication was successful...
}
Option 2:
For an implementation where the user role sits on another table (e.g., `user_roles`, `models_role`), the first implementation would not work. The 3rd parameter of the `Auth::attempt` method accepts a closure; this can be utilised to add complex queries to the login attempt. Keeping in line with checking if the user logging in has an admin role, implementing option 2 would look like:
if (Auth::attempt([
'email' => $email,
'password' => $password,
fn (Builder $query) => $query->has(adminRole),
])) {
// Authentication was successful...
}
Obviously, you would have to implement the relationship between `User` and the `Role` model. An example is shown below:
class UserRole extends Model {
public function adminRole(): BelongsTo {
return $this->belongsTo(User::class, 'user_id')
->where('role_id', Role::ADMIN);
}
}
Option 3:
Using a different method called `Auth::attemptWhen`. What this does is, there is a pre-check before attempting authentication. Keeping in line with checking if the user logging in has an admin role, implementing option 3 would look like:
if (Auth::attemptWhen([
'email' => $email,
'password' => $password,
], function (User $user) {
return $user->isAdmin();
})) {
// Authentication was successful...
}
`isAdmin` here would be implemented in the `User` model to check if the user has the admin role. If the closure returns true, then the authentication happens.
These options might reduce the lines of code, but they don’t really reduce the number of queries executed.
PS: All implementations are pseudo code.