Vue2 authentication/authorization examples, tutorials?

Hi guys!

Are there any good/complete examples/repos/tutorials about authentication/authorization in Vue2 ?

I’m looking for a standard solution supporting oath, jwt, bearer token for nodejs+passport or laravel (+maybe a .net webapi ) on the server side. I also like to implement registration/login with social auth services (fb, twitter, google,…).

4 Likes

Certainly not the best nor most complete resource you can find, but I implemented JWT+Laravel for my app here here. I use it with jQuery (don’t laugh! I have other uses for jQuery elsewhere in the app…) Anyway, the idea is simple:

Hope this gives you a headstart.

EDIT: The app now uses axios. jQuery has been completely replaced with modern API’s.
EDIT 2: Updated the links since I’ve made some major changes to the repo’s structure.

3 Likes

@ggaborx

Not Vue specific, but I would put some thought into if you really need to jump on the JWT bandwagon.

If a user needs to destroy a session remotely (sign in out of all devices for example), you’d have to implement something that would hit a database for each request to check the validity of the token. This in turn basically destroys the usefulness of storing all the authenticate parameters and authorization in the token in the first place. You might as well store what you would store in a JWT in a database, that way the user has fine grain control over their sessions.

You might ask where JWTs would be useful if not client->server in web applications, and the answer would most probably be services (mostly internal), where the tokens do not store or identify a session, but rather one off bang.

It certainly depends on your application, and you may not care that much in the first place, but if you do decide to implement fine-grained session control, I’d steer away from JWT.

Edit: I thought I’d add a couple more reasons.

  • If you’re storing JWTs in localStorage, depending on what is in the JWT and what your application does, you could be vulnerable for XSS session theft. Generally a good idea to store server side session related stuff in a HTTP only cookie, where javascript cannot read it as an extra precaution.

  • You’ll be sending the JWT for each authenticated request, and depending on how much information is in the JWT, that will start to add up over time (extra bytes in the header).

Edit as response to your question below as I’ve reached the post limit for a new user:

@ggaborx

Sure. OAuth is a way of implementing an authentication system. JWTs are not an authentication system, they have just been used as a way of storing the session created by authenticating.

The idea behind JWTs is valid, where you don’t need to store them on the server to check their validity. Only a server with the secret can create a valid token, and then validate it, meaning you don’t have to store state on a server. This is important because if you are attempting to scale horizontally (more servers) and you are storing sessions on a server without sharing them with a database, you’ll run into problems as the load balancer generally will fire each request to a server with less load. Who’s to tell if that server you just stored the user who signed in on will be the server the next request for that user goes to? Now there are methods around this, on different levels…

  • On the load-balancer you can use hash lookups to ensure the user always reaches the same server, but this method has problems such as; what if that server goes down?
  • You can store the session state on the client side with methods like JWTs. However now, as I listed above, you lose control over important aspects you may consider important in the future, like invalidating a session. If you decide to implement JWT checking with a database, congratulations! because at this stage, you might as well store the session in the database anyways.
  • Storing the state in the database, you have control over the session, no nasty techniques like blacklisting JWTs with Redis, and invalidating a session is as easy as deleting it.

OAuth dictates a number of ways a process of authentication should flow, for example; Implicit, Credentials, Authorization Code etc. Just as a side note you’ll find these flows, although well defined, aren’t always honoured to the complete spec even by larger companies.

There are quite a few resources you can find with google about authentication and authorization however I can give you a small example.

  • User makes a request to the server POSTing their credentials over HTTPS and on a successful match, gets returned a session ID in the form of a HTTP only cookie. This session ID could represent a record in a database (postgres, redis, etc), with record containing the details for the session. For example when it should expire, scopes the session has (access control), or meta data like when it was created and the user agent.
  • User makes a request to an API, the API checks the Origin header of the request (CORS to prevent CSRF), and retrieves the session via the ID from the Cookie header. If the session exists the session could be checked further if needed, for example, the User-Agent, and the request could continue if it was valid!

