passport: Consuming JavaScript via CreateFreshApiToken not working

Hi, I’ve been debugging this issue for a little over a week. I’ve asked on StackOverflow and the Laravel Discord. Multiple people have offered ideas and suggestions and unfortunately nothing has come forward as a working solution.

This is last resort in hopes for a solution. A few others have created an issue – with the only solution being “rebuild the app on 5.7”. (#906, #884)

I’m attempting to install Passport (^7.0) on Laravel 5.7.18 using PHP 7.2.13. This is an upgraded Laravel project from 5.6.

The application consumes the API within itself with JS. (Axios with Vue)

I’m getting a 401 Unauthorized error within the web application console. I’ve read the documentation and added CreateFreshApiToken to the web Kernel. The laravel_token cookie is in fact setting itself.

I’ve gone through tinker and added an access token for my user account. From there, I booted up Postman and I successfully ran the query with the access token in the header.

When I login my browser, the laravel_token sets itself but when it makes an API request I get a 401 Unauthorized error.

Http/Kernel:

    protected $middlewareGroups = [
            'web' => [
                \App\Http\Middleware\EncryptCookies::class,
                \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
                \Illuminate\Session\Middleware\StartSession::class,
                // \Illuminate\Session\Middleware\AuthenticateSession::class,
                \Illuminate\View\Middleware\ShareErrorsFromSession::class,
                \App\Http\Middleware\VerifyCsrfToken::class,
                \Illuminate\Routing\Middleware\SubstituteBindings::class,
                \Laravel\Passport\Http\Middleware\CreateFreshApiToken::class,
            ],
    
            'api' => [
                'throttle:60,1',
                'bindings',
            ],
        ];

JavaScript:

    axios.get("api/users/" + id).then(({ data }) => {
        this.user = data;
    });

Auth.php:

    'guards' => [
            'web' => [
                'driver'   => 'session',
                'provider' => 'users',
            ],
    
            'api' => [
                'driver'   => 'passport',
                'provider' => 'users',
            ],
        ],

Routes (Api.php):

    Route::middleware('auth:api')->group(function () {
        Route::resource('users', 'UserController');
        Route::resource('groups', 'GroupController');
        // .. plus more resources
    });

Axios Config:

    window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
      
    var token = document.head.querySelector('meta[name="csrf-token"]');
    
    if (token) {
      window.axios.defaults.headers.common['X-CSRF-TOKEN'] = token.content;
    } else {
      console.error('CSRF token not found: https://laravel.com/docs/csrf#csrf-x-csrf-token');
    }

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Reactions: 12
  • Comments: 36 (11 by maintainers)

Most upvoted comments

fixed mine by running this after setup

php artisan optimize:clear

reference: https://github.com/laravel/passport/issues/839#issuecomment-452889461

Wow… I got so fed up with trying to figure out why it wouldn’t work that I setup a new build myself and noticed everything was working… I ran a diff between the two projects and found the issue.

One of our devs changed the middleware on RouteServiceProvider.php so that api calls used the web middleware by default… this was driving me nuts. I shall have a long talk with him on Monday.

I am working on a Laravel 5.5 project that consumes my own API and it was working normally. After upgrade my PHP to 7.3, I was forced to run composer update and passport just stopped working (always returning 401).

In order to make things work again, I changed EncryptCookies middleware:

<?php

namespace App\Http\Middleware;

use Illuminate\Cookie\Middleware\EncryptCookies as Middleware;

class EncryptCookies extends Middleware
{
    /**
     * The names of the cookies that should not be encrypted.
     *
     * @var array
     */
    protected static $serialize = true;

    /**
     * The names of the cookies that should not be encrypted.
     *
     * @var array
     */
    protected $except = [
        //
    ];
}

Source: https://laravel.com/docs/5.6/upgrade#upgrade-5.6.30

Another trick that makes my Passport works normally here was to upgrade Laravel to 5.7.

Interestingly, on my sandbox project where I’m trying out WithFreshAPIToken, switching over from jQuery ajax to axios fixed the issue.

I am using the CDN version of axios:

<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.15/lodash.min.js"></script>

… and didn’t set any additional headers/configs.

On DevTools, I checked out the difference between request headers generated by axios and jQuery ajax.

On axios (where I’m getting a 200):

Host: l.test
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:75.0) Gecko/20100101 Firefox/75.0
Accept: application/json, text/plain, */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
X-XSRF-TOKEN: eyJpdiI6IkNnUW9nRFBWdC8veUErRHREWVY1SVE9PSIsInZhbHVlIjoiTE91OXRqWFAwVGVxT2c2dTVhaGl6aFFnQ1ZHOHhZZkt3UXhBbDZITklSVlZ6SUVuZ2lIYmhsQWJMeEJVZ2EvcyIsIm1hYyI6ImI5MDU3NWNhY2E2NGI5YzIzZDcxNDk5OGJhM2I2ZGU3YzA2N2JiM2FiN2UzMDBlM2IyMzI2YTA2OTY5NDE2ZTYifQ==
DNT: 1
Connection: keep-alive
Referer: http://l.test/consumer/hobbies
Cookie: laravel_token=eyJpdiI6ImZyVEF1c2NGRGEySUQ1SGEwR3Zlemc9PSIsInZhbHVlIjoibjhFdGlRdTJsM0RjNlZEdXlKOWV5WnorU3NtNzJXakliVExMaHlkd3piWUhCTjZjUHViOTZCUnhCYXRFU2NldUZBWVpnalhMdFFNUkRFRUkxYjh2LzVwL2JNQkNhQi9ycXhaR005TjQ2TkNKZTZXekFJeVQvRTgxcHRNMm14NVdzcVF1RTFxZEpSeUNXTXZYVzJqbWpHWEJzMUVUL3BWbnpzcWdjdUFPOUI3cDB0b3piNStTSHdDQytSaTNJWmYwMGNsdWtNSXo3TmJvdCtjQ3NmYlZDSmVYSlV3RWhFbFNZSktGb1p3RUdUWHhjdTFOU1phVkI3eXN6NzlUZWd1OCIsIm1hYyI6IjczNTMxMWQ4Y2RmNmUzMmJhOGQ5MmY1Yjk3OGQ1YTg0YWM0ZmNlODBjODg0ODI0NjgwNjM3NTcwNDZlNzQ3MGUifQ%3D%3D; XSRF-TOKEN=eyJpdiI6IkNnUW9nRFBWdC8veUErRHREWVY1SVE9PSIsInZhbHVlIjoiTE91OXRqWFAwVGVxT2c2dTVhaGl6aFFnQ1ZHOHhZZkt3UXhBbDZITklSVlZ6SUVuZ2lIYmhsQWJMeEJVZ2EvcyIsIm1hYyI6ImI5MDU3NWNhY2E2NGI5YzIzZDcxNDk5OGJhM2I2ZGU3YzA2N2JiM2FiN2UzMDBlM2IyMzI2YTA2OTY5NDE2ZTYifQ%3D%3D; laravel_session=eyJpdiI6IkpPSVNlZHZZYU1YWlJRSFBvVDZtT0E9PSIsInZhbHVlIjoieWNEYmFxRXN2NUd6bzRVamJrcTNINlYvdTk0aVdPcU1aZzhhNmlqdTQ5WmV4N1dlVlJGckJweEp1NUMrN2NuSSIsIm1hYyI6Ijc1NWQyN2QyMGJlNzAxNTk5YmJmODY2OGRjN2EwNWM1OTM2ZjY5Yjc0YzhjMTUyYWU0ZjNiODk0MDdlYTc5YjgifQ%3D%3D
Pragma: no-cache
Cache-Control: no-cache

