stueynet's avatar

Passport, Oath2, JWT and the web client

At the end of this stream around 1:24 or so Taylor discusses how you can use passport to authenticate calls to your API from within your web app. For example with Vue.js. We actually implemented this 6 months ago using JWT and some middleware that adds the JWT token to the response etc...

What I am wondering is how this relates to Oath2? We generate our JWT tokens when a user authenticates in Laravel. In Passport does the concept Taylor describes, require the Oauth Flow still or is that handled when a user does normal session based Auth with Laravel / Passport? We don't use Oauth2 at all currently, just JWT.

Thanks in advance and here is that stream: https://streamacon.com/video/laracon-us/taylor-otwell-laravel-53-overview

0 likes
13 replies
jeroenherczeg's avatar

What I took away from it was, it implements the oauth server from the php league but using the package https://github.com/lucadegasperi/oauth2-server-laravel He showed the authorization code grant implementation https://oauth2.thephpleague.com/authorization-server/auth-code-grant/ but he also showed you can bypass this, I think with JWT tokens, so you can use your api for internal requests from the front-end.

Passport is only for authentication, you still have to implement endpoint, responses, error response structure, ... yourself.

I think passport makes sense if you have a saas app with external integrations, but I only have an api for my front-end. But I am still considering using it, so I have experience implementing it and have the know how for when I need it.

But I could be wrong :)

1 like
stueynet's avatar

@jeroenherczeg I took the same thing from the video. The part where he did direct JWT has nothing to do with Oauth. Hopefully someone can confirm that.

nutracelle's avatar

@stueynet I have been playing with Passport for the last two days to see if it would work in my own personal setup. The direct JWT authentication you mentioned has nothing to do with oAuth2.

The way it works is by adding the CreateFreshApiToken middleware to your web middleware group which will create an encrypted JWT cookie called laravel_token. You can utilize that cookie along with a csrf token to authenticate through your API without having to go through the whole oAuth flow. Take a look at the example below.

In your master view (usually) you can add an inline script that will house your csrf token.

window.Laravel = {
    csrfToken: "{{ csrf_token() }}"
};

Then when making your Ajax request you can attach that as a header. I'll write it with Vue and Vue Resource to keep in line with the documentation.

export default {
    ready() {
        this.$http.get('/users')
            .then(response => {
                console.log(response.data);
            });
    }
}

By default, Laravel 5.3 includes a bootstrap.js file that sets up an interceptor to add the csrf token. It looks like this.

Vue.http.interceptors.push((request, next) => {
    request.headers['X-CSRF-TOKEN'] = Laravel.csrfToken; // Laravel is coming from the window object

    next();
});

Now assuming that your API exists on the same domain, you can skip down to the next part, but what if you're using a sub domain? There's a bit more work you have to do.

With good ol' jQuery we were used to setting something like the following.

$.ajax({
    xhrFields: {
        withCredentials: true
    }
});

