Laravel API Authorization

Sample Project

Download a sample project specific to this tutorial configured with your Auth0 API Keys.

System Requirements
  • Composer 1.0-dev
  • PHP 5.6.18
  • Laravel 5.3
  • laravel-auth0 5.0
Show requirements

To restrict access to the resources served by your API, a check needs to be made to determine whether the incoming request contains valid authorization information. There are various methods for including authorization information in a request, but for integration with Auth0, your API needs to check for a valid JSON Web Token (JWT). When users log into your application, they will receive an id_token and an access_token which are both JWTs. The specific JWT that needs to be sent to your API is the access_token.

Install the Dependencies

Protecting your Laravel API requires a middleware which will check for and verify an access_token in the Authorization header of an incoming HTTP request. You can use the middleware provided in the laravel-auth0 package.

Install laravel-auth0 using Composer.

Composer is a tool for dependency management in PHP. It allows you to declare the dependent libraries your project needs and it will install them in your project for you. See Composer's getting started doc for information on how to use it.

composer require auth0/login:"~5.0"

Enable the Provider

The laravel-auth0 package comes with a provder called LoginServiceProvider. Add this to the list of application providers.

'providers' => array(
    // ...
    \Auth0\Login\LoginServiceProvider::class,
);

If you would like to use the Auth0 facade, add it to the list of aliases.

// config/app.php

'aliases' => array(
    // ...
    'Auth0' => \Auth0\Login\Facade\Auth0::class,
);

You will now be able to access user info with Auth0::getUser().

Finally, you need to bind a class that provides a user (your app model user) each time the user is logged in or an access_token is decoded. You can use the Auth0UserRepository provided by this package or you can build your own class.

To use Auth0UserRepository, add the following lines to your AppServiceProvider:

// app/Providers/AppServiceProvider.php

// ...
public function register()
{

    $this->app->bind(
        '\Auth0\Login\Contract\Auth0UserRepository',
        '\Auth0\Login\Repository\Auth0UserRepository');

}

Configure the Plugin

The laravel-auth0 plugin comes with a configuration file that can be generated using artisan. Generate the file and complete the details found within.

php artisan vendor:publish

After the file is generated, it will be located at config/laravel-auth0.php.

Configure Apache

By default, Apache doesn't parse Authorization headers from incoming HTTP requests. To enable this, add a mod_rewrite to your .htaccess file.

RewriteCond %{HTTP:Authorization} ^(.*)
RewriteRule .* - [e=HTTP_AUTHORIZATION:%1]

Define a User and 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 laravel-auth0 plugin comes with an authentication driver called auth0. This driver defines a user structure that wraps the normalized user profile defined by Auth0. It doesn't actually persist the object but rather simply stores it in the session for future calls.

This is adequate for basic testing or if you don't have a requirement to persist the user. At any point you can call Auth::check() to determine if there is a user logged in and Auth::user() to retreive the wrapper with the user information.

Configure the driver in /config/auth.php to use auth0.

// app/config/auth.php

// ...
'providers' => [
    'users' => [
        'driver' => 'auth0'
    ],
],

Protect Routes with the Auth0 Middleware

Protecting individual API endpoints can be done by applying the auth0.jwt middleware to them.

Add the middleware to the $routeMiddleware array in app/Http/Kernel.php.

// app/Http/Kernel.php

// ...
protected $routeMiddleware = [
    // ...
    'auth0.jwt' => \Auth0\Login\Middleware\Auth0JWTMiddleware::class,
];

This middleware can now be applied to individual routes.

// routes/api.php

Route::get('/private', function (Request $request) {
    return response()->json(["message" => "Hello from a private endpoint! You need to have a valid access token to see this."]);
})->middleware('auth0.jwt');

This route is now only accessible if an access_token is included in the Authorization header of the incoming request.

Make Calls to the API

With the laravel-auth0 plugin configured and the auth0.jwt middleware applied to a route, you can now run the application and make calls to it.