… and on jQuery ajax (where I’m getting a 401):

Host: l.test
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:75.0) Gecko/20100101 Firefox/75.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
X-Requested-With: XMLHttpRequest
DNT: 1
Connection: keep-alive
Referer: http://l.test/consumer/hobbies
Cookie: laravel_token=eyJpdiI6ImZyVEF1c2NGRGEySUQ1SGEwR3Zlemc9PSIsInZhbHVlIjoibjhFdGlRdTJsM0RjNlZEdXlKOWV5WnorU3NtNzJXakliVExMaHlkd3piWUhCTjZjUHViOTZCUnhCYXRFU2NldUZBWVpnalhMdFFNUkRFRUkxYjh2LzVwL2JNQkNhQi9ycXhaR005TjQ2TkNKZTZXekFJeVQvRTgxcHRNMm14NVdzcVF1RTFxZEpSeUNXTXZYVzJqbWpHWEJzMUVUL3BWbnpzcWdjdUFPOUI3cDB0b3piNStTSHdDQytSaTNJWmYwMGNsdWtNSXo3TmJvdCtjQ3NmYlZDSmVYSlV3RWhFbFNZSktGb1p3RUdUWHhjdTFOU1phVkI3eXN6NzlUZWd1OCIsIm1hYyI6IjczNTMxMWQ4Y2RmNmUzMmJhOGQ5MmY1Yjk3OGQ1YTg0YWM0ZmNlODBjODg0ODI0NjgwNjM3NTcwNDZlNzQ3MGUifQ%3D%3D; XSRF-TOKEN=eyJpdiI6IkNnUW9nRFBWdC8veUErRHREWVY1SVE9PSIsInZhbHVlIjoiTE91OXRqWFAwVGVxT2c2dTVhaGl6aFFnQ1ZHOHhZZkt3UXhBbDZITklSVlZ6SUVuZ2lIYmhsQWJMeEJVZ2EvcyIsIm1hYyI6ImI5MDU3NWNhY2E2NGI5YzIzZDcxNDk5OGJhM2I2ZGU3YzA2N2JiM2FiN2UzMDBlM2IyMzI2YTA2OTY5NDE2ZTYifQ%3D%3D; laravel_session=eyJpdiI6IkpPSVNlZHZZYU1YWlJRSFBvVDZtT0E9PSIsInZhbHVlIjoieWNEYmFxRXN2NUd6bzRVamJrcTNINlYvdTk0aVdPcU1aZzhhNmlqdTQ5WmV4N1dlVlJGckJweEp1NUMrN2NuSSIsIm1hYyI6Ijc1NWQyN2QyMGJlNzAxNTk5YmJmODY2OGRjN2EwNWM1OTM2ZjY5Yjc0YzhjMTUyYWU0ZjNiODk0MDdlYTc5YjgifQ%3D%3D
Pragma: no-cache
Cache-Control: no-cache

You can see that the key differences are:

  • axios, by default, sends an Accepts header with the value application/json, text/plain, */*

  • jQuery ajax, by default, sends an Accepts header with the value */*

  • axios, by default, picks up the X-XSRF-TOKEN, whereas jQuery ajax doesn’t.

I tried to single out the issue by modifying the axios request and re-sending it and came to the obvious conclusion that the X-XSRF-TOKEN was the missing piece.

I have finally found the most accurate solution that worked for me, it may also be your problem if nothing works after going through all the steps shown in the https://laravel.com/docs/7.x/passport documentation The issue was that I have forgotten to change token to passport as my driver for the API auth guard.

I had to fix it like this, and it worked after some hours of debugging

'api' => [
            'driver' => 'passport',
            'provider' => 'users',
            'hash' => false,
 ]

Before it was this way, as I’ve forgotten to update my configuration

'api' => [
            'driver' => 'token',
            'provider' => 'users',
            'hash' => false,
 ]

You can check this @grayloon, @ahinkle

Thanks @marlocorridor .

I did not expect that the source of my problem was the configuration that was being cached.