laravel logo
php logo

Laravel API Authorization By Example: Using Auth0 PHP SDK

Published on March 12, 2024
Options
Laravel SDK
PHP SDK
You are reading the Early Release version of this guide!
This is our initial take on what we believe is an idiomatic implementation of authentication in Laravel.
As you read this guide, please help us shape its final release with your feedback: what should we start, continue, or stop doing?

This Laravel guide will help you learn how to secure a Laravel API application using token-based authorization. You'll learn how to integrate Auth0 by Okta with Laravel to implement the following security features:

  • Use Laravel middleware to enforce API security policies.
  • Perform access control in Laravel using a token-based authorization strategy powered by JSON Web Tokens (JWTs).
  • Decode and validate access tokens in JSON Web Token (JWT) format using the Auth0 PHP SDK.
  • Make authenticated requests to a secure Laravel API server.
  • Create permissions, roles, and users in the Auth0 Dashboard.
  • Perform Role-Based Access Control (RBAC) in Laravel.

How Does Auth0 Work?

With the help of Auth0 by Okta, you don't need to be an expert on identity protocols, such as OAuth 2.0 or OpenID Connect, to understand how to secure your web application stack.

You first integrate your client applications with Auth0. Your application will then redirect users to an Auth0 customizable login page when they need to log in. Once your users log in successfully, Auth0 redirects them to your client app, returning an access token. The client application can then use that access token to access protected resources in your Laravel API server.

Project Requirements

Ensure that you have the following tools installed in your system:

  • PHP 8.2+
  • Composer v2.6+
  • Docker Desktop v4.20+ (Required only when using Laravel Sail environment)

Get the Starter Project

We have created a starter project to help you learn Laravel security concepts through hands-on practice. You can focus on writing the Laravel logic to secure your application.

Start by cloning the api_laravel_php_hello-world_php-sdk repository on its starter branch:

COMMAND
git clone -b starter [email protected]:auth0-developer-hub/api_laravel_php_hello-world_php-sdk.git

Once you clone the repo, make api_laravel_php_hello-world_php-sdk your current directory:

COMMAND
cd api_laravel_php_hello-world_php-sdk

Install the Laravel project dependencies as follows:

COMMAND
composer install

The starter Laravel project defines the following API endpoints to let client applications access a simple message resource:

ENDPOINTS
# get a public message
GET /api/messages/public
# get a protected message
GET /api/messages/protected
# get an admin message
GET /api/messages/admin

The starter project does not implement access control for these endpoints. After you follow the steps in this guide, the API server will require each request to have a valid access token on their authorization header to access /api/messages/protected and /api/messages/admin. /api/messages/public would be the only public endpoint. Later, you'll integrate this Laravel API with an actual client application using a frontend technology of your choice.

You'll run your Laravel server on port 6060 locally. The compatible demo client application runs on http://localhost:4040 by default. However, this Laravel starter application uses CORS to restrict which origins can request its resources.

You'll manage these configuration elements of your Laravel application using environment variables. As such, create a .env file under the root project directory:

COMMAND
touch .env

Populate your .env file with the following content:

.env
APP_PORT=6060
CLIENT_ORIGIN_URL=http://localhost:4040

The APP_PORT environment variable defines the port in which your Laravel API server will run (when using Laravel Sail): http://localhost:6060. The CLIENT_ORIGIN_URL variable defines the origin from which a browser can load resources from your Laravel API — other than your API's origin.

Finally, open another terminal tab and execute this command to run your Laravel application:

COMMAND
php artisan serve --port 6060

If you're using Laravel Sail, use the following command to run the Laravel API server:

COMMAND
./vendor/bin/sail up -d

You are ready to start implementing authorization in this Laravel project. First, you'll need to configure the Larave API server to connect successfully to Auth0. Afterward, you'll use the auth0-php package to validate bearer tokens from incoming API requests.

Configure Laravel with Auth0

Follow these steps to get started with the Auth0 Identity Platform quickly:

Sign up and create an Auth0 Application

A free account also offers you:

During the sign-up process, you create something called an Auth0 Tenant, representing the product or service to which you are adding authentication.

Once you sign in, Auth0 takes you to the Dashboard. Open the "APIs" section of the Auth0 Dashboard.

Then, click the "Create API" button. A modal opens with a form to provide a name for the API registration and define an API Identifier. Use the following values:

