DEV Community

loading...

Adding Authentication(Auth0) to Your Laravel 7 Application

shahbaz17 profile image Mohammad Shahbaz Alam ・11 min read

Laravel is a free, open-source PHP web framework, intended for the development of web applications following the model–view–controller architectural pattern for companies and developers all over the world.

In this tutorial, I'll show you how to build a web application with Laravel 7 and add authentication with Auth0.

We'll be building a simple listing app with Laravel 7. Our app will simply list the last 10 UEFA Champions League winners' names with the scoreline. Once we add authentication to the app, all logged-in users will have the privilege of knowing the winner along with the scoreline.

UnAuthenticated App
Authenticated App

Let's Get Started

Laravel utilizes Composer to manage its dependencies. And I assume, you have already installed it and are ready to go. If not, please install from here. Once Composer is installed, we can install Laravel by issuing the Composer create-project command in your terminal like so:

composer create-project --prefer-dist laravel/laravel UEFA

Now run the following in your terminal to launch your application:

$ php artisan serve
Enter fullscreen mode Exit fullscreen mode

Laravel applications follow the Model-View-Controller design pattern.

  • Models query your database and return the necessary data.
  • Views are the pages that render data.
  • Controllers handles user requests, retrieves data from the models, and passes them to the views.

Setting Up The Controller

Open up your terminal and in the project root directory, run the command below to create a ListController.

php artisan make:controller ListController
Enter fullscreen mode Exit fullscreen mode

Open up app/Http/Controllers/ListController.php and configure it like so:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class ListController extends Controller
{
    public function show()
    {

       $uefa = [
        '2018-19' => ['winner' => 'Liverpool', 'played' => 'Liverpool V Tottenham Hotspur', 'score' => '2-0'],
        '2017-18' => ['winner' => 'Real Madid', 'played' => 'Real Madrid V Liverpool', 'score' => '3-1'],
        '2016-17' => ['winner' => 'Real Madid', 'played' => 'Real Madrid V Juventus', 'score' => '4-1'],
        '2015-16' => ['winner' => 'Real Madid', 'played' => 'Real Madrid V Athletico Madid', 'score' => '1*-1'],
        '2014-15' => ['winner' => 'Barcelona', 'played' => 'Barcelona V Juventus', 'score' => '3-1'],
        '2013-14' => ['winner' => 'Real Madid', 'played' => 'Real Madrid V Athletico Madid', 'score' => '4-1'],
        '2012-13' => ['winner' => 'Bayern Munich', 'played' => 'Bayern Munich V Borussia Dortmund', 'score' => '4-1'],
        '2011-12' => ['winner' => 'Chelsea', 'played' => 'Chelsea V Bayern Munich', 'score' => '4-1'],
        '2010-11' => ['winner' => 'Barcelona', 'played' => 'Manchester United V Barcelona', 'score' => '4-1'],
        '2009-10' => ['winner' => 'Internazionale', 'played' => 'Bayern Munich V Internazionale', 'score' => '4-1'],
       ];

       return view('welcome')->withUefa($uefa);
    }
}

Enter fullscreen mode Exit fullscreen mode

view('welcome')->withUefa($uefa) indicates that we are passing the $uefa array to a view called welcome.blade.php. We'll see that view later in this post.

Setting Up The Routes

Open up routes/web.php and configure it like this:

/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/

Route::get('/', 'ListController@show');
Enter fullscreen mode Exit fullscreen mode

Once a request hits the / route, it invokes the show method of the ListController and renders the returned value in the welcome view.

Setting Up Authentication

We're going to be using Auth0 for authentication. Setting up the built-in authentication with Laravel is pretty straightforward, but limited. With Auth0, you'll have access to an easy-to-use dashboard, the ability to integrate social identity providers, two-factor authentication, passwordless login, and much more.

Auth0 vs Laravel's Built-in Authentication
Laravel comes with out-of-the-box authentication that can be set up with just a bit of configuration. But with Laravel 7, it's a bit more steps.
First, you need to install laravel/ui using composer

composer require laravel/ui
Enter fullscreen mode Exit fullscreen mode

Once the laravel/ui package has been installed, you may install the frontend scaffolding using the ui Artisan command:

php artisan ui bootstrap --auth
php artisan ui vue --auth
php artisan ui react --auth
Enter fullscreen mode Exit fullscreen mode

