User authentication with NuxtJS and Laravel

Conventional wisdom states that unless you know what you're doing, you should never roll your own authentication scheme when creating a website. Luckily for us, Laravel comes with Passport, a handy package if you want to start authenticating frontend interfaces via an API call.

I've found that hunting around the internet I could only find custom JWT integrations for API authentication, so hopefully you will find this article useful if you want to use your frontend and hook into laravel's passport authentication.

On the server:

  1. You'll need to install passport. The official docs do a good job of explaining how so run through the steps and come back when you're done.

  2. You'll want to create a new LoginController.php , in this example, i've created it in app/Http/Controllers/v1/LoginController.php.

  3. You'll want to add the following endpoints to your api.php: POST /v1/auth/login , GET /v1/auth/user, POST /v1/auth/logout

Example LoginController:

<?php
// LoginController.php
namespace App\Http\Controllers\v1;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\AuthenticatesUsers;

class LoginController extends Controller
{
    use AuthenticatesUsers;
    public function sendLoginResponse(Request $request)
    {
        $user = auth()->user();
        $token = $user->tokens()
          ->where('revoked', false)
          ->whereDate('expires_at', '>', now()
          ->addDay(1))
          ->first();
        return [ 'token' => $token ? $token->accessToken : $user->createToken('SPA Login')->accessToken ];
    }
}

And here's an example api.php

<?php
// api.php
use Illuminate\Http\Request;

/*
|--------------------------------------------------------------------------
| API Routes
|--------------------------------------------------------------------------
|
| Here is where you can register API routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| is assigned the "api" middleware group. Enjoy building your API!
|
*/
//Route::middleware('auth:api')->get('/user', function (Request $request) {
//    return $request->user();
//});

Route::prefix('v1')->group(function() {
    Route::post('/auth/login', 'v1\LoginController@login');
    // Restricted endpoints
    Route::middleware('auth:api')->group(function() {      
        Route::get('/auth/user', 'v1\LoginController@user');
        Route::resource('users', 'v1\UserController')->except(['edit', 'create']);
    });
});

For the client:

On the NuxtJS side of things you'll need to install the @nuxtjs/auth module for the whole thing to work. So add the following configuration to your nuxt.config.js

// nuxt.config.js
const pkg = require('./package')
module.exports = {
  /*
  ** Nuxt.js modules
  */
  modules: [
    // Doc: https://axios.nuxtjs.org/usage
    '@nuxtjs/axios',
    // Doc: https://buefy.github.io/#/documentation
    'nuxt-buefy',
    '@nuxtjs/pwa',
    '@nuxtjs/auth',
  ],

  /*
  ** Axios module configuration
  */
  axios: {
    // See https://github.com/nuxt-community/axios-module#options
    baseURL: '//homestead.test/api/v1'
  },

  auth: {
      strategies: {
        local: {
          endpoints: {
            login: { url: 'auth/login', method: 'post', propertyName: 'token' }, 
            user: { url: 'auth/user', method: 'get' }
          }
        }
      }
  },

  router: {
    middleware: ['auth']
  }
}

You'll need some place to hold the authenticated user so create store/auth.js to make the information available to the datastore.

Last but not least, create a new login.vue page so that you can actually log in.

// login.vue
<template lang="html">
  <div class="container">
    <b-field label="E-mail">
      <b-input v-model="email" type="email" required />
    </b-field>

    <b-field label="Password">
      <b-input v-model="password" type="password" required />
    </b-field>
    <button @click="login" class="button is-primary">
      Login
    </button>
  </div>
</template>

<script>
export default {
  auth: false,
  data() {
    return {
      email: '',
      password: ''
    }
  },
  methods: {
    async login() {
      try {
        const data = { email: this.email, password: this.password }
        await this.$auth.loginWith('local', { data: data })
      } catch (e) {
      }
    }
  }
}
</script>

That's pretty much it.