How do you authenticate a VueJS app with Azure AD? How do you authenticate a VueJS app with Azure AD? vue.js vue.js

How do you authenticate a VueJS app with Azure AD?


To solve this very problem, I leaned on ADAL JS. I've made a Vue + Vue-Router sample application available here - but I'll include the important pieces below.

In your package.json:

"dependencies": {    "adal-angular": "^1.0.15",    "vue": "^2.5.2",    "vue-router": "^3.0.1"},

A basic wrapper module for the ADAL JS library:

import AuthenticationContext from 'adal-angular/lib/adal.js'const config = {  tenant: 'your aad tenant',  clientId: 'your aad application client id',  redirectUri: 'base uri for this application',  cacheLocation: 'localStorage'};export default {  authenticationContext: null,  /**   * @return {Promise}   */  initialize() {    this.authenticationContext = new AuthenticationContext(config);    return new Promise((resolve, reject) => {      if (this.authenticationContext.isCallback(window.location.hash) || window.self !== window.top) {        // redirect to the location specified in the url params.        this.authenticationContext.handleWindowCallback();      }      else {        // try pull the user out of local storage        let user = this.authenticationContext.getCachedUser();        if (user) {          resolve();        }        else {          // no user at all - go sign in.          this.signIn();        }      }    });  },  /**   * @return {Promise.<String>} A promise that resolves to an ADAL token for resource access   */  acquireToken() {    return new Promise((resolve, reject) => {      this.authenticationContext.acquireToken('<azure active directory resource id>', (error, token) => {        if (error || !token) {          return reject(error);        } else {          return resolve(token);        }      });    });  },  /**   * Issue an interactive authentication request for the current user and the api resource.   */  acquireTokenRedirect() {    this.authenticationContext.acquireTokenRedirect('<azure active directory resource id>');  },  /**   * @return {Boolean} Indicates if there is a valid, non-expired access token present in localStorage.   */  isAuthenticated() {    // getCachedToken will only return a valid, non-expired token.    if (this.authenticationContext.getCachedToken(config.clientId)) { return true; }    return false;  },  /**   * @return An ADAL user profile object.   */  getUserProfile() {    return this.authenticationContext.getCachedUser().profile;  },  signIn() {    this.authenticationContext.login();  },  signOut() {    this.authenticationContext.logOut();  }}

In the app's entry point (main.js if you used vue-cli):

import Vue from 'vue'import App from './App'import router from './router'import authentication from './authentication'// Init adal authentication - then create Vue app.authentication.initialize().then(_ => {  /* eslint-disable no-new */  new Vue({    el: '#app',    router,    template: '<App/>',    components: { App }  });});

For your Vue router configuration:

import Vue from 'vue'import Router from 'vue-router'import HelloWorld from '@/components/HelloWorld'import authentication from '../authentication'Vue.use(Router)const router = new Router({  mode: 'history',  routes: [    {      path: '/',      name: 'HelloWorld',      component: HelloWorld,      meta: {        requiresAuthentication: true      }    }  ]})// Global route guardrouter.beforeEach((to, from, next) => {  if (to.matched.some(record => record.meta.requiresAuthentication)) {    // this route requires auth, check if logged in    if (authentication.isAuthenticated()) {      // only proceed if authenticated.      next();    } else {      authentication.signIn();    }  } else {    next();  }});export default router;

In your Vue components:

import authentication from './authentication'...computed: {  isAuthenticated() {    return authentication.isAuthenticated();  }},methods: {  logOut() {    authentication.signOut();  }}

Add access token to request headers

The below is an example of a vue-resource http interceptor, but any method will do.

Vue.http.interceptors.push(function (request, next) {  auth.acquireToken().then(token => {    // Set default request headers for every request    request.headers.set('Content-Type', 'application/json');    request.headers.set('Ocp-Apim-Subscription-Key', 'api key');    request.headers.set('Authorization', 'Bearer ' + token)    // continue to next interceptor    next();  });});

Hopefully this saves somebody a little time :)


Disclaimer: I am the author of this plugin.

Use vue-adal via npm:

npm install vue-adal

Basic Usage

import Adal from 'vue-adal'Vue.use(Adal, {// This config gets passed along to Adal, so all settings available to adal can be used here.  config: {    // 'common' (multi-tenant gateway) or Azure AD Tenant ID    tenant: '<guid>',    // Application ID    clientId: '<guid>',    // Host URI    redirectUri: '<host addr>',    cacheLocation: 'localStorage'  },  // Set this to true for authentication on startup  requireAuthOnInitialize: true,  // Pass a vue-router object in to add route hooks with authentication and role checking  router: router})```

important: make sure to set the mode on your router to 'history' so that it doesn't use hashes! This will have implications on the serverside.

new Router({  mode: 'history', // Required for Adal library  ... // Rest of router init})

There are more instructions for use on npm, and instructions + a sample on github


I'm not sure that there is a library to help with security for Vue apps. However, we can easily leverage Adal.js for authentication.

I wrote a simple demo for your reference:

Index.html:

<html><head>  <script src="https://unpkg.com/vue"></script>  <script src="node_modules\adal-angular\lib\adal.js"></script>  <script src="config.js"></script>  <script>    var authContext = new AuthenticationContext(config);    function login() {      authContext.login();    }    function init(configOptions) {    if (configOptions) {      // redirect and logout_redirect are set to current location by default      var existingHash = window.location.hash;      var pathDefault = window.location.href;      if (existingHash) {        pathDefault = pathDefault.replace(existingHash, "");      }      configOptions.redirectUri = configOptions.redirectUri || pathDefault;      configOptions.postLogoutRedirectUri =      configOptions.postLogoutRedirectUri || pathDefault;      // create instance with given config    } else {      throw new Error("You must set configOptions, when calling init");    }    // loginresource is used to set authenticated status    updateDataFromCache(authContext.config.loginResource);    }    var _oauthData = {      isAuthenticated: false,      userName: "",      loginError: "",      profile: ""    };    var updateDataFromCache = function(resource) {      // only cache lookup here to not interrupt with events      var token = authContext.getCachedToken(resource);      _oauthData.isAuthenticated = token !== null && token.length > 0;      var user = authContext.getCachedUser() || { userName: "" };      _oauthData.userName = user.userName;      _oauthData.profile = user.profile;      _oauthData.loginError = authContext.getLoginError();    };    function saveTokenFromHash() {      var hash = window.location.hash;      var requestInfo = authContext.getRequestInfo(hash);      if (authContext.isCallback(hash)) {        // callback can come from login or iframe request        var requestInfo = authContext.getRequestInfo(hash);        authContext.saveTokenFromHash(requestInfo);        window.location.hash = "";        if (requestInfo.requestType !== authContext.REQUEST_TYPE.LOGIN) {          authContext.callback = window.parent.AuthenticationContext().callback;        }      }    }    function isAuthenticate() {      return _oauthData.isAuthenticated;    }    saveTokenFromHash();    init(config);  </script></head><body><div id="app">  <p v-if="_oauthData.isAuthenticated">Hello {{ oauthData.userName }}</p>  <button onclick="login()" v-else>Login</button></div><script>  var app = new Vue({    el: "#app",    data: {      oauthData: _oauthData    }  });</script></body></html>

config.js:

var config = {  tenant: 'xxx.onmicrosoft.com',  clientId: '',  redirectUri: '',  cacheLocation: 'localStorage'};