So why use Auth0 instead?
Auth0 comes with all of these options, most of which you can enable with just a click from the dashboard:

  • Universal login from a centralized authorization server
  • Social login (Facebook, Twitter, GitHub, etc.)
  • Easily manage users from the dashboard
  • Multi-factor authentication
  • Easy role management
  • Brute force protection
  • Breached password detection
  • Account linking for users who need to merge two separate accounts
  • Option to block certain users
  • Advanced user login analytics
  • Extend Auth0 capabilities with custom rules
  • And much more!

Perhaps the greatest benefit of all is being able to shift the stress of securing your application against the never-ending threat of attacks onto someone else!

Signing up for Auth0
Let's see how easy it is to integrate Auth0 into your Laravel 7 application. If you don't already have an account, go ahead and sign up for a free Auth0 account now.

Once you've signed up, click on "Applications" on the dashboard. There will be a default application, but you need to create one with the name "Laravel 7 App" or anything you'd like, with type "Regular Web Application".
Auth0 Dashboard
Create Laravel 7 App with Regular Web App

Update these values as follows:

  • Allowed Callback URLs: http://localhost:8000/auth0/callback or http://homestead.test/auth0/callback
  • Allowed Logout URLs: http://localhost:8000 or http://homestead.test Just make sure it matches exactly what your development URL is, no trailing slashes.

Callaback-Logout-URLs

Install the Auth0 PHP plugin
Now go back to your terminal and install the Auth0 plugin and dependencies.

composer require auth0/login:"~5.0"
Enter fullscreen mode Exit fullscreen mode

This will install the Auth0 PHP plugin and Auth0 Laravel plugin.

Finishing up Auth0 integration
Next, open up the config/app.php file and add the Auth0 login service provider to the list of providers:

// ...
'providers' => [
    // ...
    Auth0\Login\LoginServiceProvider::class,
];
Enter fullscreen mode Exit fullscreen mode

Scroll down in that same file until you find the aliases array and then add the Auth0 facade:

// ...
'aliases' => [
    // ...
    'Auth0' => Auth0\Login\Facade\Auth0::class,
];
Enter fullscreen mode Exit fullscreen mode

Now you'll bind the Auth0UserRepository class that provides the User model every time a user is logged in or a JWT is decoded. Open up app/Providers/AppServiceProvider.php and add the following under register():

// ...
class AppServiceProvider extends ServiceProvider
{
    /**
     * Register any application services.
     *
     * @return void
     */
    public function register()
    {
        $this->app->bind(
        \Auth0\Login\Contract\Auth0UserRepository::class,
        \Auth0\Login\Repository\Auth0UserRepository::class
        );
    }

    // ...
}
Enter fullscreen mode Exit fullscreen mode

Now head back to the terminal to publish the plugin configuration. You'll run the following command and then it will ask you which vendor file you'd like to publish.

php artisan vendor:publish
Enter fullscreen mode Exit fullscreen mode

Select Auth0\Login\LoginServiceProvider from the resulting list, which will create the config/laravel-auth0.php configuration file.

laravel-auth0.php

<?php

return [

    /*
    |--------------------------------------------------------------------------
    |   Your auth0 domain
    |--------------------------------------------------------------------------
    |   As set in the auth0 administration page
    |
    */
    'domain'        => env( 'AUTH0_DOMAIN' ),

    /*
    |--------------------------------------------------------------------------
    |   Your APP id
    |--------------------------------------------------------------------------
    |   As set in the auth0 administration page
    |
    */
    'client_id'     => env( 'AUTH0_CLIENT_ID' ),

    /*
    |--------------------------------------------------------------------------
    |   Your APP secret
    |--------------------------------------------------------------------------
    |   As set in the auth0 administration page
    |
    */
    'client_secret' => env( 'AUTH0_CLIENT_SECRET' ),

    /*
     |--------------------------------------------------------------------------
     |   The redirect URI
     |--------------------------------------------------------------------------
     |   Should be the same that the one configure in the route to handle the
     |   'Auth0\Login\Auth0Controller@callback'
     |
     */
    'redirect_uri'  => env( 'APP_URL' ) . ':8000/auth0/callback',

    /*
    |--------------------------------------------------------------------------
    |   Persistence Configuration
    |--------------------------------------------------------------------------
    |   persist_user            (Boolean) Optional. Indicates if you want to persist the user info, default true
    |   persist_access_token    (Boolean) Optional. Indicates if you want to persist the access token, default false
    |   persist_refresh_token   (Boolean) Optional. Indicates if you want to persist the refresh token, default false
    |   persist_id_token        (Boolean) Optional. Indicates if you want to persist the id token, default false
    |
    */
    'persist_user' => true,
    'persist_access_token' => false,
    'persist_refresh_token' => false,
    'persist_id_token' => false,

    /*
    |--------------------------------------------------------------------------
    |   The authorized token issuers
    |--------------------------------------------------------------------------
    |   This is used to verify the decoded tokens when using RS256
    |
    */
    'authorized_issuers'  => [ env( 'AUTH0_DOMAIN' ) ],

    /*
    |--------------------------------------------------------------------------
    |   The authorized token audiences
    |--------------------------------------------------------------------------
    |
    */
    // 'api_identifier'  => '',

    /*
    |--------------------------------------------------------------------------
    |   The secret format
    |--------------------------------------------------------------------------
    |   Used to know if it should decode the secret when using HS256
    |
    */
    'secret_base64_encoded'  => false,

    /*
    |--------------------------------------------------------------------------
    |   Supported algorithms
    |--------------------------------------------------------------------------
    |   Token decoding algorithms supported by your API
    |
    */
    'supported_algs'        => [ 'RS256' ],

    /*
    |--------------------------------------------------------------------------
    |   Guzzle Options
    |--------------------------------------------------------------------------
    |   guzzle_options    (array) optional. Used to specify additional connection options e.g. proxy settings
    |
    */
    // 'guzzle_options' => []
];