Now this is a simple example, and your application may as well not need it, however, it keeps you in control of the session storage, as well as being suitable for scaling horizontally when the time comes. You’ll find that Vue doesn’t care about how you authenticate, so really implement it how you will. There are well trodden methods, and Vue can work smoothly with pretty much all of them.

By all means, you can use JWTs, just my two cents on the matter.

Have a check out of https://www.owasp.org/ it has some good resources on what is good and bad.

6 Likes

Thanks guys!

@phanan:
I will check your solution. Just curious why did you use jquery ajax insted of vue http or any other (promise) library. Is there any benefit over them?

@avitex:
I thought that the oauth/jwt is the standard way of doing auth stuff. What other options exists? Can you please tell some details about other alternatives? Where should I steer away from the JWT?

@ggaborx Like I said, there are other parts in my app that require jQuery, so I just went ahead and use its AJAX functionalities instead of adding an extra package into the stack, saving several kBs. Of course, vue http (vue-resource you mean?), axios, or superagent etc. would do the job just fine.

EDIT: The app now uses axios. jQuery has been completely replaced with modern API’s.

@avitex: Thank you very much! Your summary was awesome! I didn’t really know the difference between oauth a jwt. I just used all-in-one solution packages before and those always defined themself as oauth+jwt+bearer token+whatever. That was an eye-opening moment for me :slight_smile:

So as you wrote I can use any auth technology with Vue. Fine. Can you please recommend a package or repo to implement an auth logic which supports own account system and various social apis? I would like to provide to register with an e-mail or for example a facebook account. I think nowdays this way became standard.

1 Like

I created a node/passport auth logic on server side.
Now I try to implement the authentication logic on the client side too.

First of all I try to understand how things works so I don’t prefer any library yet. Instead I wonder what are the best practices to handle login/logout + authorization in Vue2.

Somewhere in my components there is a login box which will send a request to the server and recieves a result which contains some user data if login attempt was successfull. I also have a logout button somewhere which will similary send a request and recieves result. I also have routes and components which can be loaded for anyone or just a logged in user. Plus I have some dynamic components which shows different contents for guests and authenticated users.

Nothing new I think.

My question is what is the best way to manage all auth stuff within a Vue2 project?

  1. Where can I put the result of success login recived from the server?
  2. How can I share that user object across all components and routes?
  3. How can I detect if user logged out or the session expired?
  4. How can I automatically update all rendered components if the user just logged in/out?
  5. How can I check if user has the appropriate right to see certain content?

There’s no “best way,” it all comes down to what your application does and what you want the behaviors to be. For example:

  1. Local storage, client DB, cookie, or just memory.
  2. By accessing said local storage, client DB, cookie, or memory, maybe via a wrapper library/service.
  3. Send the token/cookie with each of your requests and let the server decide.
  4. Use conditional rendering. <app v-if="userService.isUserLoggedIn" /><login v-else />
  5. Again, conditional rendering. <admin-panel v-if="user.isAdmin" />. Of course, you’ll want to control the response data from the server as well, using some authorization mechanism.

My advice, if I could: Just do it, and only worry about a blocker when it becomes one. You’re worrying too much it seems. Btw you do realize some of the questions are repeated, right?

1 Like

Thank you @phanan! You are absolutely right. I’m worrying too much :slight_smile: Just because the lack of the real-life examples for vue2. I never know if I do it right or wrong. As many repos I checked as many different concepts I found.

BTW I ended up with my custom auth plugin which creates an instance of an Auth class which solves all auth related issues. I did this just because I didn’t found any repo with a similar concept :slight_smile: I’m inspired by the https://github.com/websanova/vue-auth/blob/master/src/index.js repo but I created a much more simplified ES6 based solution. Now I have an $auth property for all components and that one contains everything like: $auth.user, $auth.login(), $auth.register()…