Name
Hello World API Server
Identifier
https://hello-world.example.com

Click the "Create" button to complete the process. Your Auth0 API registration page loads up.

View 'Register APIs' document for more details.

A page loads up, presenting all the details about your application register with Auth0.

Next, locate the "General Settings" section.

Store the "Identifier" value in the following field to set up your Laravel API server in the next section:

In the next step, you'll learn how to help Laravel and Auth0 communicate.

What's the relationship between Auth0 Tenants and Auth0 Applications?

Let's say that you have a photo-sharing app called "Laragram". You then would create an Auth0 tenant called laragram. From a customer perspective, Laragram is that customer's product or service.

Now, say that Laragram is available on three platforms: web as a single-page application and Android and iOS as a native mobile application. If each platform needs authentication, you need to create three Auth0 applications to provide the product with everything it needs to authenticate users through that platform.

Laragram users belong to the Auth0 Laragram tenant, which shares them across its Auth0 applications.

Install the PHP Auth0 SDK

You will install the PHP Auth0 SDK. With this SDK, you will be able to communicate with Auth0 and validate/decode the Auth0 JWT access token received in the API requests:

COMMAND
composer require auth0/auth0-php

Create a communication bridge between Laravel and Auth0

In this guide, you'll implement token-based authorization. That is, your Laravel API server will protect an endpoint by requiring that each request to that endpoint contains a valid access token.

The issuer of those access tokens will be an Auth0 authorization server. Your Laravel API server needs to validate the access token coming along with each request, i.e., verify it was issued by Auth0, it is not expired, your API is the intended receiver, among others things.

You'll need the Auth0 Domain and Auth0 Audience values to validate the access tokens.

When setting up APIs in the Auth0 Dashboard, you also refer to the API identifier as the Audience value, which you have already set up in the previous section.

Get the Auth0 domain

Now, follow these steps to get the Auth0 Domain value.

  • Open the Auth0 Domain Settings

  • Locate the bold text in the page description that follows this pattern: tenant-name.region.auth0.com. That's your Auth0 domain!

  • Paste the Auth0 domain value in the following field so that you can use it in the next section to set up your API server:

The region subdomain (au, us, or eu) is optional. Some Auth0 Domains don't have it.

Get an Auth0 access token

You'll also need a test access token to practice making secure calls to your API from a terminal application.

You can get a test access token from the Auth0 Dashboard by following these steps:

  • Head back to the Auth0 registration page of your Laravel API and click on the "Test" tab.
If this is the first time that you are setting up a testing application, click on the "Create & Authorize Test Application" button.
  • Locate the section called "Response" and click on the copy button in the top-right corner.

  • Paste the access token value in the following field so that you can use it in the next sections to test your API server:

When you enter a value in the input fields present on this page, any code snippet that uses such value updates to reflect it. Using the input fields makes it easy to copy and paste code as you follow along.

For security, these configuration values are stored in memory and only used locally. They are gone as soon as you refresh the page! As an extra precaution, you should use values from an Auth0 test application instead of a production one.

Define the Auth0 Environment Variables

Update the .env file under the project directory as follows to integrate the Auth0 Domain and Auth0 Audience values with your Laravel application:

.env
APP_PORT=6060
CLIENT_ORIGIN_URL=http://localhost:4040
AUTH0_AUDIENCE=AUTH0-AUDIENCE
AUTH0_DOMAIN=AUTH0-DOMAIN

Restart your development server for your Laravel project to become aware of these .env file changes:

COMMAND
php artisan serve --port 6060

Or if you're using Laravel Sail:

COMMAND
./vendor/bin/sail restart

Once you reach the "Request Laravel API Resources From a Client App" section of this guide, you'll learn how to use CLIENT_ORIGIN_URL along with an Auth0 Audience value to request protected resources from your Laravel API using a client application that is also protected by Auth0.

Set Up the Authentication Middleware

In this section, you will configure the Auth0 PHP SDK to use the Laravel authentication middleware. This will help us use Laravel authentication helpers as well as allow us to write less and simpler code.

First, let's create a new configuration file for Auth0. Create the file config/auth0.php and fill it with the following code:

config/auth0.php
<?php
return [
'domain' => env('AUTH0_DOMAIN'),
'audience' => [env('AUTH0_AUDIENCE')]
];

