Laravel Builtin Authorisation & Social Login

What we'll cover

  • What Laravel provides out of the box
  • Introduce Laravel Socialite
  • Implementing Social Authentication
  • Deeper look at Socialite

What Laravel provides out of the box

Included auth files

We only need to add a few details to get going

// app/Http/routes.php

// Authentication routes...
Route::get('auth/login', 'Auth\AuthController@getLogin');
Route::post('auth/login', 'Auth\AuthController@postLogin');
Route::get('auth/logout', 'Auth\AuthController@getLogout');

// Registration routes...
Route::get('auth/register', 'Auth\AuthController@getRegister');
Route::post('auth/register', 'Auth\AuthController@postRegister');

// Password reset link request routes...
Route::get('password/email', 'Auth\PasswordController@getEmail');
Route::post('password/email', 'Auth\PasswordController@postEmail');

// Password reset routes...
Route::get('password/reset/{token}', 'Auth\PasswordController@getReset');
Route::post('password/reset', 'Auth\PasswordController@postReset');

Routes

Views

<!-- resources/views/auth/login.blade.php -->

<form method="POST" action="/auth/login">
    {!! csrf_field() !!}

    <div>
        Email
        <input type="email" name="email" value="{{ old('email') }}">
    </div>

    <div>
        Password
        <input type="password" name="password" id="password">
    </div>

    <div>
        <input type="checkbox" name="remember"> Remember Me
    </div>

    <div>
        <button type="submit">Login</button>
    </div>
</form>

Views continued...

<!-- resources/views/auth/register.blade.php -->

<form method="POST" action="/auth/register">
    {!! csrf_field() !!}

    <div>
        Name
        <input type="text" name="name" value="{{ old('name') }}">
    </div>

    <div>
        Email
        <input type="email" name="email" value="{{ old('email') }}">
    </div>

    <div>
        Password
        <input type="password" name="password">
    </div>

    <div>
        Confirm Password
        <input type="password" name="password_confirmation">
    </div>

    <div>
        <button type="submit">Register</button>
    </div>
</form>

Views continued...

<!-- resources/views/auth/password.blade.php -->

<form method="POST" action="/password/email">
    {!! csrf_field() !!}

    <div>
        Email
        <input type="email" name="email" value="{{ old('email') }}">
    </div>

    <div>
        <button type="submit">
            Send Password Reset Link
        </button>
    </div>
</form>

Views continued...

<!-- resources/views/auth/reset.blade.php -->

<form method="POST" action="/password/reset">
    {!! csrf_field() !!}
    <input type="hidden" name="token" value="{{ $token }}">

    <div>
        Email
        <input type="email" name="email" value="{{ old('email') }}">
    </div>

    <div>
        Password
        <input type="password" name="password">
    </div>

    <div>
        Confirm Password
        <input type="password" name="password_confirmation">
    </div>

    <div>
        <button type="submit">
            Reset Password
        </button>
    </div>
</form>

Email view

<!-- resources/views/emails/password.blade.php -->

Click here to reset your password: {{ url('password/reset/'.$token) }}

A note on password reset

Actually... auth.password.expire

<?php
// config/auth.php

return [

    ...

    'password' => [
        'email' => 'emails.password',
        'table' => 'password_resets',
        'expire' => 60,
    ],
];

And you're ready to go!

  • Migrations
  • Views
  • Routes
  • Controllers
  • Emails
$ php artisan migrate:install
Migration table created successfully.

$ php artisan migrate
Migrated: 2014_10_12_000000_create_users_table
Migrated: 2014_10_12_100000_create_password_resets_table

Migrate the auth tables

Lets look a bit further however. How does this all work?

  • Controller comes with a few required methods
  • Setting forms up with specific input fields
  • Routes to built-in methods for registration

By default, the following methods are to be implemented for registration

<?php

namespace App\Http\Controllers\Auth;
...
class AuthController extends Controller
{
    use AuthenticatesAndRegistersUsers, ThrottlesLogins;
    ...
    protected function validator(array $data)
    {
        return Validator::make($data, [
            'name' => 'required|max:255',
            'email' => 'required|email|max:255|unique:users',
            'password' => 'required|confirmed|min:6',
        ]);
    }
    ...
    protected function create(array $data)
    {
        return User::create([
            'name' => $data['name'],
            'email' => $data['email'],
            'password' => bcrypt($data['password']),
        ]);
    }

Which is used by the RegistersUsers Trait

<?php

namespace Illuminate\Foundation\Auth;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;

trait RegistersUsers
{
    ...