Only one question I have. As you mentioned I can use conditional rendering - for example: v-if="$auth.user". Is this will automatically watching $auth.user changes or I need to do something to update?

1 Like

It should be automatic – that’s the beauty of Vue :slight_smile:.

1 Like

Unfortunatelly its not automatic.
Here is what I created (just a functional mock - without real server requests):

export class AuthPlugin {
  constructor(options) {
    this.user = { name: 'John' };
    ApplyRouteGuard.call(this, options.router);
  }
  login() {
    this.user = { name: 'John' };
    return true;
  }
  logout() {
    this.user = undefined;
    return true;
  }
  static install(Vue, options) {
    Vue.prototype.$auth = new AuthPlugin(options);
  }
};

function ApplyRouteGuard(router) {
  router.beforeEach((to, from, next) => {
    let route = to.matched.find(e => e.meta.auth != null);
    if (route) {
      let auth = route.meta.auth;
      if (auth && !this.user) {
        console.log('Access denied - only for authenticated users:', route.path);
        //TODO: redirect to the login page
      }
      else if (!auth && this.user) {
        console.log('Hide from authenticated users - only for guests:', route.path);
        //TODO: disable, hide somehow
      }
      else {
        console.log('Appropriate route, user/guset has rights to visit:', route.path);
      }
    }
    next();
  });
}

My main.js (entry point):

import main from './main.vue';
import home from './page/home';
import info from './page/info';
import { AuthPlugin, AuthRoutes, AuthBox } from './pack/auth';
Object.assign(main, {
  router: new VueRouter({
    mode: 'history',
    routes: [
      { path: '/', component: home },
      { path: '/info', component: info },
      ...AuthRoutes('/auth')
    ],
  }),
  components: { AuthBox }
});
Vue.use(VueRouter);
Vue.use(AuthPlugin, {
  router: main.router
});
export default new Vue(main);

I copy here my dynamic AuthBox which changes rendered component if the user logged in our out. My first issue here that it was implemented with a manual re-check logic. Is it possible to automatically change rendered view component if this.$auth.user changed?

<template>
  <component :is="view" v-on:changed="checkUser"></component>
</template>
<script>
  import authBoxUser from './authBoxUser';
  import authBoxGuest from './authBoxGuest';
  export default {
    data: function()  {
      return {
        view: this.$auth.user ? authBoxUser : authBoxGuest
      };
    },
    methods: {
      checkUser
    }
  };
  function checkUser() {
    this.view = this.$auth.user ? authBoxUser : authBoxGuest;
  }
</script>

I also copy here my auth routes.js (here you can see how I applied ‘auth’ property within the meta):

import page from './page';
import login from  './page/login';
import profile from './page/profile';
import register from './page/register';
export const AuthRoutes = (root) => {
  return {
    path: root,
    component: page,
    children: [
      { path: 'login',    meta: { auth: false }, component: login },
      { path: 'profile',  meta: { auth: true  }, component: profile },
      { path: 'register', meta: { auth: false }, component: register }
    ]
  }
}

Finally I show you where is my second issue resides. The auth part contains its own root page (as you can see in my routes.js) which will provide multiple different links for guest and authenticated users. As you mentioned I tried to apply v-if for each router links. (BTW: is it possible to automatically show/hide router links by applied route guards?):

<template>
  <div>
    <h1>AUTH PAGE</h1>
    <nav>
      <router-link v-if="!$auth.user" to="/auth/login">Login</router-link>
      <router-link v-if="$auth.user"  to="/auth/profile">Profile</router-link>
      <router-link v-if="!$auth.user" to="/auth/register">Register</router-link>
    </nav>
    <router-view></router-view>
  </div>
</template>

The problem is when I login/logout and the $auth.user's value changes those v-if won’t applied automatically. If I navigated to another page (without browser refresh) then came back to the auth page the v-if conditions applied.

