Wrapper of MSAL.js (Microsoft Authentication Library) for usage in Vue.
The vue-msal library enables client-side vue applications, running in a web browser, to authenticate users using Azure AD work and school accounts (AAD), Microsoft personal accounts (MSA) and social identity providers like Facebook, Google, LinkedIn, Microsoft accounts, etc. through Azure AD B2C service. It also enables your app to access Microsoft Cloud services such as Microsoft Graph.
Add the vue-msal
dependency to your project using yarn or npm.
npm install vue-msal
or
yarn add vue-msal
Use the plugin in your vue instance like this
import msal from 'vue-msal'
Vue.use(msal, {
auth: {
clientId: '<YOUR CLIENT ID HERE>'
}
});
new Vue({
//... vue options
})
Add a new javascript file like msal.js
under /plugins/
directory with the following content
❕ Note: you should add Vue as a second argument to the constructor if you want to add the global mixin automatically with the
framework.globalMixin
option. Check the mixin section below for more information
import Vue from 'vue' //import Vue if you want to use the framework.globalMixin option
import MSAL from 'vue-msal'
export default ({ app, error, $axios }, inject) => {
inject('msal', new MSAL(
{
auth: {
clientId: '<YOUR CLIENT ID HERE>'
}
}, Vue /* [optional] should be passed as an argument if you want to the framework.globalMixin option*/
))
}
Then include it to the plugins array in nuxt.config.js
like this
export default {
plugins: [
//...
'@/plugins/msal'
//...
]
}
This will make the $msal
object available in both the vue instances and the context. For example you can access it in the context of a middleware via the app object like this:
export default function ({ app, route, error }) {
// If the user is not authenticated and he's not in the /login page throw Error
if (!app.$msal.isAuthenticated() && route.name !== 'login') {
error({ statusCode: 401, message: 'Unauthorized' });
}
}
When the plugin is initialized it exposes its context to vm.$msal
(where vm
refers to the Vue's scope) so you can, for example, call the signIn method like this:
new Vue({
//...
created() {
if (!this.$msal.isAuthenticated()) {
this.$msal.signIn();
}
}
});
signIn()
: Start the sign-in process manually
❕ Note: you can also start the process automatically in case the user needs to be authorized in all pages by setting the option
auth.requireAuthOnInitialize
totrue
. Check the Auth Configuration Options below for more details
signOut()
: Sign out an authorized userisAuthenticated()
: Returnstrue
if the user has been authenticated andfalse
otherwise.
❕ Note: This function should not be used for reactivity. In order to watch whether the user is authenticated or not you should use the mixin data properties below.
acquireToken()
: Acquire an access token manually.
❕ Note: This will also run automatically after the user's successful authentication using the default permissions defined in the
auth.scopes
property of the configuration options. You should however run this manually in case you want to get an access token with more permissions than the default, by adding the new request options as an argument, like this
acquireToken({scopes: ["user.read", "another.permission"]})
Check the Request Configuration Options below for more details
❕ *Note: If requesting an additional access token for an API other than MS Graph, use the optional disableTokenCache argument. This maintains the integrity of the cached MS Graph access token while still returning the requested access token. Example:
acquireToken({scopes: ["api://cf5774e9-6d41-4ed2-944c-69b91bcf83cc/user_impersonation"]}, true)
msGraph(endpoints[,batchUrl])
: Manually call the MS Graph API using the acquired access token.
❕ Note: Read the Calling MS Graph section for more details
saveCustomData(key, data)
: You can use this function to add custom data to the selected cache location (set withcache.cacheLocation
in the configuration options), that will be automatically deleted when the user signs out or his access token expires. This should be used, for example, to store any user related data fetched from another API.
❕ Note: You can read this data without reactivity from the data object or with reactivity by watching the
msal.custom
property of the mixin's data object
You can access the data object that contains all of the user related data using the $msal.data
object which is available in nuxt's context. However in case you want reactivity for this data, it is recomended that you use the mixin method below.
The properties provided in the data object are the following:
isAuthenticated
: Istrue
if the user has been successfully authenticated andfalse
otherwise.accessToken
: The authenticated user's access tokenuser
: The user's data provided as a response by the authentication's API callgraph
: The data provided as a response by the MS Graph API call that runs on initialization when thegraph.callAfterInit
option is set to true. Check the Calling MS Graph section for more detailscustom
: Whatever data you have saved using thesaveCustomData(key, data)
function call. (Check the relevant section in the plugin's function list above for more details)
All user related data can be exposed via a mixin in the msal
data property so that you can have access to it like you would normally access any of the component's data properties with reactivity.
❗ Notice that the dollar sign ($) is missing here. While
this.$msal
refers to the plugin's exposed object,this.msal
refers to the mixin's data object. Be careful not to confuse these two.
So for example you can do this:
<div id="demo">
<div v-if="user">
<div>Welcome {{user.name}}</div>
<div v-if="user.profile.jobTitle">Your job title is {{user.profile.jobTitle}}</div>
<div><button @click="$msal.signOut()">logout</button></div>
</div>
<div v-else>
Please sign-in
<button @click="$msal.signIn()"></button>
</div>
</div>
<script>
//Importing the mixin locally (omit the following line if you are using the 'framework.globalMixin' option)
import { msalMixin } from 'vue-msal';
new Vue({
el: '#demo',
//Importing the mixin locally (omit the following line if you are using the 'framework.globalMixin' option)
mixins: [msalMixin],
computed: {
user() {
let user = null;
if (this.msal.isAuthenticated) { // Note that the dollar sign ($) is missing from this.msal
user = {
...this.msal.user,
profile: {}
}
if (this.msal.graph && this.msal.graph.profile) {
user.profile = this.msal.graph.profile
}
}
return user;
}
}
});
</script>
❗ Note: In case you want to import the mixin globally instead of importing it to specific vue instances you can do so by simply setting the
framework.globalMixin
totrue
in the Framework Configuration Options. This will automatically add the mixin to all vue instances so that you have out-of-the-box access to the msal object. In nuxt you must also add the Vue object as an argument to the plugin's initialization for this to work. Check the nuxt usage section for details.
You can directly call the MS Graph API for a logged-in user, with the following methods.
In order to manually call the MS Graph API you can use the $msal.msGraph(endpoints[,batchUrl])
function that will automatically use the access token set for the logged in user.
This function receives the following arguments:
endpoints
: [required] This can be either a single value for a single request to the API, or an array of values for a batch request to the API. Each value can be either:- An
object
containing the following properties:url
: [required] This can either be:- A Full URL (starting with 'http...') in case of a single request (this is invalid for batch requests)
- The URI part (i.e. /me), which must be used for batch requests but can also be used for single requests (in which case the full URL will be composed using the value of
graph.baseUrl
option from the Graph Configuration Options as the Base URL).
id
: [optional] setting this to a string will result to returning a keyed object instead of an array containing the responses of a batch request. This property is ignored for single requests.- Any other optional property from the Axios Request Configuration
- A
string
containing only the url (following the same rules as theurl
property of the object type argument)
- An
batchUrl
: [optional] using this argument you can set a custom URL for this batch call. If this is not set thegraph.baseUrl
option from the Graph Configuration Options will be used as the Batch URL. This argument is ignored for single requests.
The response of this call depends on the arguments passed to it.
- For a single request, it returns the response object (with properties: status, headers, body)
- For a batch request:
- with an array of URIs passed as strings in the endpoints argument, it will return an array of response objects that match the URI's index.
- with an array of objects containing an id, it will return an object keyed with those ids containing the response object.
Example usage:
new Vue({
//...
async mounted() {
let result;
result = await app.$msal.msGraph('https://www.example.com/1.0/me');
// Single request at: https://www.example.com/1.0/me
// Returns: { status: <number>, headers: <object>, body: <object> }
result = await app.$msal.msGraph('/me');
// Single request at: graph.baseUrl + '/me'
// Returns: { status: <number>, headers: <object>, body: <object> }
await app.$msal.msGraph(['/me', '/me/messages']);
// Batch request at: graph.baseUrl for endpoints '/me' & '/me/messages'
// Returns: [
// { status: <number>, headers: <object>, body: <object> },
// { status: <number>, headers: <object>, body: <object> }
// ]
await app.$msal.msGraph(['/me', '/me/messages'], 'https://www.custom-msgraph-url.com');
// Batch request at: 'https://www.custom-msgraph-url.com' for endpoints '/me' & '/me/messages'
// Returns: [
// { status: <number>, headers: <object>, body: <object> },
// { status: <number>, headers: <object>, body: <object> }
// ]
await app.$msal.msGraph([{ url: '/me'}, { url: '/me/photo/$value', responseType: 'blob' }]);
// Batch request at: graph.baseUrl for endpoints '/me' & '/me/photo/$value'
// Returns: [
// { status: <number>, headers: <object>, body: <object> },
// { status: <number>, headers: <object>, body: <object> }
// ]
await app.$msal.msGraph([{ url: '/me', id: 'profile'}, { url: '/me/photo/$value', id: 'photo', responseType: 'blob' }]);
// Batch request at: graph.baseUrl for endpoints '/me' & '/me/photo/$value'
// Returns: {
// profile: { status: <number>, headers: <object>, body: <object> },
// photo: { status: <number>, headers: <object>, body: <object> }
// }
await app.$msal.msGraph(['/me', { url: '/me/photo/$value', id: 'photo', responseType: 'blob' }]);
// Batch request at: graph.baseUrl in endpoints '/me' & '/me/photo/$value'
// Returns: {
// 0: { status: <number>, headers: <object>, body: <object> },
// photo: { status: <number>, headers: <object>, body: <object> }
// }
}
});
You can also call the MS Graph API on initialization (in case the user is logged-in) by setting the graph.callAfterInit
option to true in the Graph Configuration Options.
You can assign the endpoints to be called in an object with keys like this:
{
// Configuration options
graph: {
callAfterInit: true,
endpoints: {
// ...
// 'key' : endpoint
// ...
profile: '/me',
photo: { url: '/me/photo/$value', responseType: 'blob', force: true }
}
}
}
This will create an object with the body of each result assigned to its respective key. You can get the result in vm.msal.graph
data object (using the mixin) or in vm.$msal.data.graph
. The results are also cached to the storage you have selected (see cache options) unless the force
option has been set to true in an endpoint (see bellow).
The endpoints that can be passed as a value to that object can have any of the formats described in the manual call. However the object format can also have two extra properties:
batchUrl
: [optional] If this option is set to a URL string, the endpoint will be grouped with any other endpoints that have the same batchUrl and the actual call to the API will be a batch call. You can also set this to'default'
(as a string) in which case it will be executed as a batch request to the URL set ingraph.baseUrl
option in graph configuration;force
: [optional] If this is set totrue
, the result of this endpoint will not be read from / written to the cache. All other endpoints that don't have this option set to true will be cached, but this will be executed on every initialization. You should use this option for any result that cannot be encoded to JSON (like a blob for example).
Msal implements the Implicit Grant Flow, as defined by the OAuth 2.0 protocol and is OpenID compliant.
Our goal is that the library abstracts enough of the protocol away so that you can get plug and play authentication, but it is important to know and understand the implicit flow from a security perspective. The implicit flow runs in the context of a web browser which cannot manage client secrets securely. It is optimized for single page apps and has one less hop between client and server so tokens are returned directly to the browser. These aspects make it naturally less secure. These security concerns are mitigated per standard practices such as- use of short lived tokens (and so no refresh tokens are returned), the library requiring a registered redirect URI for the app, library matching the request and response with a unique nonce and state parameter.
❗ Please check this article for details on how to enable implicit grant flow for your project
We offer two methods of storage for Msal, localStorage
and sessionStorage
. Our recommendation is to use sessionStorage
because it is more secure in storing tokens that are acquired by your users, but localStorage
will give you Single Sign On across tabs and user sessions. We encourage you to explore the options and make the best decision for your application.
Configuration options are organized into groups like this
Vue.use(msal, {
auth: { //Group
clientId: '<YOUR CLIENT ID HERE>', //Option 1
tenantId: '<YOUR TENANT ID HERE>', //Option 2
//...
},
request: { //Group
//...
},
cache: { //Group
//...
},
system: { //Group
//...
},
framework: { //Group
//...
},
});
Option | Type | Description |
---|---|---|
clientId | string |
*Required. The clientID of your application, you should get this from the application registration portal. |
tenantId | string |
This is an identifier representing the sign-in audience. Can be:common : - Used to sign in users with work and school accounts or a Microsoft personal account.organizations - Used to sign in users with work and school accounts.consumers - Used to sign in users with only personal Microsoft account (live)or <Tenant ID> from Azure AD.Default: common |
tenantName | string |
This is is the identity provider domain. Default: login.microsoftonline.com |
validateAuthority | boolean |
Validate the issuer of tokens. For B2C applications, since the authority value is known and can be different per policy, the authority validation will not work and has to be set to false. Default: true |
redirectUri | string | (() => string) |
The redirect URI of your app, where authentication responses can be sent and received by your app. It must exactly match one of the redirect URIs you registered in the portal. Default: window.location.href . |
postLogoutRedirectUri | string | (() => string) |
Redirects the user to postLogoutRedirectUri after sign out. Default: redirectUri (the previous option) |
navigateToLoginRequestUrl | boolean |
Ability to turn off default navigation to start page after login. Default: true |
requireAuthOnInitialize | boolean |
Setting this to true will automatically require authentication right after the plugin has been initialized Default: false |
autoRefreshToken | boolean |
When a token expires, if this is set to:false the plugin will set the accessToken to an empty stringtrue the plugin will automatically attempt to renew the token❕ Note: Expiration time includes the tokenRenewalOffsetSeconds value set in System Options Default: true |
onAuthentication | (ctx, error, response) => any |
Callback function to be executed after authentication request. Function's arguments are: ctx - the msal class's context (vm.$msal)error - request error (=null if request was successful)response - request's result (=null if request was unsuccessful) |
onToken | (ctx, error, response) => any |
Callback function to be executed after token request. Function's arguments are: ctx - the msal class's context (vm.$msal)error - request error (=null if request was successful)response - request's result (=null if request was unsuccessful) |
beforeSignOut | (ctx) => any |
Callback function to be executed before manual sign out. Function's arguments are: ctx - the msal class's context (vm.$msal) |
Option | Type | Description |
---|---|---|
scopes | string[] |
An array of strings representing the scopes that will be used for the Sign In request and the default Acquire Token request Default: ["user.read"] |
Option | Type | Description |
---|---|---|
callAfterInit | boolean |
Setting this to true will automatically call vm.$msal.callMSGraph() once the user has been authenticated.Default: false |
endpoints | object |
Please check the endpoint options in the Automatically calling the MS Graph on initialization section. Default: {profile: '/me'} |
baseUrl | string |
The default URL to be used when no full URL is set in single requests or no batch URL is set in batch requests. Default: 'https://graph.microsoft.com/v1.0' |
onResponse | (ctx, response) => any |
Callback function called when a response has been received from the graph call. Function's arguments are: ctx - the msal class's context (vm.$msal)response - the graph call's response |
Option | Type | Description |
---|---|---|
cacheLocation | "localStorage" | "sessionStorage" |
Sets browser storage to either localStorage or sessionStorage .Default: localstorage |
storeAuthStateInCookie | boolean | This flag was introduced in MSAL.js v0.2.2 as a fix for the authentication loop issues on Microsoft Internet Explorer and Microsoft Edge. Set this flag to true to take advantage of this fix. When this is enabled, MSAL.js will store the auth request state required for validation of the auth flows in the browser cookies.Default: true |
Option | Type | Description |
---|---|---|
logger | Logger object | A Logger object with a callback instance that can be provided by the developer to consume and publish logs in a custom manner. For details on passing logger object, see logging with msal.js. |
loadFrameTimeout | number | The number of milliseconds of inactivity before a token renewal response from Azure AD should be considered timed out. Default: 6000 . |
tokenRenewalOffsetSeconds | number | The number of milliseconds which sets the window of offset needed to renew the token before expiry. Default: 300 . |
Option | Type | Description |
---|---|---|
globalMixin | boolean |
Setting this to true will add a mixin with the msal data object to ALL vue instances. Check the Mixin section for more information Default: false |
(2.x.x) to (3.x.x): Added timer for automatically changing the accessToken on expiration
(1.x.x) to (2.x.x): Changed the methods used for accessing the MS Graph API
Copyright (c) Marios Vertopoulos