In the AuthServiceProvider class, you will configure and register the Auth0 service using the configuration from the newly created configuration file. You will register it as a singleton so you can resolve the same configured instance anywhere within the Laravel API app with ease. Update the app/Providers/AuthServiceProvider.php file with the following code:

app/Providers/AuthServiceProvider.php
<?php
namespace App\Providers;
use Auth0\SDK\Auth0;
use Auth0\SDK\Configuration\SdkConfiguration;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
class AuthServiceProvider extends ServiceProvider
{
/**
* The model to policy mappings for the application.
*
* @var array<class-string, class-string>
*/
protected $policies = [
//
];
public function register()
{
$this->app->singleton(Auth0::class, function () {
return new Auth0(new SdkConfiguration([...config('auth0'), 'strategy' => 'api']));
});
parent::register();
}
/**
* Register any authentication / authorization services.
*/
public function boot(): void
{
//
}
}

The Auth0 service is now configured and ready to be used within the app. Now, let's create a new authentication guard auth0 that utilizes the Auth0 service so that you can use it to authenticate users instead of the Laravel default guard.

First, you will update the authentication configuration file config/auth.php to add the new auth0 guard with the following code:

config/auth.php
<?php
return [
/*
|--------------------------------------------------------------------------
| Authentication Defaults
|--------------------------------------------------------------------------
|
| This option controls the default authentication "guard" and password
| reset options for your application. You may change these defaults
| as required, but they're a perfect start for most applications.
|
*/
'defaults' => [
'guard' => 'web',
'passwords' => 'users',
],
/*
|--------------------------------------------------------------------------
| Authentication Guards
|--------------------------------------------------------------------------
|
| Next, you may define every authentication guard for your application.
| Of course, a great default configuration has been defined for you
| here which uses session storage and the Eloquent user provider.
|
| All authentication drivers have a user provider. This defines how the
| users are actually retrieved out of your database or other storage
| mechanisms used by this application to persist your user's data.
|
| Supported: "session"
|
*/
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'auth0',
'provider' => 'users',
],
],
/*
|--------------------------------------------------------------------------
| User Providers
|--------------------------------------------------------------------------
|
| All authentication drivers have a user provider. This defines how the
| users are actually retrieved out of your database or other storage
| mechanisms used by this application to persist your user's data.
|
| If you have multiple user tables or models you may configure multiple
| sources which represent each model / table. These sources may then
| be assigned to any extra authentication guards you have defined.
|
| Supported: "database", "eloquent"
|
*/
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\Models\User::class,
],
// 'users' => [
// 'driver' => 'database',
// 'table' => 'users',
// ],
],
/*
|--------------------------------------------------------------------------
| Resetting Passwords
|--------------------------------------------------------------------------
|
| You may specify multiple password reset configurations if you have more
| than one user table or model in the application and you want to have
| separate password reset settings based on the specific user types.
|
| The expiry time is the number of minutes that each reset token will be
| considered valid. This security feature keeps tokens short-lived so
| they have less time to be guessed. You may change this as needed.
|
| The throttle setting is the number of seconds a user must wait before
| generating more password reset tokens. This prevents the user from
| quickly generating a very large amount of password reset tokens.
|
*/
'passwords' => [
'users' => [
'provider' => 'users',
'table' => 'password_reset_tokens',
'expire' => 60,
'throttle' => 60,
],
],
/*
|--------------------------------------------------------------------------
| Password Confirmation Timeout
|--------------------------------------------------------------------------
|
| Here you may define the amount of seconds before a password confirmation
| times out and the user is prompted to re-enter their password via the
| confirmation screen. By default, the timeout lasts for three hours.
|
*/
'password_timeout' => 10800,
];

Next, you will use Laravel closure request guards to add a resolver for the newly created auth0 guard. Update the app/Providers/AuthServiceProvider.php file with the following code:

app/Providers/AuthServiceProvider.php
<?php
namespace App\Providers;
use Auth0\SDK\Auth0;
use Auth0\SDK\Configuration\SdkConfiguration;
use Auth0\SDK\Exception\InvalidTokenException;
use Auth0\SDK\Token;
use Illuminate\Auth\GenericUser;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
class AuthServiceProvider extends ServiceProvider
{
/**
* The model to policy mappings for the application.
*
* @var array<class-string, class-string>
*/
protected $policies = [
//
];
public function register()
{
$this->app->singleton(Auth0::class, function () {
return new Auth0(new SdkConfiguration([...config('auth0'), 'strategy' => 'api']));
});
parent::register();
}
/**
* Register any authentication / authorization services.
*/
public function boot(): void
{
Auth::viaRequest('auth0', function (Request $request) {
/**@var Auth0 $auth0 */
$auth0 = app(Auth0::class);
if (!$request->headers->has('authorization')) return null;
try {
[$_, $token] = explode(" ", $request->headers->get('Authorization'));
$decoded = $auth0->decode($token,
config('auth0.audience'),
null,
null,
null,
null,
null,
Token::TYPE_ACCESS_TOKEN)->toArray();
} catch (InvalidTokenException $e) {
return null;
}
return new GenericUser([...$decoded, 'id' => $decoded['sub'] ?? null]);
});
}
}

In the above code for the authentication guard auth0 resolver, you do the following:

  • You resolve the Auth0 service instance and check to ensure there's an authorization header that has an access token.
  • You use the Auth0 PHP SDK's decode method using the AUTH0_AUDIENCE environment variable to ensure the provided access token is valid and retrieve the user information from it.
  • Lastly, you use the Laravel GenericUser class to create a new generic user object with the decoded user information and return it.
Note if you return null from the authentication guard closure, Laravel will consider this to be an unauthenticated request and return the appropriate authentication error response.

Protect Laravel API Endpoints

Now that you have the Auth0 service and the auth0 guard configured and working together, it's time to protect the endpoints. Let's update the routes file to make use of the new authentication guard middleware. Update the routes/api.php file with the following code:

routes/api.php
<?php
use App\Http\Controllers\MessagesController;
use Illuminate\Support\Facades\Route;
/*
|--------------------------------------------------------------------------
| API Routes
|--------------------------------------------------------------------------
|
| Here is where you can register API routes for your application. These
| routes are loaded by the RouteServiceProvider and all of them will
| be assigned to the "api" middleware group. Make something great!
|
*/
Route::group(['prefix' => 'messages'], function ($router) {
Route::get('public', [MessagesController::class, 'showPublicMessage']);
Route::group(['middleware' => 'auth:api'], function () {
Route::get('protected', [MessagesController::class, 'showProtectedMessage']);
Route::get('admin', [MessagesController::class, 'showAdminMessage']);
});
});

You're now using the auth middleware, which provides the API with the auth0 guard to ensure the client's requests include Auth0 access tokens. These endpoints should be protected now and require authentication to access them.

Handle Authentication Exception

Laravel's default behavior when there's an authentication exception is to redirect to a login page if the request is coming from the web. Since the app is a Laravel API, let's ensure it consistently returns the correct authentication error response when the authentication fails. Update the Authenticate middleware with the following code:

app/Http/Middleware/Authenticate.php
<?php
namespace App\Http\Middleware;
use App\Exceptions\ApiException;
use Illuminate\Auth\Middleware\Authenticate as Middleware;
use Illuminate\Http\Request;
class Authenticate extends Middleware
{
/**
* Get the path the user should be redirected to when they are not authenticated.
*/
protected function redirectTo(Request $request): ?string
{
if ($this->auth->guard()->guest())
throw new ApiException('Requires authentication', 401);
return null;
}
}

With this update, the app will now always return an appropriate authentication error response instead of trying to redirect the user to a login page.

Test Your Protected API Endpoints

Use an application like Postman or a terminal to test that your API server is working as prescribed.

As an anonymous user:

Request:

COMMAND
curl localhost:6060/api/messages/public

Response:

Status code: 200

{
"text": "This is a public message."
}

Request:

COMMAND
curl localhost:6060/api/messages/protected

Response:

Status code: 401

{
"message": "Requires authentication"
}

Request:

COMMAND
curl localhost:6060/api/messages/admin

Response:

Status code: 401

{
"message": "Requires authentication"
}

Request:

COMMAND
curl localhost:6060/api/messages/invalid

Response:

Status code: 404

{
"message": "Not Found"
}

For any other error, the response will be as follows:

Response:

Status code: 500

{
"message": "Internal Server Error"
}

As a user with a valid test access token:

In the "Configure Laravel with Auth0" section, you learned how to get a test access token. Use that access token in this section, please.

