How to restrict Google sign-in to an institutional domain in Laravel

When an organization wants only its own people to access an application (for example, @university.edu emails), it is tempting to rely on Google’s account chooser. That is a common mistake: restricting the domain on the button does not protect your application. This article shows how to do it properly with Laravel Socialite.

1. The hd parameter is only a hint

Google accepts the hd (hosted domain) parameter so the account chooser lists accounts from that domain first. But anyone building the authorization URL by hand can remove or change it, so it is not a security barrier.

return Socialite::driver('google')
    ->with(['hd' => 'university.edu'])
    ->redirect();

Use it for user convenience, never as access control.

2. Validate the domain in the callback

The check that matters happens on the server, using the data Google returns:

$googleUser = Socialite::driver('google')->user();

$email = strtolower($googleUser->getEmail());
$allowedDomain = 'university.edu';

abort_unless(str_ends_with($email, '@'.$allowedDomain), 403);

Two details that prevent subtle bugs:

  • Compare against '@'.$domain, not just the domain. With str_ends_with($email, 'university.edu'), someone@fakeuniversity.edu would also pass.
  • Lowercase the address before comparing.

3. Check that the email is verified

Google’s response says whether the email was verified. Require it before creating or linking an account:

abort_unless(($googleUser->user['email_verified'] ?? false) === true, 403);

4. Create or update the user and assign a role

Once validated, look the user up by email and give them a default role with the fewest privileges. Elevated permissions are granted explicitly later, never inferred from the email address.

$user = User::firstOrCreate(
    ['email' => $email],
    ['name' => $googleUser->getName(), 'password' => Str::random(40)],
);

Auth::login($user);

5. Test it

Write at least three tests: an email from the allowed domain gets in, an email from another domain gets a 403, and a lookalike domain (fakeuniversity.edu) also gets a 403. They protect you from regressions when someone refactors the callback.

Summary

Layer What it is for Is it security?
hd parameter Better account-chooser experience No
Server-side domain validation Real access control Yes
email_verified Reject unconfirmed emails Yes
Least-privilege default role Limit the blast radius Yes