Mohammad Shahbaz Alam
Posted on March 29, 2020
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.
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:
Now run the following in your terminal to launch your application:
$ php artisan serve
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
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);
}
}
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');
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
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
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".
Update these values as follows:
- Allowed Callback URLs:
http://localhost:8000/auth0/callback
orhttp://homestead.test/auth0/callback
- Allowed Logout URLs:
http://localhost:8000
orhttp://homestead.test
Just make sure it matches exactly what your development URL is, no trailing slashes.
Install the Auth0 PHP plugin
Now go back to your terminal and install the Auth0 plugin and dependencies.
composer require auth0/login:"~5.0"
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,
];
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,
];
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
);
}
// ...
}
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
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' => []
];
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
All of these values can be found in your Auth0 dashboard under "Applications" > "Your Application" > "Settings".
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
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',
],
],
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');
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('/');
}
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');
They use a controller called Auth0IndexController, which you need to create now.
php artisan make:controller Auth/Auth0IndexController
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);
}
}
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
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>
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:
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
Once logged in, you will be able to see the winner's name and score.
Well done! You have successfully integrated Auth0 in your Laravel 7 Application.
Source code for this application can be found on Github.
Posted on March 29, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.