A guide to MSAL authentication in Vue
Architect a Vue JS app secured with Microsoft Authentication Library
Overview
The Microsoft Authentication Library (opens new window) (MSAL) is Microsoft’s own OAuth solution for the Microsoft identity platform that in theory provides a simple and robust login solution. The reality is that getting it working is a cavalcade (opens new window) of (opens new window) pain (opens new window).
In this post I’ll:
- cover a long list of MSAL gotchas
- share a no-fluff, modular MSAL / Vue Demo (opens new window)
- walk through the steps I took to get it working
MSAL gotchas
Note: this post is aimed at frontend developers aiming to implement MSAL in Vue, and assumes you (or a backend team member) have already configured your tenant (opens new window), scopes (opens new window), redirects (opens new window), etc.
If you need some help getting that set up, check out this article (opens new window) which covers how to configure your application in the Azure dashboard.
GitHub repo
The MSAL for JS (opens new window) repo is big; there’s many supported frameworks, lots of code, lots of docs, and lots of samples.
Here’s where to find the good stuff:
+- lib
| +- browser/docs <-- docs on browser wrapper, i.e. MSAL interface, walkthorugh
| +- common/docs <-- docs on common operations, i.e. authorities, requests
| +- core/docs <-- docs on core operations, i.e. scopes, errors
+- samples
+- msal-browser-samples/* <-- vanilla, typescript, vue 3 samples
+- msal-core-samples/* <-- vanilla, react samples
For the platform-specific libraries, check the README
s as well as the docs/*.md
for each, there’s lots of hidden technical information should you wish to dig for it.
Docs
Confusingly, there are several sets of docs…
On the Microsoft Identity Platform (opens new window) site:
- An overview of the library (opens new window)
- An SPA hub (opens new window) which only has code for Angular and React
On the GitHub (opens new window) repo at /lib/<framework>/docs
:
- framework-specific docs for Angular (opens new window) and React (opens new window), hidden deep in the individual wrapper folders
- core docs for the main Browser (opens new window) implementation used for all other frameworks
Samples
Unlike the deeply nested docs, you’ll find the samples (opens new window) placed at the root level of the GitHub repo.
Most of them seem unnecessarily verbose, with concerns such as IE compatibility and 3rd-party UI libraries, with poor modularity, spaghetti abstractions, and obtuse core code; it’s hard to tell how MSAL does its job, and impossible to easily extract to your own code.
There is a single Vue 3 sample (opens new window) in the browser samples folder, but it does too many things and the MSAL code is so deeply entwined with the Vue code, that it’s hard to understand how the two libraries really work together.
If you attempt to extract code to your own application, it seems to blow up in your face (opens new window) for no good reason, and going back to the ambiguous docs and bloated samples makes you question your sanity as a developer (opens new window).
MS Graph and accounts
You’ll see a lot of passing of accounts
and references to Microsoft Graph in the samples (opens new window).
Account data is needed in two places:
- to set an “active” account so tokens can be acquired without passing
account
data each time - if you’re using MS Graph in your app
If you’re not using MS Graph, you can ignore all the callMSGraph()
code; consider it a Microsoft platform-specific implementation of a GET /user/
API which you will of course set up manually in your app.
Configuration and options
There are several classes of configuration and options to think about in MSAL:
auth
- theclientId
,authorities
andscopes
you get from the dashboardredirects
full redirectURI
s that should exactly match those listed in the dashboardlibrary
- additional parameters determining how the library should work (tip: you don’t need many)requests
- options to be passed (at least includingscope
) to login and logout calls
One other point to bear in mind is, MSAL implements OAuth 2, which means that some MSAL options are a superset of OAuth 2 (opens new window) options; worth knowing if auth isn’t your thing, as you may want to google further than just “MSAL”.
Logging and errors
MSAL logging (opens new window) is actually very useful, additionally you can hook into events (opens new window) to see what is being called and when.
Read the console for errors, and the repo’s explanations and resolutions (opens new window) for browser auth errors; they may or may not answer your questions, but good to know it’s there.
Redirect or popup interaction
MSAL supports two authentication “interactions” (opens new window), redirect or popup.
Even though the docs state “It is not recommended to use both interaction types in a single application” (opens new window) every sample demonstrate both interactions in a single codebase, with verbose branching and logic to handle both scenarios.
This is frustrating, as the architecture and code required to trigger and complete both interactions is quite different, which makes the samples feel overwhelming, and grasping what is included vs what is necessary is much more difficult than it should be.
Additionally, redirect flow has specific problems which I’ll cover in the next section.
TL;DR stick to popup interaction if you value your sanity.
Problems specific to redirects
Even though redirect flow is fully supported, there are things you need to know.
Firstly, there are sometimes seemingly unrecoverable MSAL errors (opens new window); these can require session data and sometimes cookies to be cleared in order to continue.
Secondly, MSAL clears the location hash
which will trigger any global Vue Router route guards; this can interrupt any currently-executing guard logic before it has finished.
Finally, redirect flow must return you to a pre-registered URL, which means additional work to if you wanted to go somewhere else. One solution is to pass and receive custom state (opens new window) such as the page path, then handle that in the redirect handler.
Single page applications
MSAL has some quirks regarding routing in SPAs which requires some additional workarounds.
Firstly, when MSAL handles an authentication redirect, it clears the location hash
. This route modification will re-trigger global Vue Router route guards, which can be problematic if a global route guard is already handling the login.
Secondly, when using popup interaction, MSAL will need to hook into your router (opens new window) which it does via a somewhat cumbersome class extension. It can handle internal (SPA routes) and external (navigating to MS Identity platform) and both are specific to chosen interaction type.
Initialising the library
If you’ve read the MSAL docs, you may still not be clear if and when you’re supposed to call handleRedirectPromise()
.
The short answer is, yes, always call it.
The longer answer is:
- call it once
- call it whether you use popup or redirect flows
- call it whether or not you are handling a login redirect
- call it before any other MSAL functions
- call it and await the response
- call it either when
- the page loads
- before you mount your app
- if using route guards, before your first route navigation
The dirty secret seems to be that this is MSAL’s initialization function as well as the function to handle redirects.
Additionally, see the section on redirects and SPAs as the handling function has some side effects.
Required implementation
Note that MSAL handles storage and expiry of the token, so you do not need to manually store or calculate any values yourself.
But there is work to:
- initialize the library
- handle login and logout
- handle the token acquisition flow
- place the token acquisition function
- handle errors
MSAL / Vue Demo
You can find the code for the demo at github.com/davestewart/msal-vue-demo (opens new window).
Aims
Having trawled through the multiple Microsoft samples, and picked and complained my way through the fairly muddled Vue 3 sample (opens new window) I wanted to create a brand-new repo with the following aims:
Regarding authentication:
- use popup interaction to avoid issues with redirects
- use route guards to authenticate routes
Making the code simple:
- put MSAL interaction in one place
- use absolute bare-minimum dependencies, markup and code
- use services and stores to separate logic and state
- use a single interaction type to prevent code bloat
Making the repo transferable:
- use standard vue architecture to make it familiar
- use env files to prevent hard-coding
Architecture
The key parts of this demo are:
- Setup - env variables and config files
- Services - services for logic (api, auth)
- Stores - stores for state (auth, user)
- Router - route guards, authentication and root hooks
Check each of the sections for explanations, with links to lines of code in the repo.
Setup
The demo uses env (opens new window) variables and config (opens new window) files to configure both MSAL and your API.
The repo’s configuration (opens new window) section explains how this all fits together.
Note that MSAL requires very little configuration to get going, really just the auth (opens new window) credentials. The additional logging (opens new window) config can be simplified or omitted (though it is very useful in development).
Services
I like to separate state from logic, and keep the footprint of dependencies within the app as small as possible.
Services (opens new window) are the right place to do this:
- The Auth (opens new window) service encapsulates all MSAL interaction and is called only from auth store (opens new window) and Api (opens new window) service
- The Api (opens new window) service has a minimal fetch implementation that can be called from anywhere (opens new window)
Note that the Auth service’s only public members are:
initialize()
login()
logout()
getToken()
(See the individual methods for comments and info).
Also note the simplicity of the service; it uses the popup interaction only, so no need for branching, alternate logic, config and additional error handling.
Stores
With services doing all the hard work to fetch data, stores (opens new window) are the right place to store it; this way services stay clean, and data is easily accessible from the rest of the application:
- the auth (opens new window) store wraps Auth (opens new window) service calls, and exports reactive state, errors and methods
- the user (opens new window) store manages API calls and stores user data separately from auth
Regarding state, stores should be preferred to services as they are reactive; any change in state triggers updates in views and components.
Router
The Router (opens new window) is where most of the magic happens:
- guarded and unguarded routes are declared
- a single global route guard handles authentication
- a single logic branch handles initialisation
I use some simple helpers (opens new window) to make the routes config more easily readable; note the hook()
helper to handle login and logout.
Note the route guard (opens new window) itself, it handles quite simply the following scenarios:
- 404
- guarded
- initialization
- authorised
- unauthorised
- unguarded
The NavigationClient
required by MSAL to hook into the rooter is implemented as a VueNavigationClient (opens new window) and passed to the store and into the service, in the one-time initialization step.
The class itself has only one method that is used in popup flow, and that is the navigateInternal()
method which converts any full redirect URIs (opens new window) to paths that the Vue Router can safely handle.
Conclusion
Whilst the premise of MSAL is simple, the challenge of implementing it is sadly not.
I hope this repo will go some way to making it clear how logic and state can be separated, and how it’s simpler to see what is going on once that’s the case.
Whilst it may also be possible to successfully implement redirect flow in a clearer and more simplified codebase, I’ll leave that as a challenge to the reader (or may come back to this in future and attempt to implement in another branch).
If you found this post helpful, or you have anything to add which may help others, do please leave a comment.
For now, I’ll just say, happy authenticating!
Addendum
Whilst I’m here, a couple of awesome OAuth 2 resources for reference:
- Understanding OAuth 2 with PKCE in Single-Page Applications (2020) (opens new window)
- OAuth 2 Simplified (opens new window)
Additionally, I’ve been recommended this library if you decide to give up on MSAL altogether (I don’t blame you) which you can use with the Microsoft Identity Platform: