I have a few different applications and admin panels that I use pretty frequently throughout my day-to-day. All of these application have basic email and password authentication. Recently I wanted to change this to allow acces to these systems based on my corporate account @131studios.com
Currently I'm the only one that accesses these systems and in the future, if anyone joins the team, they will also need access to these as well. Since I use G-Suite for email, this process is fairly simple using Laravel and Laravel Socialite
First we'll start by installing Socialite
1composer require laravel/socialite
The next step is to configure our socialite driver. In this case we will be using Google for our authentication and authorization. To do this we need to add a driver to our config/services.php
1'google' => [2 'client_id' => env('GOOGLE_CLIENT_ID'),3 'client_secret' => env('GOOGLE_CLIENT_SECRET'),4 'redirect' => env('GOOGLE_CLIENT_REDIRECT'),5 ],
then update your .env
with the appropriate values
1GOOGLE_CLIENT_ID=2GOOGLE_CLIENT_SECRET=3GOOGLE_CLIENT_REDIRECT=
We're going to need 2 routes to handle our redirect and callback for our Oauth controller. In your web.php
create the following routes
1Route::get('/oauth/{provider}', 'Auth\OauthController@redirect')->name('oauth.redirect');2Route::get('/oauth/{provider}/callback', 'Auth\OauthController@callback');
We're going to create a controller to handle our Oauth requests. I like to use the App\Http\Controllers\Auth
namespace.
1php artisan make:controller Auth\OauthController
This controller will handle redirecting the user to the Oauth Provider as well as handling the authorization and authentication of the user when they pass through. Let's handle the former first.
We'll create a redirect()
method to handle the redirect to the provider
1... 2 /** 3 * @param $provider 4 * 5 * @return \Symfony\Component\HttpFoundation\RedirectResponse 6 */ 7 public function redirect($provider) 8 { 9 return Socialite::driver($provider)->redirect();10 }11...
Let's break this down. Since we can support multiple drivers such as Google, Github, Facebook and Twitter we will accept the $provider
variable from our route. Then we will simply tell Socialite to redirect us to our provider's Oauth consent screen.
Once the user accepts the consent banner they are redirected back to our callback route that was set in your ENV. At that point we are going to need to either fetch an existing user or create the user if they do not currently exist. Remember we are only going to do this for users that are a member of our organization only, 131studios.com
In our OauthController.php
let's create a few methods to do that
1... 2 3 /** 4 * @var array 5 */ 6 protected $allowedDomains = [ 7 '@131studios.com', 8 ]; 9 10 /**11 * @param $provider12 *13 * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector14 */15 public function callback($provider)16 {17 $user = Socialite::driver($provider)->user();18 19 if ($this->isAnAllowedDomainUser($user)) {20 $this->findOrCreateUser($user);21 22 return redirect()->intended('/dashboard');23 }24 25 return redirect()->route('login')->withErrors([26 'email' => 'You must be a member of the 131Studios Organization to Login',27 ]);28 }29 30 /**31 * @param $user32 *33 * @return bool34 */35 private function isAnAllowedDomainUser($user)36 {37 return Str::endsWith($user->email, $this->allowedDomains);38 }39 40 /**41 * @param $user42 *43 */44 private function findOrCreateUser($user)45 {46 $authorizedUser = User::firstOrCreate(47 ['email' => $user->getEmail()],48 ['name' => $user->getName(), 'password' => bcrypt(Str::random(15)),]49 );50 51 $this->guard()->login($authorizedUser);52 }53 54 /**55 * @return mixed56 */57 private function guard()58 {59 return Auth::guard(config('auth.defaults.guard'));60 }
The first part of the callback
method will grab the user from the socialite driver. We'll store that user for use throughout.
The next part is very important to ensure that only users within our organization are allowed access. First we check whether the user is allowed. We do this by verifying the domain portion of the Oauth user to our array of $allowedDomains
. We do this with a simple string comparison method
1/**2 * @param $user3 *4 * @return bool5 */6 private function isAnAllowedDomainUser($user)7 {8 return Str::endsWith($user->email, $this->allowedDomains);9 }
If the user is allowed we'll either grab the user from our database or create a new user based off the Oauth user. Once we either fetch the user or create a new user, we will log them in using our default guard
1/** 2 * @param $user 3 * 4 */ 5 private function findOrCreateUser($user) 6 { 7 $authorizedUser = User::firstOrCreate( 8 ['email' => $user->getEmail()], 9 ['name' => $user->getName(), 'password' => bcrypt(Str::random(15)),]10 );11 12 $this->guard()->login($authorizedUser);13 }14 15 /**16 * @return mixed17 */18 private function guard()19 {20 return Auth::guard(config('auth.defaults.guard'));21 }
Finally we redirect the user to their dashboard or we will send them back to the login page with an error.
1/** 2 * @param $provider 3 * 4 * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector 5 */ 6 public function callback($provider) 7 { 8 ... 9 10 if ($this->isAnAllowedDomainUser($user)) {11 $this->findOrCreateUser($user);12 13 return redirect()->intended('/dashboard');14 }15 16 return redirect()->route('login')->withErrors([17 'email' => 'You must be a member of the 131Studios Organization to Login',18 ]);19 }
At this point we are ready to accept Oauth logins to our administrator backends.