Enter fullscreen mode Exit fullscreen mode

A few of these need to be filled in, but you want to keep them out of the repository since this is sensitive information. This is done using the .env file.

Open up .env and add the following:

AUTH0_DOMAIN=your-auth0-domain.auth0.com
AUTH0_CLIENT_ID=your-client-id
AUTH0_CLIENT_SECRET=your-client-secret
Enter fullscreen mode Exit fullscreen mode

All of these values can be found in your Auth0 dashboard under "Applications" > "Your Application" > "Settings".

ClientID

Next, take a look at what's set for APP_URL. It should match the URL that you've been using for your application, which is most likely http://homestead.test or http://localhost:8000. If you're using http://localhost:8000, make sure the port is included here! It is most likely in the top of the .env file, please don't create a new entry for this.

APP_URL=http://localhost:8000
Enter fullscreen mode Exit fullscreen mode

Integrate Auth0 with Laravel authentication system
Next, the Auth0 plugin needs to be integrated with the Laravel authentication system.

The Laravel authentication system needs a User Object from the User Provider so that it can know how user data is structured and where it is stored. This is configured in config/auth.php. The default provider is Eloquent, which will persist the User model in the database using the Eloquent ORM. For this application, we're not using the default User model.

Because our user data will be stored in Auth0's database, the Auth0 plugin comes with its own authentication driver that defines the user based on a standardized user profile instead of Laravel's User model.

To switch out the user driver, open up config/auth.php and change it to this:

// ...
'providers' => [
    'users' => [
        'driver' => 'auth0',
    ],
],
Enter fullscreen mode Exit fullscreen mode

Setup authentication routes
Open routes/web.php and add these authentication routes:

Route::get( '/auth0/callback', '\Auth0\Login\Auth0Controller@callback' )->name( 'auth0-callback' );  
Route::get( '/login', 'Auth\Auth0IndexController@login' )->name( 'login' );  
Route::get( '/logout', 'Auth\Auth0IndexController@logout' )->name( 'logout' )->middleware('auth');  
Enter fullscreen mode Exit fullscreen mode

This first route is using the Auth0Controller provided by the plugin that was installed earlier to handle the callback. If you'd like to take a look at the "magic" occurring here, you can find the controller in vendor/auth0/login/src/controllers. The rest of the Auth0 Laravel files lie in vendor/auth0/login/src/Auth/Login.

/**
    * Callback action that should be called by auth0, logs the user in.
    */
public function callback()
{
    // Get a handle of the Auth0 service (we don't know if it has an alias)
    $service = \App::make('auth0');

    // Try to get the user information
    $profile = $service->getUser();

    // Get the user related to the profile
    $auth0User = $this->userRepository->getUserByUserInfo($profile);

    if ($auth0User) {
        // If we have a user, we are going to log them in, but if
        // there is an onLogin defined we need to allow the Laravel developer
        // to implement the user as they want an also let them store it.
        if ($service->hasOnLogin()) {
            $user = $service->callOnLogin($auth0User);
        } else {
            // If not, the user will be fine
            $user = $auth0User;
        }
        \Auth::login($user, $service->rememberUser());
    }

    return \Redirect::intended('/');
}
Enter fullscreen mode Exit fullscreen mode

The next two routes handle the actual login and logout.