Make a secure request to your API server by including an access token in the authorization header:

Request:

COMMAND
curl --request GET \
--url http:/localhost:6060/api/messages/protected \
--header 'authorization: Bearer AUTH0-ACCESS-TOKEN'

Response:

Status code: 200

{
"text": "This is a protected message."
}

Request:

COMMAND
curl --request GET \
--url http:/localhost:6060/api/messages/admin \
--header 'authorization: Bearer AUTH0-ACCESS-TOKEN'

Response:

Status code: 200

{
"text": "This is an admin message."
}

Request:

COMMAND
curl --request GET \
--url http:/localhost:6060/api/messages/invalid \
--header 'authorization: Bearer AUTH0-ACCESS-TOKEN'

Response:

Status code: 404

{
"message": "Not Found"
}

Any other errors as needed if no other status codes apply:

Response:

Status code: 500

{
"message": "Internal server error"
}

When using an invalid test access token:

Request:

COMMAND
curl --request GET \
--url http:/localhost:6060/api/messages/protected \
--header 'authorization: Bearer invalidtoken1234567890'

Response:

Status code: 401

{
"message": "Requires authentication"
}

Request Laravel API Resources From a Client App

Let's simulate an essential feature of an API: serving data to client applications.

You can pair this API server with a client application that matches the technology stack that you use at work. Any "Hello World" client application can communicate with this "Hello World" API server sample.

You can simulate a secure full-stack application system in no time. Each client application sample gives you clear instructions to quickly get it up and running.

Once set up, you can test the client-server connection in the http://localhost:4040/protected or http://localhost:4040/admin pages of your client application.

Pick a Single-Page Application (SPA) code sample in your preferred frontend framework and language:

angular
typescript
Angular Standalone Components Code Sample:Basic Authentication
This code sample uses Angular Standalone Components with TypeScript to implement single-page application authentication using the Auth0 Angular SDK.
angular
typescript
Angular Code Sample:Basic Authentication
This code sample uses Angular with TypeScript to implement single-page application authentication using the Auth0 Angular SDK.
react
javascript
React Router 6 Code Sample:Basic Authentication
Code sample showing how to protect a simple React single-page application using React Router 6, Auth0, and JavaScript.
react
typescript
React Router 6/TypeScript Code Sample:Basic Authentication
Code sample showing how to protect a simple React single-page application using React Router 6, Auth0, and TypeScript.
react
javascript
React Code Sample:Basic Authentication
JavaScript code that implements user login, logout and sign-up features to secure a React Single-Page Application (SPA) using Auth0.
react
typescript
React/TypeScript Code Sample:Basic Authentication
Code sample of a simple React single-page application built TypeScript that implements authentication using Auth0.
svelte
javascript
Svelte Code Sample:Basic Authentication
JavaScript code that implements user login, logout and sign-up features to secure a Svelte Single-Page Application (SPA), using routing middleware.
vue
javascript
Vue.js Composition API Code Sample:Basic Authentication
This code sample uses Vue.js 3 with JavaScript and the Composition API to implement single-page application authentication using the Auth0 Vue SDK.
vue
javascript
Vue.js Options API Code Sample:Basic Authentication
This code sample uses Vue.js 3 with JavaScript and the Options API to implement single-page application authentication using the Auth0 Vue SDK.
vue
javascript
Vue.js 2 Code Sample:Basic Authentication
This code sample uses Vue.js 2 with JavaScript to implement single-page application authentication using the Auth0 SPA SDK.

That's all it takes to integrate a client application with a Laravel API server that is also secured by Auth0 and to use an access token to consume protected server resources from the client application.

Set Up Role-Based Access Control (RBAC)

Within the context of Auth0, Role-based access control (RBAC) systems assign permissions to users based on their role within an organization. Everyone who holds that role has the same set of access rights. Those who hold different roles have different access rights.

Developers who use Role-based access control (RBAC) for access management can mitigate the errors that come from assigning permissions to users individually.

You can use the Auth0 Dashboard to enable Role-Based Access Control (RBAC) in any API that you have already registered with Auth0. You then implement RBAC by creating API permissions, assigning those permissions to a role, and assigning that role to any of your users.

Whenever a user logs in to one of your client applications, the Auth0 authorization server issues an access token that the client can use to make authenticated requests to an API server. Auth0 authorization servers issue access tokens in JSON Web Token (JWT) format.

