How to authenticate your frontend apps with Laravel Sanctum

How to authenticate your frontend apps with Laravel Sanctum

Passport or Sanctum?

One popular question I have seen everywhere is people asking: "Should I use Passport or Sanctum to authenticate my API"? I have seen people answer this in various ways, but my answer is: Use Sanctum when you need to create a frontend application to interact with your API.

Sanctum provides authentication for your frontend application without the need for tokens unlike Passport, and that isn't to say Sanctum isn't capable of issuing tokens, you can also issue tokens with abilities (read: scope as used in Oauth) to users to authenticate all requests made to your API.

The two core functionalities Sanctum provides are:

  • Stateful authentication
  • API Tokens

I love to use Sanctum when building an API backend with Laravel that will interact with a frontend application as it's simple and straight-forward to use for that purpose.

Passport

Passport is a much more compact tool than Sanctum, with a lot of options for authenticating your users. This is more appropriate when building an application that offers Oauth2 capabilities, to enable your users to connect an external service with your application and also gives you the capability to grant each application scopes and abilities.

If you want to build an application that needs to issue Oauth2 tokens to external applications that may connect with a tool like Socialite, then Passport is definitely the right choice.

Installing sanctum in your Laravel application

The Laravel docs provide the information needed to set up Sanctum for your application, but I still got confused about setting it up and deciding what was right for my use case between the API tokens option and SPA Authentication option. So I have written this guide hoping it will help anyone else setting Sanctum for the first time.

Setting up:

API Tokens or SPA Authentication

Sanctum provides these two authentication methods to you by default and you do not have to use the two at once. I have put together this shortlist to determine which one is best to use:

SPA Authentication:

  • Stateful authentication without tokens.
  • CSRF protection

API Tokens:

  • Issue tokens to users
  • Authenticate users with Bearer Authorization
  • Attach abilities to tokens for access control

To use the SPA Authentication, your SPA has to be on the same domain as your Laravel API.

The stateful authentication method uses Laravel sessions to authenticate the user and thus eliminating the need for tokens, and I think that is super smart!

To authenticate with this method, note that you need to use the web middleware as the Laravel session is only available through this middleware.

You could write your own custom authentication or use any of Laravel starter kits to authenticate, I love to use Breeze and take out the views as this is an API only application.

Configuring your 3rd party domains

In order for Sanctum to maintain stateful authentication, you need to configure your application's domain names from config/sanctum.php:

/*
|--------------------------------------------------------------------------
| Stateful Domains
|--------------------------------------------------------------------------
|
| Requests from the following domains / hosts will receive stateful API
| authentication cookies. Typically, these should include your local
| and production domains which access your API via a frontend SPA.
|
*/

'stateful' => explode(',', env(
  'SANCTUM_STATEFUL_DOMAINS',
  'localhost,localhost:8000,localhost:8080,::1'
)),

This setting notifies Sanctum on which domains it should keep your users logged in using cookie based session authentication. We want our users to remain logged-in on our SPA and also our API.

While in development, you want to make sure you add the different port names for your Laravel application and your SPA.

Above we have added localhost:8000 which is same as 127.0.0.1:8000 for our Laravel application and we have also added localhost:8080 which the URL of our SPA which is also running locally. Of course, you can change this to whatever the URL of your SPA is. I am using a Vue CLI app and the default URL is localhost:8080

Note that you have to update these settings in our environment variables once our app is deployed.

You should clear your application cache with php artisan config:cache after changing your config files to make sure the new changes are working.

Add the Sanctum middleware

Follow the docs to add the Sanctum middleware.

CORS

If your SPA is on a different URL like we have in this tutorial, you will come across cross-site resource sharing errors (cors), but do not fret, I will show you how to overcome those. First you should make sure the API paths you will be accessing via the SPA are listed in the paths key of the config/cors.php file:

/*
|--------------------------------------------------------------------------
| Cross-Origin Resource Sharing (CORS) Configuration
|--------------------------------------------------------------------------
|
| Here you may configure your settings for cross-origin resource sharing
| or "CORS". This determines what cross-origin operations may execute
| in web browsers. You are free to adjust these settings as needed.
|
| To learn more: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
|
*/

'paths' => [
    'api/*', 
    'sanctum/csrf-cookie'
        //define other custom paths
],

Above, we have defined the paths we will be accessing on the API. The first is the /api/* path which will be handling all requests to our API routes in the routes/api.php file. We have also defined the sanctum/csrf-cookie path which will set the headers for our application to use in subsequent requests to the backend. More details on this can be found in the Authentication section.

TIP: You may configure the prefix for the routes defined in routes/api.php from App\Providers\RouteServiceProvider.php, or even set a new prefix for a new file path in the routes folder.

While testing, you should set your domains key in config/sessions.php to [localhost](http://localhost):

'domain' => 'localhost',

On the frontend, you should use a library like Axios with Vue or React which already helps you set your headers based on the response from the server during the initial authentication request as we will see below.

Further, you should set the withCredentials option of axios to true:

axios.defaults.withCredentials = true;

To enable Laravel transform XHTTP (or Ajax) responses to JSON instead of the regular redirect or view responses, you should set the Accept header to application/json when making requests.

In a Vue CLI app, this can be done from the main.js file:

import Vue from 'vue'
import App from './App.vue'
import axios from 'axios';

Vue.config.productionTip = false;
axios.defaults.headers.common['Accept'] = 'application/json';
axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
axios.defaults.withCredentials = true;

Authentication

The method of authentication Sanctum uses is similar to the regular authentication method in Laravel. The only difference is that you have to first make a post request to sanctum/csrf-token from your frontend application:

methods:{
    login(){
            //initial request to set Laravel authentication cookies
      axios.get('http://localhost:8000/sanctum/csrf-cookie').then(() => {
                //make a request to /login
        axios.post('http://localhost:8000/login', {
          email: "olaegbesamuel@gmail.com",
          password: "strong-password"
        }).then(res => {
                    //do anything with returned response
        })
      });
    },
}

Get authenticated user from a protected route

To protect a route with Sanctum, you need to apply the auth:sanctum middleware to the route definition:

//api.php
Route::middleware('auth:sanctum')->post('/me', function (Request $request) {
    return auth()->user();
});

If Sanctum was set up correctly you should get the authenticated user back:

{
"id":1,
"name":"Samuel Olaegbe",
"email":"olaegbesamuel@gmail.com",
"email_verified_at":null,
"created_at":"2020-12-12T23:19:04.000000Z",
"updated_at":"2020-12-12T23:19:04.000000Z"
}

Conclusion

While building a Laravel powered API, this is one of the problems we are faced with and I had to do a lot of research to get this working solution that has worked for me and thought to share it here. This post should be a guide on best practices on how to authenticate your first-party SPA with Sanctum and would be updated regularly.

Taylor promised some additional documentation on how to do this well:

Let me know if you have any questions or additions and I will be happy to discuss them!

PS: Subscribe to my blog so you could receive an email each time I publish content.