Route::get( '/login', 'Auth\Auth0IndexController@login' )->name( 'login' );
Route::get( '/logout', 'Auth\Auth0IndexController@logout' )->name( 'logout' )->middleware('auth');
Enter fullscreen mode Exit fullscreen mode

They use a controller called Auth0IndexController, which you need to create now.

php artisan make:controller Auth/Auth0IndexController
Enter fullscreen mode Exit fullscreen mode

Now open up app/Http/Controllers/Auth/Auth0IndexController.php and replace it with the following:

<?php

namespace App\Http\Controllers\Auth;

use Illuminate\Http\Request;
use App\Http\Controllers\Controller;

class Auth0IndexController extends Controller
{
  /**
    * Redirect to the Auth0 hosted login page
    *
    * @return mixed
    */
  public function login()
  {
    $authorize_params = [
      'scope' => 'openid profile email',
    ];
    return \App::make('auth0')->login(null, null, $authorize_params);
  }

  /**
    * Log out of Auth0
    *
    * @return mixed
    */
  public function logout()
  {
    \Auth::logout();
    $logoutUrl = sprintf(
      'https://%s/v2/logout?client_id=%s&returnTo=%s',
      env('AUTH0_DOMAIN'),
      env('AUTH0_CLIENT_ID'),
      env('APP_URL'));
    return  \Redirect::intended($logoutUrl);
  }
}
Enter fullscreen mode Exit fullscreen mode

Then the login() function will send users to Auth0 to enter in their credentials. You'll see this in action soon.

The scopes being requested are:

  • openid — to indicate that the application intends to use OIDC to verify the user's identity
  • profile — returns name, nickname, and picture.
  • email — returns email and if the email is verified

The logout() function uses those environment variables you set earlier to hit an Auth0 logout URL, redirect back to the logout URL you set in the dashboard, and clear all session data for the user.

Finally, you just need to add the login links to the navigation and display the list of winners.

Open your welcome.blade.php and configure it like this:


@extends('layouts.app')

@section('content')
<div class="container">
    <div class="row justify-content-center">
        <div class="col-md-8">
            <div class="card">
                <div class="card-header">UEFA Winners</div>

                <div class="card-body">
                    @if (session('status'))
                        <div class="alert alert-success" role="alert">
                            {{ session('status') }}
                        </div>
                    @endif

                    <!-- Table -->
                    <table class="table">
                        <thead class="thead-dark">
                            <tr>
                            <th scope="col">Year</th>
                            <th scope="col">Match Played</th>
                            <th scope="col">Winner</th>
                            <th scope="col">Score</th>
                            </tr>
                        </thead>
                        <tbody>
                            @foreach($uefa as $key => $value)
                            <tr>
                            <th scope="row">{{ $key }}</th>
                            <td>{{ $value['played'] }}</td>
                            @if(Auth::user())
                            <td><a href="#" class="btn btn-sm btn-success">{{ $value['winner'] }}</a></td>
                            <td><a href="#" class="btn btn-sm btn-info">{{ $value['score'] }}</a></td>
                            @endif
                            @if(Auth::guest())
                            <td> <a href="/login" class="btn btn-sm btn-warning"> Login to see winner </a> </td>
                            <td> <a href="/login" class="btn btn-sm btn-danger"> SCORE </a> </td>
                            </tr>
                            @endif
                            @endforeach
                        </tbody>
                    </table>
                </div>
            </div>
        </div>
    </div>
</div>
@endsection

Enter fullscreen mode Exit fullscreen mode

Here, we are looping through the $uefa array data passed from the ListController for appropriate rendering in the welcome view.

Auth::user() — You can check if a user is authenticated or not via this method from the Auth Facade. It returns true if a user is logged-in and false if a user is not.
Auth::guest() — This does the opposite of Auth::user(). It returns true if a user is not logged in and false if a user is logged in.

We are extending the layouts/app.blade.php file, for this to work properly, open layouts/app.blade.php change it to this:

<!doctype html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <!-- CSRF Token -->
    <meta name="csrf-token" content="{{ csrf_token() }}">

    <title>{{ config('app.name', 'Laravel') }}</title>

    <!-- Scripts -->
    <script src="{{ asset('js/app.js') }}" defer></script>

    <!-- Fonts -->
    <link rel="dns-prefetch" href="//fonts.gstatic.com">
    <link href="https://fonts.googleapis.com/css?family=Nunito" rel="stylesheet">

    <!-- Styles -->
    <link href="{{ asset('css/app.css') }}" rel="stylesheet">
