Search code examples
laravelsingle-page-applicationaccess-token

How to use access_token to authenticate SPA with Laravel 5.4


I'm trying to authenticate my single page app (written in Marionette) with my Laravel 5.4 app. In my previous experience, an SPA would:

  • send the username & password to an /api/login-token endpoint which would return with something like the following:

    { "access_token":"-wt7x8UPRH9GcbTeUQ3wIA7zuc02XeHil4qsoKJcOUU", "type":"Bearer", "expires_in":2570, "refresh_token":"g9zmNkC1zpQ8fM0uSeJdy_VZe7TuBUG4MYGIBuAtXvg" }

  • the SPA will store the access_token in the browser's localstorage, and send it in an access_token header with every subsequent request made to the backend

  • the backend will recognise this access_token and authenticate the user

Unfortunately I'm struggling to understand how to achieve this in Laravel 5.4.

Do I need to use Passport? I really don't think I need OAuth2, but does Passport also offer simple token-based auth? It seems to, and I have gotten the expected tokens described above from the /oauth/token endpoint, but I don't know how to use this token. I don't think it's even for this purpose.

I tried sending a request to /api/user with this token in the headers, POSTing it, and as a query string, with no luck. Also making me concerned is the expires_in from Laravel is a year (31536000 seconds = 365 days) which seems way too long. I'm worried this Passport OAuth token is actually for OAuth2 access and not a 1-day-ish expiring access token that I'm used to.

I also read about Laravel's TokenGuard but that seems like some weird kind of token that's stored in an api_token column on the user table, which is all wrong by my mindset. For example, it doesn't expire, and it's per-user not per-user-session, meaning the same token would have to be used from multiple devices. etc

Very confused... grateful for any help!


Solution

  • I actually ended up using Passport, based on some really helpful code found at https://laracasts.com/discuss/channels/code-review/api-authentication-with-passport?page=1&replyId=282168:

    routes/api.php

    Route::post('auth/token', 'Api\Auth\DefaultController@authenticate');
    Route::post('auth/refresh', 'Api\Auth\DefaultController@refreshToken');
    

    app/Http/Controllers/Api/Auth/DefaultController.php

    <?php
    
    namespace App\Http\Controllers\Api\Auth;
    
    use Illuminate\Http\Request;
    use App\Http\Controllers\Controller;
    use Illuminate\Support\Facades\DB;
    use Illuminate\Support\Facades\Route;
    
    class DefaultController extends Controller
    {
        /**
         * @var object
         */
        private $client;
    
        /**
         * DefaultController constructor.
         */
        public function __construct()
        {
            $this->client = DB::table('oauth_clients')->where('id', 2)->first();
        }
    
        /**
         * @param Request $request
         * @return mixed
         */
        protected function authenticate(Request $request)
        {
            $request->request->add([
                'username' => $request->username,
                'password' => $request->password,
                'grant_type' => 'password',
                'client_id' => $this->client->id,
                'client_secret' => $this->client->secret,
                'scope' => '*'
            ]);
    
            $proxy = Request::create(
                'oauth/token',
                'POST'
            );
    
            return Route::dispatch($proxy);
        }
    
        /**
         * @param Request $request
         * @return mixed
         */
        protected function refreshToken(Request $request)
        {
            $request->request->add([
                'grant_type' => 'refresh_token',
                'refresh_token' => $request->refresh_token,
                'client_id' => $this->client->id,
                'client_secret' => $this->client->secret,
            ]);
    
            $proxy = Request::create(
                '/oauth/token',
                'POST'
            );
    
            return Route::dispatch($proxy);
        }
    }