Use artisan to serve the application.

php artisan serve

Send a request to your protected endpoint which includes an access_token.


curl --request GET \
  --url http://localhost:8000/api/private \
  --header 'authorization: Bearer YOUR_ACCESS_TOKEN_HERE'
var client = new RestClient("http://localhost:8000/api/private");
var request = new RestRequest(Method.GET);
request.AddHeader("authorization", "Bearer YOUR_ACCESS_TOKEN_HERE");
IRestResponse response = client.Execute(request);
package main

import (
	"fmt"
	"net/http"
	"io/ioutil"
)

func main() {

	url := "http://localhost:8000/api/private"

	req, _ := http.NewRequest("GET", url, nil)

	req.Header.Add("authorization", "Bearer YOUR_ACCESS_TOKEN_HERE")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := ioutil.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
HttpResponse<String> response = Unirest.get("http://localhost:8000/api/private")
  .header("authorization", "Bearer YOUR_ACCESS_TOKEN_HERE")
  .asString();
var settings = {
  "async": true,
  "crossDomain": true,
  "url": "http://localhost:8000/api/private",
  "method": "GET",
  "headers": {
    "authorization": "Bearer YOUR_ACCESS_TOKEN_HERE"
  }
}

$.ajax(settings).done(function (response) {
  console.log(response);
});
var request = require("request");

var options = { method: 'GET',
  url: 'http://localhost:8000/api/private',
  headers: { authorization: 'Bearer YOUR_ACCESS_TOKEN_HERE' } };

request(options, function (error, response, body) {
  if (error) throw new Error(error);

  console.log(body);
});
#import <Foundation/Foundation.h>

NSDictionary *headers = @{ @"authorization": @"Bearer YOUR_ACCESS_TOKEN_HERE" };

NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://localhost:8000/api/private"]
                                                       cachePolicy:NSURLRequestUseProtocolCachePolicy
                                                   timeoutInterval:10.0];
[request setHTTPMethod:@"GET"];
[request setAllHTTPHeaderFields:headers];

NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request
                                            completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
                                                if (error) {
                                                    NSLog(@"%@", error);
                                                } else {
                                                    NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) response;
                                                    NSLog(@"%@", httpResponse);
                                                }
                                            }];
[dataTask resume];
$curl = curl_init();

curl_setopt_array($curl, array(
  CURLOPT_PORT => "8000",
  CURLOPT_URL => "http://localhost:8000/api/private",
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_ENCODING => "",
  CURLOPT_MAXREDIRS => 10,
  CURLOPT_TIMEOUT => 30,
  CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
  CURLOPT_CUSTOMREQUEST => "GET",
  CURLOPT_HTTPHEADER => array(
    "authorization: Bearer YOUR_ACCESS_TOKEN_HERE"
  ),
));

$response = curl_exec($curl);
$err = curl_error($curl);

curl_close($curl);

if ($err) {
  echo "cURL Error #:" . $err;
} else {
  echo $response;
}
import http.client

conn = http.client.HTTPConnection("localhost:8000")

headers = { 'authorization': "Bearer YOUR_ACCESS_TOKEN_HERE" }

conn.request("GET", "/api/private", headers=headers)

res = conn.getresponse()
data = res.read()

print(data.decode("utf-8"))
require 'uri'
require 'net/http'

url = URI("http://localhost:8000/api/private")

http = Net::HTTP.new(url.host, url.port)

request = Net::HTTP::Get.new(url)
request["authorization"] = 'Bearer YOUR_ACCESS_TOKEN_HERE'

response = http.request(request)
puts response.read_body
import Foundation

let headers = ["authorization": "Bearer YOUR_ACCESS_TOKEN_HERE"]

var request = NSMutableURLRequest(URL: NSURL(string: "http://localhost:8000/api/private")!,
                                        cachePolicy: .UseProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.HTTPMethod = "GET"
request.allHTTPHeaderFields = headers

let session = NSURLSession.sharedSession()
let dataTask = session.dataTaskWithRequest(request, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    println(error)
  } else {
    let httpResponse = response as? NSHTTPURLResponse
    println(httpResponse)
  }
})