</head>
<body>
    <div id="app">
        <nav class="navbar navbar-expand-md navbar-light bg-white shadow-sm">
            <div class="container">
                <a class="navbar-brand" href="{{ url('/') }}">
                    {{ config('app.name', 'Laravel') }}
                </a>
                <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="{{ __('Toggle navigation') }}">
                    <span class="navbar-toggler-icon"></span>
                </button>

                <div class="collapse navbar-collapse" id="navbarSupportedContent">
                    <!-- Left Side Of Navbar -->
                    <ul class="navbar-nav mr-auto">

                    </ul>

                    <!-- Right Side Of Navbar -->
                    <ul class="navbar-nav ml-auto">
                        <!-- Authentication Links -->
                        @guest
                            <li class="nav-item">
                                <a class="nav-link" href="{{ route('login') }}">{{ __('Login') }}</a>
                            </li>
                            @if (Route::has('register'))
                                <li class="nav-item">
                                    <a class="nav-link" href="{{ route('register') }}">{{ __('Register') }}</a>
                                </li>
                            @endif
                        @else
                            <li class="nav-item dropdown">
                                <a id="navbarDropdown" class="nav-link dropdown-toggle" href="#" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" v-pre>
                                    {{ Auth::user()->name }} <span class="caret"></span>
                                </a>

                                <div class="dropdown-menu dropdown-menu-right" aria-labelledby="navbarDropdown">
                                    <a class="dropdown-item" href="{{ url('/logout') }}">
                                        {{ __('Logout') }}
                                    </a>
                                </div>
                            </li>
                        @endguest
                    </ul>
                </div>
            </div>
        </nav>

        <main class="py-4">
            @yield('content')
        </main>
    </div>
</body>
</html>

Enter fullscreen mode Exit fullscreen mode

If the user is logged in, they'll see the logout button and if not, they'll see the login button.

Now that we have all the routes and views setup, your application should look like this:

Landing Page
Landing Page

We're using Auth0 for authentication, when clicked on login our application will redirect users to the Auth0 login page, so you don't have to create these on your own!
Auth0 Login Page
Auth0 Login Page

Once logged in, you will be able to see the winner's name and score.
Authenticated Login

Well done! You have successfully integrated Auth0 in your Laravel 7 Application.

Source code for this application can be found on Github.

Discussion (7)

pic
Editor guide
Collapse
ycore profile image
y-core • Edited

Hi Mohammad,

I followed the tutorial, created a new Laravel App on a local vagrant homestead box. It runs succesfully, but get the following error after Login authentication of registered user. This error appears on both the /login and /logout routes.

Client error: `POST https://<MYSUBDOMAIN>.eu.auth0.com/oauth/token` resulted in a `401 Unauthorized` response: {"error":"access_denied","error_description":"Unauthorized"}

Sequence:
https://auth.app is hosted on local vagrant homestead

  1. Click Login: https://auth.app/login

  2. Redirects: https://<MYSUBDOMAIN>.eu.auth0.com/authorize?scope=openid%20profile%20email&response_mode=query&response_type=code&redirect_uri=https%3A%2F%2Fauth.app%2Fauth0%2Fcallback&state=5f1c14b651e973.44938199&client_id=<CLIENTID>

  3. Redirects: https://auth.app/auth0/callback?code=wQqvLXGj1UgxfjxN&state=5f1c14d1447417.08360011

  4. Receive above error message

Could you assist to resolve the issue, please?

Collapse
shahbaz17 profile image
Mohammad Shahbaz Alam Author

Hi y-core,

Sure.

Can you share what you have written in Allowed callback URLs and Allowed Logout URLs?

Collapse
ribafs profile image
Ribamar FS

I run with success the application.
I create a user with register, but my password dont recogniced.
What a make to correct it.

Collapse
shahbaz17 profile image
Mohammad Shahbaz Alam Author

Hi
Happy to know it ran successfully.

You mean to say, you registered a user using email id(username) and password. And while logging in, it says password didn't recognize?

If that is so, can you try to sign up again, and make sure to type your password somewhere else too, and try logging?

If that doesn't solve the problem, let's connect over twitter.com/mdsbzalam

Thank you

Collapse
ribafs profile image
Ribamar FS

Thanks for your quick reply.

The user is in the database, but when I try to log in, the password is not recognized. I created the user again and again does not recognize.

Thread Thread
shahbaz17 profile image
Mohammad Shahbaz Alam Author

I think, in that case, I need to see the code to help you out here.

Collapse
ribafs profile image
Ribamar FS

I try to recovery my password but receive

These credentials do not match our records.