After so many wrong try finally I figured out that I must create a new Vue instance - instead of new instance of my own AuthPlugin class - to easily provide a reactive prototype property across the whole project. So at this point I really think the rule of thumb is:

If you want a reactive variable anywhere in your project you must always create a new Vue({…}) instance and set that variable as a data member of it.

Is it true?

Anyway, I modified my AuthPlugin as you can see below (exactly same content as it was in my AuthPlugin class):

export const AuthPlugin = {
  install(Vue, options) {
    Vue.prototype.$auth = new Vue({
      data: function() {
        return {
          user: undefined
        }
      },
      created: function() {
        ApplyRouteGuard.call(this, options.router);
      },
      methods: {
        login() {
          this.user = { name: 'John' };
          return true;
        },
        logout() {
          this.user = undefined;
          return true;
        }
      }
    });
  }
};

The v-if conditions works well only with this technique.

I also can simplify my AuthBox logic becasue of the $auth.user is reactive now - so I can define my view variable as a computed property (compare the code with the previous post):

<template>
  <component :is="view"></component>
</template>
<script>
  import authBoxUser from './authBoxUser';
  import authBoxGuest from './authBoxGuest';
  export default {
    computed: {
      view: function() {
        return this.$auth.user ? authBoxUser : authBoxGuest;
      }
    }
  };
</script>

I think I’m on the right way now with the solution above.
Only one issue left:

How can I automatically navigate to the first appropriate parent route when my $auth.user changed? I didn’t found any option in the router docs so I think I need to implement it on my own.

I also wonder if there is a way to automatically hide/disable <router-link> if the corresponding auth guard would restrict access? Then I wouldn’t need to set the same logic parallel in the route and for the link with v-if:

route:

{ path: 'profile', meta: { auth: true }, component: profile },

template:

<router-link v-if="$auth.user" to="/auth/profile">Profile</router-link>

Thank you very much for your writeup, it helped me a lot getting my own running!

Great info in this topic, thanks a lot

*btw you can highlight code when you add the language like this:
’’‘js
someJSCodeHere
’’‘
or
’’‘html
someHtmlOrVueMarkupHere
’’’

Sample output

<template>
  <component :is="view"></component>
</template>
<script>

  import authBoxUser from './authBoxUser';
  import authBoxGuest from './authBoxGuest';

  export default {
    computed: {
      view: function() {
        return this.$auth.user ? authBoxUser : authBoxGuest;
      }
    }
  };

</script>

Hi , I’m new beginning for this site recommend I thank you for everything.

As you said, if you’re using HTTP only cookie, how to prevent the CSRF vulnerability? Some framework like Angular read token from SS/LS/Cookie and inject it into request header to avoiding forged request take with user cookie, obviously a HTTP-only can’t satisfy this tech.

Sure. There are plently of methods of protecting against CSRF, quite simply, do something else in addition to the cookie to verify the type of request. For example, CORS, double submit cookie, custom header, etc.

OWASP has some great solutions to these problems listed here.

HEllo . pleasee help me . im a novice . i got this problem

You are running Vue in development mode.
Make sure to turn on production mode when deploying for production.
See more tips at https://vuejs.org/guide/deployment.html

what should i do to turn on production mode?

THis is my component

1.About.vue

  1. customers**strong text**.vue
ABOUT

3.main.js

// The Vue build version to load with the import command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from ‘vue’
import App from ‘./App’
import VueRouter from ‘vue-router’
import VueResource from ‘vue-resource’
import Customers from ‘./components/Customers’
import About from ‘./components/About’

Vue.use(vueResource)
Vue.use(VueRouter)

const router = new VueRouter({
mode:‘history’,
base:__dirname,
routes:[
{path:’/’,componrnt: Customers},
{path:’/about’, component: About}
]

})

/* eslint-disable no-new */
new Vue({
router,
template:

}).$mount('app')

thanks in advance

Thanks for sharing such useful ideas here. Can anyone give any ideas to fix iTunes error 9? Please help me to fix it.