dataTask.resume()

Extend the Auth0UserRepository Class

There may be situations where you need to customize the Auth0UserRepository class. For example, you may want to use the default User model and store the user profile in your database. If you need a more advanced custom solution such as this, you can extend the Auth0UserRepository class with your own custom class.

namespace App\Repository;

use Auth0\Login\Contract\Auth0UserRepository;

class MyCustomUserRepository implements Auth0UserRepository {

    /* This class is used on api authN to fetch the user based on the jwt.*/
    public function getUserByDecodedJWT($jwt) {
      /*
       * The `sub` claim in the token represents the subject of the token
       * and it is always the `user_id`
       */
      $jwt->user_id = $jwt->sub;

      return $this->upsertUser($jwt);
    }

    public function getUserByUserInfo($userInfo) {
      return $this->upsertUser($userInfo['profile']);
    }

    protected function upsertUser($profile) {

      $user = User::where("auth0id", $profile->user_id)->first();

      if ($user === null) {
          // If not, create one
          $user = new User();
          $user->email = $profile->email; // you should ask for the email scope
          $user->auth0id = $profile->user_id;
          $user->name = $profile->name; // you should ask for the name scope
          $user->save();
      }

      return $user;
    }

    public function getUserByIdentifier($identifier) {
        //Get the user info of the user logged in (probably in session)
        $user = \App::make('auth0')->getUser();

        if ($user === null) return null;

        // build the user
        $user = $this->getUserByUserInfo($user);

        // it is not the same user as logged in, it is not valid
        if ($user && $user->auth0id == $identifier) {
            return $auth0User;
        }
    }

}

With your custom class in place, change the binding in the AppServiceProvider.

// app/Providers/AppServiceProvider.php

// ...
public function register()
{

    $this->app->bind(
        '\Auth0\Login\Contract\Auth0UserRepository',
        '\App\Repository\MyCustomUserRepository');

}

Optional Steps

Configure CORS

To configure CORS, you should add the laravel-cors dependency. You can check it out here.

After installation, add the following to the configuration file for CORS:

'defaults' => array(
    'supportsCredentials' => false,
    'allowedOrigins' => array(),
    'allowedHeaders' => array(),
    'allowedMethods' => array(),
    'exposedHeaders' => array(),
    'maxAge' => 0,
    'hosts' => array(),
),

'paths' => array(
    '*' => array(
        'allowedOrigins' => array('*'),
        'allowedHeaders' => array('Content-Type', 'Authorization', 'Accept'),
        'allowedMethods' => array('POST', 'PUT', 'GET', 'DELETE')
    ),
),

Authenticate your Requests Without Laravel Passport

If you don't want to use Laravel Passport, you can use the middlewares provided by this package.

To register the middlewares, go to app/Http/Kernel.php and add these lines to the routeMiddleware collection:

protected $routeMiddleware = [
  ...
  'auth0.jwt' => '\Auth0\Login\Middleware\Auth0JWTMiddleware',
  'auth0.jwt_verification' => '\Auth0\Login\Middleware\Auth0OptionalJWTMiddleware',
  'auth0.jwt_force' => '\Auth0\Login\Middleware\ForceAuthMiddleware',
  ...
];

Auth0JWTMiddleware

This middleware will extract, decode, and verify the access_token from the Authorization header. If the token is not present, it will reject the login with a 401 Unauthorized response.

Auth0OptionalJWTMiddleware & ForceAuthMiddleware

Auth0OptionalJWTMiddleware with extract, decode, and verify the access_token from the Authorization header. If the token is not present it will not set the user.

ForceAuthMiddleware will check if there is a user set up. If there is not, it will reject the login with a 401 Unauthorized response.

Previous Tutorial
1. Getting Started
Use Auth0 for FREECreate free Account