But with Vue Resource (at least the latest version), we have to do this (the documentation is a bit lacking at the moment, but I'm sure that will be solved soon enough).

Vue.http.options.credentials = true;

So all in all, we end up with whats below. We can add in the request.url line so we don't have to write the full hostname before every url in every ajax call.

Vue.http.options.credentials = true;

Vue.http.interceptors.push((request, next) => {
    request.url = 'http://api.example.dev' + request.url;
    request.headers['X-CSRF-TOKEN'] = Laravel.csrfToken;

    next();
});

So now we get back to the server side, with Passport installed and set up, we reach the auth:api middleware. We see one call in the handle method in this new middleware, to the authenticate method which looks like this.

protected function authenticate(array $guards)
{
        if (empty($guards)) {
            return $this->auth->authenticate();
        }

        foreach ($guards as $guard) {
                if ($this->auth->guard($guard)->check()) {
                    return $this->auth->shouldUse($guard);
            }
        }

    throw new AuthenticationException;
}

The $guard that get's passed through in the default setup is api and when we call ->check() it is being called on the \Illuminate\Auth\RequestGuard class. check() does nothing more then the following.

return ! is_null($this->user());

If we following that through, we eventually get taken to our proper guard for passport, which is \Laravel\Passport\Guards\TokenGuard. The user() method takes the following form.

public function User(Request $request)
{
    if ($request->bearerToken()) {
        return $this->authenticateViaBearerToken($request);
    } elseif ($request->cookie('laravel_token')) {
        return $this->authenticateViaCookie($request);
    }
}

So from this, we can see that either a bearer token (the actual oAuth2 stuff) or the cookie is required to authenticate. Now seeing as a cookie can only be read by the domain (or a subdomain if set up properly), that makes it a prime candidate for consuming your own API with javascript. I'm not going to bother going further, but that is the basics of how to consume your own API without the whole oAuth flow.

7 likes
stueynet's avatar

Thanks for the detailed response @craigpaul! Much appreciated. Yes so as expected this is pretty much what we did but we rolled our own. Created our own middleware. Look for headers in all requests etc... If Passport came out 6 months ago this would have been very helpful. But at this point I don't see a good reason to migrate over.

ovidiu_dtp's avatar

Taylor uses some Vue components as example. Any clue where can we see the implementation for those components?

ovidiu_dtp's avatar

I did the steps described by Taylor in his tutorial on Laracast, but I have a problem. When I call my API from Postman, I am always unauthorized, and I can't see why that is. I have noticed that the id in the table is a lot shorter than the token provided in the interface.

For the record, this is the token:

eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImp0aSI6IjNhZjRiZWNjZDhiNmQ5MjYwNGU0Yzc4NWVkM2RkZTE0MWRhMWM3MThiMDg0NTU4ZTQ4NWE3ZGU2NjJhYWY1MDE0MmY4ZDUyYjZhNzc3NGIwIn0.eyJhdWQiOiIxIiwianRpIjoiM2FmNGJlY2NkOGI2ZDkyNjA0ZTRjNzg1ZWQzZGRlMTQxZGExYzcxOGIwODQ1NThlNDg1YTdkZTY2MmFhZjUwMTQyZjhkNTJiNmE3Nzc0YjAiLCJpYXQiOjE0NzIxMjIzMDMsIm5iZiI6MTQ3MjEyMjMwMywiZXhwIjo0NjI3Nzk1OTAzLCJzdWIiOiIxIiwic2NvcGVzIjpbXX0.O1AT44RqSmuWTGPuxhTbl9AYZSgkAVuxFDd75pewr_H7JQudV656TivdenLbzoETeKypPXMX_jwHNqSR8k_sRiZLBszNPWjaRVFFFY2t08GyaoCkdwojw5brfkbo5MrVzpLJfnRH4ubZWCMb3PdDzg1hgLkVd6NB2BgezGk6-3XD4EXfUAHH5RxsFl6o9dvhFEYyFTjD2MYx_7CrZat5Dy2UPHadzEtdlWIZD1mLnpnp_XHizI25Qhe3FOBHIpksPXCYdR0LmxCEUXo0QzKkQDyP9hN_pLG-YgYt7mFH-Nh_D_3JketNyngTOt_5gG6MEyIw4vJGoNla4Q1PMC1ezeRgB1Cb_IXTKcblfspPdELc8QnTFXrNlUyMHQCuKcbMfTQMbHIyY7LSoW0nGlx_DM5tqtp_wmjlDsVWjWhSl8dwRjrficEuC2ihvAS9JKGmVCVIoamaUoHc93QTytJJwuKc0cDaHQtZ2X_DfXiuP9pXzbZYArperccsqImSnHMsYrNvdIHgT7YHTbEzVgaLR0f6kXbbMHZMZraBfHGbt30hMbNaQRZT_oWnpPBFjxIg-l-FPkUhgNKVYhOMdyxNtpe1Bxou6m6fK-nTSYm9RUxhaLosHnkOcQESj-Bqpglyg2oUYJl7Uljtn47S9bfiNcKnJ64BXg5FlQk0buGULd8

and the id is

3af4beccd8b6d92604e4c785ed3dde141da1c718b084558e485a7de662aaf50142f8d52b6a7774b0

Is this normal? What do you think I am doing wrong?

ovidiu_dtp's avatar

Looks like there was something I was missing. In order for this to work, you do need at least one scope. If you do not add in the service provider a Passport::can, it would not work. That was my mistake. Now it works!

jerlandsson's avatar

@craigpaul Wow! Thanks A LOT! I've been struggling with this for like almost two days. Your eminent guide helped me instantly! SO greatful!

nutracelle's avatar

Hey @rm, not a problem! Keep in mind that was written a while ago, so some things might be slightly out of date. Especially the way to set the csrf token header, that way only works in vue resource 0.9.3 and below.

edstevo's avatar

@craigpaul - You are a lifesaver.

I have been hitting my head against a wall with this for days! (Passport via a subdomain).

This line is what was missing:

Vue.http.options.credentials = true;

Thank you so much for taking the time to do this.

aneeshsudhakaran's avatar

How we can do Single Sign On (SSO) in Laravel 5.3?

  1. Is passport support SSO?

Please or to participate in this conversation.