    public function getRegister()
    {
        return view('auth.register');
    }

    ...
    public function postRegister(Request $request)
    {
        $validator = $this->validator($request->all());

        if ($validator->fails()) {
            $this->throwValidationException(
                $request, $validator
            );
        }

        Auth::login($this->create($request->all()));

        return redirect($this->redirectPath());
    }
}
<?php

namespace Illuminate\Foundation\Auth;

trait AuthenticatesAndRegistersUsers
{
    use AuthenticatesUsers, RegistersUsers {
        AuthenticatesUsers::redirectPath insteadof RegistersUsers;
    }
}

How we "remember user" works?

The AuthController uses the AuthenticatesAndRegistersUsers Trait

This Trait is composed of a few other Traits, namely AuthenticatesUsers

<?php

namespace Illuminate\Foundation\Auth;

...

trait AuthenticatesUsers
{

    /**
     * Handle a login request to the application.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    public function postLogin(Request $request)
    {
        ...

        if (Auth::attempt($credentials, $request->has('remember'))) {
            return $this->handleUserWasAuthenticated($request, $throttles);
        }

Which includes....

As you can see, postLogin looks for the remember field in the request data. Auth::attempt expects a boolean for the second parameter which is to remember user.

Introduce Laravel Socialite

  • Social login for Laravel
  • Library for  OAuth logins
  • Includes facebooktwitterlinkedin
    googlegithub and bitbucket
  • Pluggable drivers (extensible)

Implementing Social Authentication

Install

$ composer require laravel/socialite

Register Service Provider and Facade

<?php

// config/app.php
return [
    ...
    'providers' => [
        // Other service providers...
        ...
        Laravel\Socialite\SocialiteServiceProvider::class,
    ],
    ...
    'aliases' => [
        // Other service aliases...
        ...
        'Socialite' => Laravel\Socialite\Facades\Socialite::class,
    ],
];

Add configuration for auth providers

<?php
// config/services.php

return [
    ...
    'github' => [
        'client_id' => 'your-github-app-id',
        'client_secret' => 'your-github-app-secret',
        'redirect' => 'http://yourapp.com/auth/github/callback',
    ],

    'facebook' => [
        ...
    ],

    'google' => [
        ...
    ],

    'linkedin' => [
        ...
    ],

    'bitbucket' => [
        ...
    ],

    'twitter' => [
        ...
    ],
...

Lets add a few more details. It would be ideal to manage active providers.

<?php
// config/services.php

return [
    ...
    'enabled_providers' => [
        'github',
        'facebook',
        'google',
        'linkedin',
        'bitbucket',
        'twitter',
    ],
...
  • Help for enabling/disabling providers
  • Configure routing dynamically

Update Views

<?php
// resources/views/login.blade.php
// resources/views/register.blade.php
...

    {{-- Add login providers --}}
    @foreach (Config::get('services.enabled_providers', []) as $provider)

        <div class="login-provider">
            <a class="{{ $provider }}-provider" href="{{ url('auth/' . $provider) }}">{{ $provider }}</a>
        </div>

    @endforeach

</form>

Register Routes

<?php

...

// OAuth provider routes
Route::get('auth/{provider}', 'Auth\AuthController@redirectToProvider')
    ->where('provider', implode('|', Config::get('services.enabled_providers', [])));
Route::get('auth/{provider}/callback', 'Auth\AuthController@handleProviderCallback')
    ->where('provider', implode('|', Config::get('services.enabled_providers', [])));

Register Controller Methods

<?php namespace App\Http\Controllers\Auth;
...
use Socialite;
...
class AuthController extends Controller
{
    ...
    public function redirectToProvider($provider)
    {
        return Socialite::driver($provider)->redirect();
    }
    ...
    public function handleProviderCallback($provider)
    {
        $user = Socialite::driver($provider)->user();

        // OAuth Two Providers
        $token = $user->token;

        // OAuth One Providers
        $token = $user->token;
        //$tokenSecret = $user->tokenSecret;

        // All Providers
        $user->getId();
        $user->getNickname();
        $user->getName();
        $user->getEmail();
        $user->getAvatar();
    }

We're done!

A huge thanks to the docs!

Laravel Authentication & Social Login

By Shaun Smekel

Laravel Authentication & Social Login

  • 336