3

I'm attempting to install Laravel Passport on Laravel 5.7.18 using PHP 7.2.13.

My application consumes the API within itself using JavaScript (Axios with Vue)

I'm getting a 401 Unauthorized error within the JavaScript web application. I've read the documentation and added CreateFreshApiToken to the web Kernel. The laravel_token cookie is in fact setting itself. However, the oauth tables are clean in the database.

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');
}

Headers in Browser that returns 401: enter image description here

Working Request with Postman: enter image description here

ahinkle
  • 2,117
  • 3
  • 29
  • 58

2 Answers2

3

If you are using a username and password for initial login then the assumption is you are building a First Party application that has the right to make a user/pass login attempt.

It is highly recommended that if you are building a Reactive app using Angular, React.js or Vue.js then an SPA (Single Page Application) approach will yield a much more robust product.

https://en.wikipedia.org/wiki/Single-page_application


You should note that with this particular method, if your application makes a static (none ajax request) and thus reloads in the browser, you will loose the auth token. In this case you are not playing host to an app that is by it's very definition an SPA, so if you need to retain the token between static request reloads then you need to store the token in a cookie, I suggest using a cookie rather than localStorage because the availability of localStorage is not 100% guaranteed to be at your disposal in all web browsers.

If your application is on the same domain, you do not need to use Passport. Instead native session cookie auth is perfectly fine, all you have to do is make sure you are passing the CSRF Token for post requests.


For user/pass token grants you should follow this guideline: https://laravel.com/docs/5.7/passport#password-grant-tokens

From that guide, when you make a successful request to /oauth/token, the returned token should be set in your application as an Authorization header with Bearer token.

The token request response looks like this:

{
    "token_type": "Bearer",
    "expires_in": 31536000,
    "access_token": "eyJ0eXAiOiJKVJhb...nheKL-fuTlM",
    "refresh_token": "def502008d6313e...94508f1cb"
}

You should request and handle that JSON object as follows:

axios.post('/oauth/token', {
    grant_type: "password",
    client_id: "1",
    client_secret: "zkI40Y.......KvPNH8",
    username:"email@address.com",
    password:"my-password"
}).then( response => {

    axios.defaults.headers.common['Authorization'] = `Bearer ${response.data.access_token}` 

} );

The values for client_id (id) and client_secret come from oauth_clients table, there should already be an entry in there.

If not then run php artisan passport:client --password

Don't forget that you will have to configure some headers, look to this post has some relevant information for the Oauth Authorization header: How to send authorization header with axios

Marc
  • 5,109
  • 2
  • 32
  • 41
  • Hi Thanks for the suggestion but I've double checked the authorization headers and everything matches the documentation. – ahinkle Dec 21 '18 at 04:03
  • Could you paste a copy of the response headers from a login and the request headers for a request after login in your original post? – Marc Dec 21 '18 at 10:53
  • Yes, I've updated my question with two screenshots. One from browser (returns 401) and one from Postman (working) – ahinkle Dec 21 '18 at 14:39
  • Are you following tutorial for first party application? https://laravel.com/docs/5.7/passport#password-grant-tokens – Marc Dec 21 '18 at 20:20
  • I updated my answer with a small example of handling the oauth token request. – Marc Dec 21 '18 at 21:12
  • This answer looks right - This is how I handled taht process last year - https://stackoverflow.com/questions/44172818/registering-user-with-laravel-passport – senty Dec 21 '18 at 21:56
  • yeah, that will work for one axios call but when your app has several hundred it's a real pain to add the header.. in my opinion this should just work out of the box. – ahinkle Dec 22 '18 at 03:37
  • Are you building a Single Page Application (SPA) or are you just building on-top of Laravels native Vue implementation because the one that ships with Laravel is a poor way of leveraging Vue? In a true SPA you would only have to set the Auth header one time only. Sounds like you don't have an SPA setup on the go? The basically means are you building an app using Vue-Router? – Marc Dec 22 '18 at 10:18
-1

If you say your tables are clean in the database, try to run this command again:

php artisan passport:install

This command will create the encryption keys needed to generate secure access tokens. In addition, the command will create "personal access" and "password grant" clients which will be used to generate access tokens:

Laravel 5.7 Passport docs

Diego Vidal
  • 1,022
  • 12
  • 21
  • Nah, just adds more access tokens. ```php artisan passport:install Encryption keys already exist. Use the --force option to overwrite them. Personal access client created successfully. Client ID: 2 Client Secret: MUiAhE********************** Password grant client created successfully. Client ID: 3 Client Secret: zgsEoU*****************************``` – ahinkle Dec 20 '18 at 01:22