PHP (Laravel)
Sample Project
Download a sample project specific to this tutorial configured with your Auth0 API Keys.
- Composer 1.5
- PHP 7.0
- Laravel 5.5
Install and Configure Laravel 5.5
If you are installing Auth0 to an existing app, you can skip this section. Otherwise, walk through the Laravel guides below to get started with a sample project.
- Installation
- Use any of the install methods listed to start a new project
- PHP can be served any way that works for your development process (we use Homebrew-installed Apache and PHP)
- Walk through the "Configuration" section completely
- Configuration
- Create a .env file, used later for critical and sensitive Auth0 connection values
- Make sure
APP_DEBUG
is set totrue
By the end of those 2 sections, you should have a Laravel application up and running locally or on a test server.
Install Auth0 Login and Dependencies
To install this plugin run composer require auth0/login:"~5.0"
This will install:
- The Auth0 PHP SDK in
vendor\auth0\auth0-php
- The Auth0 Laravel plugin in
vendor\auth0\login
Enable Auth0 Login in Laravel
First, we need to add the Auth0 Services to the list of Providers in config/app.php
:
// config/app.php
'providers' => array(
// ...
Auth0\Login\LoginServiceProvider::class,
);
If you want to use an Auth0
facade, add an alias in the same file (not required, more information on facades here):
// config/app.php
'aliases' => array(
// ...
'Auth0' => Auth0\Login\Facade\Auth0::class
);
Finally, you will need to bind a class that provides the app's User model each time a user is logged in or a JWT is decoded. You can use the Auth0UserRepository
provided by this package or build your own (see the "Custom User Handling" section below).
Add the following to your AppServiceProvider::register()
method:
// app/Providers/AppServiceProvider.php
// ...
use Auth0\Login\Contract\Auth0UserRepository as Auth0Contract;
use Auth0\Login\Repository\Auth0UserRepository as UserRepo;
class AppServiceProvider extends ServiceProvider
{
// ...
public function register()
{
$this->app->bind(
Auth0Contract::class,
UserRepo::class
);
}
}
Configure It
To configure the plugin, you must publish the plugin configuration and complete the file config/laravel-auth0.php
using information from your Auth0 account and details of your implementation.
Publish the default configuration file with the following command:
php artisan vendor:publish
Select the option for Auth0\Login\LoginServiceProvider
and look for Publishing complete.
This creates the file config/laravel-auth0.php
with the following settings:
domain
- Your Auth0 tenant domain, found in your Application settings (required)client_id
- Your Auth0 Client ID, found in your Application settings (required)client_secret
- Your Auth0 Client Secret, found in your Application settings (required)redirect_uri
- The callback URI for your Laravel application to handle the login response from Auth0; by default this isAPP_URL/auth0/callback
(required)persist_user
- Should the user information persist in a PHP session? Default istrue
persist_access_token
- Should the Access Token persist in a PHP session? Default isfalse
persist_id_token
- Should the ID Token persist in a PHP session? Default isfalse
authorized_issuers
- An array of authorized token issuers, should include at least your tenant domainapi_identifier
- The authorized token audiencesecret_base64_encoded
- Is the Client Secret Base64 encoded? Look below the Client Secret field in the Auth0 dashboard to see how to set this; default isfalse
supported_algs
- JWT decoding algorithms supported by your application; default isRS256
guzzle_options
- Specify additional request options for Guzzle
To keep sensitive data out of version control and allow for different testing and production instances, we recommend using the .env
file Laravel uses to load other environment-specific variables:
// .env
// ...
AUTH0_DOMAIN=YOUR_AUTH0_DOMAIN
AUTH0_CLIENT_ID=YOUR_CLIENT_ID
AUTH0_CLIENT_SECRET=YOUR_CLIENT_SECRET
Call env()
in laravel-auth0.php
to get the correct setting:
// config/laravel-auth0.php
return array(
// ...
'domain' => env( 'AUTH0_DOMAIN' ),
// ...
'client_id' => env( 'AUTH0_CLIENT_ID' ),
// ...
'client_secret' => env( 'AUTH0_CLIENT_SECRET' ),
// ...
);
Set Up Routes
The plugin works with the Laravel authentication system by creating a callback route to handle the authentication data from the Auth0 server.
First, we'll add our route and controller to routes/web.php
. The route used here must match the redirect_uri
configuration option set previously:
// routes/web.php
// ...
Route::get( '/auth0/callback', '\Auth0\Login\Auth0Controller@callback' )->name( 'auth0-callback' );
If you load this callback URL now, you should be immediately redirected back to the homepage rather than getting a 404 error. This tells us that the route is setup and being handled.
Now we need to add this URL to the Allowed Callback URLs field in the Application settings screen for the Application used with this app. Add the complete URL, like https://myapp.com/auth0/callback
.
Lastly, we need to set up how users log in and out of our app. This is handled by redirecting users to Auth0 for the former and clearing out session data for the latter. Let's start by creating a generic route handling controller. In the console:
php artisan make:controller Auth/Auth0IndexController
This creates a controller file to handle the routes used to log in and out. Let's create a function for each of these in the controller we just made:
// app/Http/Controllers/Auth/Auth0IndexController.php
// ...
class Auth0IndexController extends Controller
{
/**
* Redirect to the Auth0 hosted login page
*
* @return mixed
*/
public function login()
{
return \App::make('auth0')->login(null, null, ['scope' => 'openid email email_verified'], 'code');
}
/**
* Log out of Auth0
*
* @return mixed
*/
public function logout()
{
\Auth::logout();
return \Redirect::intended('/');
}
}
Now, add the routes tied to the correct handler method along with names we can use and auth
middleware where needed:
// routes/web.php
// ...
Route::get('/login', 'Auth\Auth0IndexController@login' )->name( 'login' );
Route::get('/logout', 'Auth\Auth0IndexController@logout' )->name( 'logout' )->middleware('auth');
Defining a User and a User Provider
The Laravel authentication system needs a User Object given by a User Provider. With these two abstractions, the user entity can have any structure you like and can be stored anywhere. You configure the User Provider indirectly, by selecting a user provider in app/config/auth.php
. The default provider is Eloquent, which persists the User model in a database using the ORM.
The plugin comes with an authentication driver called auth0
which defines a user structure that wraps the Normalized User Profile defined by Auth0. This driver does not actually persist the User, it just stores it in session for future calls. This works fine for basic testing or if you don't really need to persist the user. For persistence in the database, see the "Custom User Handling" section below.
At any point you can call Auth::check()
to see if there is a user logged in and Auth::user()
to get the wrapper with the user information.
Configure the User driver in config/auth.php
to use auth0
like this:
// config/auth.php
// ...
'providers' => [
'users' => [
'driver' => 'auth0'
],
],
Add Login and Logout Links
To test all this out, let's add links to our site to access these routes. If you're using the default project, open up resources/views/welcome.blade.php
and look for the @if (Route::has('login'))
block. Change that block to the following:
@if (Route::has('login'))
<div class="top-right links">
@auth
<a href="{{ route('logout') }}">Logout</a>
@else
<a href="{{ route('login') }}">Login/Signup</a>
@endauth
</div>
@endif
Load the homepage of your app and you should see a Login link at the top right. If you click on that, you should be redirected to an Auth0 login page for your application. If this does not happen then you should see one of two things:
- A Laravel debug screen (is
APP_DEBUG
is set totrue
) which, along with the steps above, should help diagnose the issues - An Auth0 error page with more information under the "Technical Details" heading (click See details for this error)
Once you're able to successfully log in, you will be redirected to your homepage with a Logout link at the top right. This tells us that the login process was successful and the Auth0 user provider is functioning properly. Click Logout and you should be back where you started.
At this point you should have a fully-functioning authentication process using Auth0!
Extra: Custom User Handling
What if you need to customize how users are handled beyond what Auth0 provides? You may want to, for example, store the user profile in a database. These kind of customizations can be done by extending the Auth0UserRepository
class with your own custom class.
Let's walk through how to add user database persistence using this a custom Repository class called CustomUserRepository
. You'll need to have a database configured and a users
table created. If you're starting with the default project, update your MySQL information in your .env
file:
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel
DB_USERNAME=db_user
DB_PASSWORD=db_password
In the database/migrations
folder for the default Laravel project there will be two files, one for creating a users table and one for creating a password reset table. We can delete the password reset one since that's handled by Auth0 but we want to change the users one to remove unnecessary columns and add a new one.
Edit the migration file with a name similar to create_users_table
and change the CreateUsersTable::up()
method to remove the password
field and add one for sub
(the Auth0 user ID):
// database/migrations/x_x_x_create_users_table.php
// ...
class CreateUsersTable extends Migration
{
// ...
public function up()
{
Schema::create('users', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->string('sub')->unique();
$table->string('email')->unique();
$table->rememberToken();
$table->timestamps();
});
}
// ...
}
Save this file and run the artisan command to create this table:
php artisan migrate
Next, make a CustomUserRepository.php
class we can modify:
# From the Laravel root
mkdir app/Repositories;
touch app/Repositories/CustomUserRepository.php
We're going to implement a method called upsertUser()
to retrieve or add users to the database. We'll also re-implement the getUserByDecodedJWT()
and getUserByUserInfo()
methods to use upsertUser()
when getting users:
// app/Repositories/CustomUserRepository.php
namespace App\Repositories;
use App\User;
use Auth0\Login\Auth0User;
use Auth0\Login\Auth0JWTUser;
use Auth0\Login\Repository\Auth0UserRepository;
class CustomUserRepository extends Auth0UserRepository
{
/**
* Get an existing user or create a new one
*
* @param array $profile - Auth0 profile
*
* @return User
*/
protected function upsertUser( $profile ) {
// See if we have a user that matches the Auth0 user_id
$user = User::where( 'sub', $profile['sub'] )->first();
// In not, add them to the database
if ( ! $user ) {
$user = new User();
// All are required, no default set
$user->setAttribute( 'email', $profile['email'] );
$user->setAttribute( 'sub', $profile['sub'] );
$user->setAttribute( 'name', isset( $profile['name'] ) ? $profile['name'] : '' );
$user->save();
}
return $user;
}
/**
* Authenticate a user with a decoded ID Token
*
* @param object $jwt
*
* @return Auth0JWTUser
*/
public function getUserByDecodedJWT( $jwt ) {
$user = $this->upsertUser( (array) $jwt );
return new Auth0JWTUser( $user->getAttributes() );
}
/**
* Get a User from the database using Auth0 profile information
*
* @param array $userinfo
*
* @return Auth0User
*/
public function getUserByUserInfo( $userinfo ) {
$user = $this->upsertUser( $userinfo['profile'] );
return new Auth0User( $user->getAttributes(), $userinfo['accessToken'] );
}
}
Finally, we'll change the binding in the AppServiceProvider
to point to this new custom Repository class:
// app/Providers/AppServiceProvider.php
// ...
use \Auth0\Login\Contract\Auth0UserRepository as Auth0Contract;
use \App\Repositories\CustomUserRepository as UserRepo;
class AppServiceProvider extends ServiceProvider
{
// ...
public function register()
{
$this->app->bind(
Auth0Contract::class,
UserRepo::class
);
}
}
Logging in for the first time should create a new entry in the database with the Auth0 sub
ID and email address used. Subsequent logins should simply access that same user and not create any new records.
Extra: More About the QuickStart Project
The Sample Project linked at the start of this guide implements everything here and more.
- A simple Profile screen that displays user data from Auth0
- Additional work with templates