Username? Email? Both? Yes!

People need to be able to log in to your application, right? Of course! And sometimes, people don’t want to log in via a 3rd party source like Google or Facebook. As much as we might not want to do it, supporting user name and password logins is still a requirement for web apps today (and frankly, that might be a good thing, considering the power big tech companies already leverage over the internet).

Those who use Laravel (top-tier people, if you ask me) might be aware of starter kits like Laravel Breeze and Jetstream, which provide you with authentication pages and functionality out of the gate. These are great, but their boilerplate only contains authentication logic for using someone’s email. What if people want to use a username? Yes, they are out there. And we want to give the people what they want, right?

We can roll our own authentication logic to implement both username and email login, or we can modify what happens with Breeze or Jetstream. Let’s look at rolling our own first. And really, all we are doing is providing a route and a login function. You can do this with a controller if you like. For simplicity, let’s make it an anonymous function:

Route::post('/login', function(Request $request) {
   $credentials = $request->validate([
	   'email_or_username' => 'required',
	   'password' => 'required|(other good pass requirements)'
   ]);
})

Alright, breaking this down. Under the hood, the auth methods provided by Breeze or Jetstream expect email to be passed in the request. But our application expects either and just needs the field to be required. And that can be tricky! We first need to figure out what we are working with here. We could use regex, but I am always wary of rolling my own regex when validating emails. It may be a case of being able to pass the blame, but if I am using a framework, I want to rely on what is good enough for the framework. Thankfully, Laravel provides methods to validate an email on demand like it would under the hood by letting us create validators.

$emailValidator = Validator::make($credentials, [
	'email_or_username' => 'email'
]);

$type = $emailValidator->fails() ? 'username' : 'email';

Since we know we have an email or username field in this validator, we only need to check if it passes the email test. If it doesn’t, we know we have a username! Next, we can use the Auth facade to attempt to authenticate a user with the given type and value:

$authenticated = Auth::attempt([

	$type => $credentials['email_or_username'],
	'password' => $credentials['$password']
]);

return $authenticated ? redirect('/loggedin') : redirect(back()->withErrors([...]));

This is a simplified version of how to handle someone logged in versus a failed attempt. However, the principle of handling either a username or a password can apply to any scenario. To require either, use a validator to test for one, then use the auth facade to authenticate with whichever type is appropriate. Easy peasy!

The process is fairly simple if you are already using Breeze or Jetstream and want to use either email or username. It’s a little more complicated for Jetstream, but not much. You must use the Fortify::authenticateUsing method and call it in your Jetstream service provider. This method receives the request as a parameter like any routing method, so we can use the exact same code and logic for authenticating, except you will want to return the logged-in user instead of a response. More info can be found in the official documentation.

For breeze, the controllers you would use to log in as a user are readily available! Just modify App\Http\Requests\Auth\LoginRequest.php with the logic for accepting email or username. Of course, you must modify the views used to pass the login request for both of these solutions.

Let people use a username or email to log in, whichever they prefer. Thankfully, Laravel makes it very easy!


Categories

  • Development
  • Project Planning

Tags:

  • Laravel
  • Code
  • Tutorial