When you enable Auth0 Role-Based Access Control (RBAC) for an API, the access token will include a permissions claim that has all the permissions associated with any roles that you have assigned to that user.

For this particular API code sample, the access token present in the authorization header of a request must include a permissions claim that contains the read:admin-messages permission to access the GET /api/messages/admin endpoint.

Enable Role-Based Access Control (RBAC)

  • Open the APIs section of the Auth0 Dashboard and select your "Hello World API Server" registration.

  • Click on the "Settings" tab and locate the "RBAC Settings" section.

  • Switch on the "Enable RBAC" and "Add Permissions in the Access Token" options.

Visit the "Role-Based Access Control" document for more details.

Create an API permission

In the same Auth0 API registration page, follow these steps:

  • Click on the "Permissions" tab and fill a field from the "Add a Permission (Scope)" section with the following information:
Permission (Scope)
read:admin-messages
Description
Read admin messages
  • Click the "+ Add" button to store the permission.
Visit the "Add API Permissions" document for more details.

Create a role with permissions

Create a role

  • Open the User Management > Roles section of the Auth0 Dashboard.

  • Click on the Create role button and fill out the "New Role" form with the following values:

Name
messages-admin
Description
Access admin messaging features
  • Click on the Create button.
Visit the "Create Roles" document for more details.

Add permissions to the role

  • Click on the "Permissions" tab of the roles page.

  • Click on the "Add Permissions" button.

  • Select the "Hello World API Server" from the dropdown menu that comes up and click the "Add Permissions" button.

  • Select all the permissions available by clicking on them one by one or by using the "All" link.

  • Finally, click on the "Add Permissions" button to finish up.

Visit the "Add Permissions to Roles" document for more details.

Create an admin user

  • Open the User Management > Users section from the Auth0 Dashboard.

  • Click on the "Create user" button and fill out the form with the required information. Alternatively, you can also click on any of your existing users to give one of them the admin role.

  • On the user's page, click on the "Roles" tab and then click on the "Assign Roles" button.

  • Select the messages-admin role from the dropdown menu and click on the "Assign" button.

Visit the "Assign Roles to Users" document for more details.

Implement RBAC in a Laravel API

Now that you have created the permission read:admin-messages in the Auth0 API application, let's use it in the Laravel API to protect the /api/messages/admin endpoint. You will only show the admin message to users who have this permission.

First create a Laravel Policy for the Message model using the following command:

Policy
php artisan make:policy MessagePolicy

Now fill the newly created policy file app/Policies/MessagePolicy.php with the following code:

app/Policies/MessagePolicy.php
<?php
namespace App\Policies;
class MessagePolicy
{
protected array $permissions;
public function __construct()
{
$this->permissions = auth()->guard('api')->user()->permissions ?? [];
}
public function readAdminMessages(): bool
{
return in_array('read:admin-messages', $this->permissions);
}
}

You created one permission method in the policy read AdminMessages to check if the user has the permission read:admin-messages you previously set up in the Auth0 dashboard.

Next you will check if the authenticated user has the required permission read:admin-messages before they can access the admin message. You will use the Laravel gate method can, which works with the policy to check for the permission. Update the routes/api.php file with the following code:

routes/api.php
<?php
use App\Http\Controllers\MessagesController;
use App\Models\Message;
use Illuminate\Support\Facades\Route;
/*
|--------------------------------------------------------------------------
| API Routes
|--------------------------------------------------------------------------
|
| Here is where you can register API routes for your application. These
| routes are loaded by the RouteServiceProvider and all of them will
| be assigned to the "api" middleware group. Make something great!
|
*/
Route::group(['prefix' => 'messages'], function ($router) {
Route::get('public', [MessagesController::class, 'showPublicMessage']);
Route::group(['middleware' => 'auth:api'], function () {
Route::get('protected', [MessagesController::class, 'showProtectedMessage']);
Route::get('admin', [MessagesController::class, 'showAdminMessage'])
->can('read-admin-messages', Message::class);
});
});

Finally, let's add an exception handler method for when the user has insufficient permissions to access the admin message resource. Update the app/Exceptions/Handler.php file with the following code:

app/Exceptions/Handler.php
<?php
namespace App\Exceptions;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Illuminate\Http\JsonResponse;
use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Throwable;
class Handler extends ExceptionHandler
{
/**
* The list of the inputs that are never flashed to the session on validation exceptions.
*
* @var array<int, string>
*/
protected $dontFlash = [
'current_password',
'password',
'password_confirmation',
];
/**
* Register the exception handling callbacks for the application.
*/
public function register(): void
{
$this->renderable(function (NotFoundHttpException $e) {
return $this->apiError('Not Found', 404);
});
$this->renderable(function (MethodNotAllowedHttpException $e) {
return $this->apiError('Not Allowed Http Method', 405);
});
$this->renderable(function (AccessDeniedHttpException $e) {
return $this->apiError('Insufficient permissions to access resource', 403);
});
$this->renderable(function (ApiException $e) {
return $this->apiError($e->getMessage(), $e->getCode());
});
$this->reportable(function (Throwable $e) {
return $this->apiError('Internal Server Error', 500);
});
}
protected function apiError($message, $code): JsonResponse
{
return response()->json(['message' => $message], $code);
}
}

Access the Admin Endpoint

Let's test access to the GET /api/messages/admin endpoint by simulating a real user login and requesting that protected resource using a real access token.

You can pair this API server with a client application that matches the technology stack that you use at work. Any "Hello World" client application can communicate with this "Hello World" API server sample.

When you log in to a "Hello World" client application as a user who has the messages-admin role, your access token will have the required permissions to access the GET /api/messages/admin endpoint.

You can simulate a secure full-stack application system in no time. Each client application sample gives you clear instructions to get it up and running quickly.

Pick a Single-Page Application (SPA) code sample in your preferred frontend framework and language:

angular
typescript
Angular Standalone Components Code Sample:Basic Authentication
This code sample uses Angular Standalone Components with TypeScript to implement single-page application authentication using the Auth0 Angular SDK.
angular
typescript
Angular Code Sample:Basic Authentication
This code sample uses Angular with TypeScript to implement single-page application authentication using the Auth0 Angular SDK.
react
javascript
React Router 6 Code Sample:Basic Authentication
Code sample showing how to protect a simple React single-page application using React Router 6, Auth0, and JavaScript.
react
typescript
React Router 6/TypeScript Code Sample:Basic Authentication
Code sample showing how to protect a simple React single-page application using React Router 6, Auth0, and TypeScript.
react
javascript
React Code Sample:Basic Authentication
JavaScript code that implements user login, logout and sign-up features to secure a React Single-Page Application (SPA) using Auth0.
react
typescript
React/TypeScript Code Sample:Basic Authentication
Code sample of a simple React single-page application built TypeScript that implements authentication using Auth0.
svelte
javascript
Svelte Code Sample:Basic Authentication
JavaScript code that implements user login, logout and sign-up features to secure a Svelte Single-Page Application (SPA), using routing middleware.
vue
javascript
Vue.js Composition API Code Sample:Basic Authentication
This code sample uses Vue.js 3 with JavaScript and the Composition API to implement single-page application authentication using the Auth0 Vue SDK.
vue
javascript
Vue.js Options API Code Sample:Basic Authentication
This code sample uses Vue.js 3 with JavaScript and the Options API to implement single-page application authentication using the Auth0 Vue SDK.
vue
javascript
Vue.js 2 Code Sample:Basic Authentication
This code sample uses Vue.js 2 with JavaScript to implement single-page application authentication using the Auth0 SPA SDK.

Once you set up the client application, log in and visit the guarded "Admin" page (http://localhost:4040/admin).

When you log in to a "Hello World" client application as a user who has the messages-admin role, your access token will have the required permissions to access the GET /api/messages/admin endpoint, and you'll get the following response:

{
"text": "This is an admin message."
}

However, when you log in as a user who doesn't have the messages-admin role, you'll get a 403 Forbidden status code and the following response:

{
"message": "Permission denied"
}

Next Steps

You have implemented token-based authorization in Laravel to restrict access to server resources.

This guide covered the most common authorization use case for a Laravel API: use middleware to validate access tokens and make authenticated requests to your API. However, Auth0 is an extensible and flexible identity platform that can help you achieve even more. If you have a more complex use case, check out the Auth0 Architecture Scenarios to learn more about the typical architecture scenarios you have identified when working with customers on implementing Auth0.

We'll cover advanced authorization strategies in future guides, such as implementing Fine-Grained Authorization (FGA).