Giter Site home page Giter Site logo

vue-msal's Introduction

vue-msal

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.

Installation

Add the vue-msal dependency to your project using yarn or npm.

npm install vue-msal
or
yarn add vue-msal

Vue Usage

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
})

Nuxt Usage

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' });
  }
}

Plugin usage

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();
        }
    }
});

List of functions

  • 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 to true. Check the Auth Configuration Options below for more details

  • signOut(): Sign out an authorized user
  • isAuthenticated(): Returns true if the user has been authenticated and false 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([request[,retries]]): 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
.
You can also pass in a second parameter, with a number of retries in case of an unexpected failure (i.e. network errors).

  • 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 with cache.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

The 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: Is true if the user has been successfully authenticated and false otherwise.
  • accessToken: The authenticated user's access token. Read below for information on usage.
  • idToken: The authenticated user's id token. Read below for information on usage.
  • user: The user's data provided as a response by the authentication's API call
  • graph: The data provided as a response by the MS Graph API call that runs on initialization when the graph.callAfterInit option is set to true. Check the Calling MS Graph section for more details
  • custom: Whatever data you have saved using the saveCustomData(key, data) function call. (Check the relevant section in the plugin's function list above for more details)

Using accessToken vs idToken

  • accessToken: This token is not validatable outside of MS Graph API and therefore can only be used for MS Graph calls.
  • idToken: This token is validatable and can be used for authentication / authorization with exteral APIs.

Mixin

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 to true 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.

Calling MS Graph

You can directly call the MS Graph API for a logged-in user, with the following methods.

Manually calling the MS Graph

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 the url property of the object type argument)
  • batchUrl: [optional] using this argument you can set a custom URL for this batch call. If this is not set the graph.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> }
        // }
    }
});

Automatically calling the MS Graph on initialization

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 in graph.baseUrl option in graph configuration;
  • force: [optional] If this is set to true, 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).

General notes

OAuth 2.0 and the Implicit Flow

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

Cache Storage

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

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
        //...
    },
});

auth options (*Required)

Option Type Description
clientId string *Required. The clientID of your application, you should get this from the application registration portal.
authority string Your application's authority URL. Check this page for more details.
tenantId (legacy) 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
❗ This option is deprecated and will be removed in next major version. Please use the authority option above instead. You should replace tenantId and tenantName options by adding the authority option with value:
https://{tenantName}/{tenantId}
tenantName (legacy) string This is is the identity provider domain.
Default:login.microsoftonline.com
❗ This option is deprecated and will be removed in next major version. Please use the authority option above instead. You should replace tenantId and tenantName options by adding the authority option with value:
https://{tenantName}/{tenantId}
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 (either the idToken or the accessToken), if this is set to:
false the plugin will set the relevant token to an empty string
true 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)

request options

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"]

graph options

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

cache options

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

system options

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.
Note: Setting this number too high may result in invalid_grant errors (more info here)

framework options

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

Major (breaking) changes

(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

License

MIT License

Copyright (c) Marios Vertopoulos

vue-msal's People

Contributors

bassill avatar mvertopoulos avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

vue-msal's Issues

Accessing msal in vuex store

Hey, i wanted to know if there was a way to access the $msal object outside a Vue component, like in a vuex store for example.
For information, i'm not using Nuxt so this part of documentation won't help me !
Thank you for your work and i hope you will be able to help me with this !

Have a good day

Incorrect discovery endpoint

I have configured options as

new MSAL(
      {
        auth: {
          clientId: 'cfb536c3-057a-4d15-8283-91fcd5101c50',
          // tenantId: '3f4cc564-facc-4cef-bb21-415cf301f9d3',
          tenantId: 'virtualcollegebyankur.onmicrosoft.com',
          tenantName: 'virtualcollegebyankur.b2clogin.com',
          validateAuthority: false,
          redirectUri: 'localhost:3000'
        },
        framework: {
          globalMixin: true
        }
      },

And still it goes to common discovery endpoint

image

It should go to url https://login.microsoftonline.com/virtualcollegebyankur.onmicrosoft.com/v2.0/.well-known/openid-configuration

Am I missing something? Config setting?

Support for Vue3

Is there any plans to update this library to support Vue3? The changes necessary seems minimal

requireAuthOnInitialize not working and no docs on how to use with routes

Certain routes in my application require authentication. And I don't know how to make it work with routes.
So, I set the requireAuthOnInitialize option in the plugin and set it to true, but it doesn't work either.
The browser shows an error when I open it:

image

How can I set authentication per route to avoid visual flick?

[Option 1]

  {
    path: '/explorer',
    name: 'Explorer',
    component: () => import(/* webpackChunkName: "explorer" */ '../views/LogExplorer.vue'),
    beforeRouteEnter(to from, next) {
      if (!this.$msal || !this.$msal.isAuthenticated()) {
        this.$msal.signIn();
      }
    }
  },

It is not possible to use this and vue-msal does not export these methods.

[Option 2]

Vue.use(msal, {
  auth: {
    clientId: process.env.VUE_APP_CLIENT_ID,
    requireAuthOnInitialize: true,
  },
  graph: {
    callAfterInit: false,
  },
});

Dynamic Graph Endpoints

Is it possible to have endpoints in the Graph initialization call that is dynamically constructed out of data received from other graph endpoint calls return values?

An example of this function would be to use the first Graph endpoint call to get the "Groups" a user belongs to and then looping through each group in the array with calls to fetch the "Members" of each group based on a URL endpoint constructed using the "Group.ID" property of each array item.

Or is there another way to execute a method with the same functionality after the call comes back from the Graph initialization using a watcher or something? I'm not having any luck, any help would be appreciated

Invalid tenant id. Tenant have been mutated in lowercase, and this is bad.

Hi @mvertopoulos ,
the tenandId have been converted in a lowercase string. The assignment is correct.

The configuration:
Vue.use(msal, { auth: { clientId: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', tenantId: 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX', redirectUri: 'localhost:8080', validateAuthority: false, } });

Request URL: https://login.microsoftonline.com/xxxxxxxxxxxxxxxxxxxxxxxx/v2.0/.well-known/openid-configuration 400 Bad Request

{ "error":"invalid_tenant", "error_description":"AADSTS90002: Tenant 'xxxxxxxxxxxxx' not found. This may happen if there are no active subscriptions for the tenant. Check to make sure you have the correct tenant ID. Check with your subscription administrator.\r\nTrace ID: **********************************\r\nCorrelation ID: ******************************\r\nTimestamp: 2020-05-08 17:54:51Z", "error_codes":[ 90002 ], "timestamp":"2020-05-08 17:54:51Z", "trace_id":"**********************************", "correlation_id":"***********************************", "error_uri":"https://login.microsoftonline.com/error?code=90002" }

AuthenticationFlow does not start

When storeAuthStateInCookie is enabled and i closed the application for round about 1 hour, it sometimes happens that the authentication flow does not start. When i call this.$msal.isAuthenticated() in the mounted hook of my application, it returns true, but when i take a look to the this.$msal.data the idToken is empty and accessToken is invalid.

The only way to bring my application back working is to clear all cookies and local storage.

Is there somebody who has the same issues?

Create a timer to renew the token automatically when it expires

Quoting from issue #1

The token expiration check is performed on initial page load (where the plugin is initialized), and not when navigating through pages using the vue router. When the token is found to be expired during that check, the user is redirected to the Microsoft login page.
A timer for renewing the token on that scenario should indeed solve the problem.

Errors trying to use vue-msal

I installed vue-msal and all dependencies listed but I get the following errors when trying to follow the examples.

Could not find a declaration file for module 'vue-msal'. '/Vue/mdi-webclient/node_modules/vue-msal/dist/plugin.js' implicitly has an 'any' type.
Try npm install @types/vue-msal if it exists or add a new declaration (.d.ts) file containing declare module 'vue-msal';

This occurs on the line : import msal from 'vue-msal'

I tried adding declaring the module but then I get the error with 'this.$msal' afterwards.

New to vuejs so i'm using latest install.

Please let me know if i'm missing anything.

null_or_empty_id_token errors on newer versions of msal

Vue-msal always targets the latest version of msal, according to its package.json. We didn't know this, so to our surprise, in a new release, our authentication suddenly broke without us changing anything to the authentication. This happened when opening our app, closing the tab, and then opening the app in a new tab again. It would give an authentication error, but it wouldn't reauthenticate automatically.

Turns out that the newer version of msal changed quite a bit in how the cookies and storage caches its data. In a new tab, this causes an error of null_or_empty_id_token if you were already logged in. This is because msal recognizes the user is logged in (it can find the account and access token in the cookie), but it can't access the session storage, as that is unique to every tab. It throws a null_or_empty_id_token, because it tries to look for the id token in the session storage.

This error type is not in the list of "requiresInteraction", so it will just silently fail, and won't try to log back in.
We aren't sure if the problem is that this error should also require interaction, or if the problem is that the app shouldn't think it is logged in when you open a new tab. In other words, we aren't sure if this problem is in msal itself, or in the "requiresInteraction" method.

We solved this by manually resolving an old version of msal, but (for our own reasons) we would really like to use the latest version of msal. Not sure if there's a way for us to temporarily get around this issue, or if we should just wait for this to be fixed. Regardless, we thought it was worth adding this issue here, because as it is now, the interaction between msal and vue-msal seems to be a factor in this issue, even if it turns out that the root of the problem is in msal itself.

the idToken property of both $msal.data and the mixin's data object are an empty string

Expected Behavior

The idToken property of both $msal.data and the msal mixin's data object should both be the correct id token.

Current Behavior

The idToken property of both $msal.data and the msal mixin's data object are both an empty string. However the id token is present in the browses local storage.

Steps to Reproduce

  1. create a new vue project
  2. add vue-msal
  3. use the plugin in a vue component using the clientId, authority and redirectUri of a registered application
  4. add a function to signIn
  5. output $msal.data and the msal mixin's data object; check the browsers local storage

Context

I would like to use the id token as verification for connecting to my api. I do not want to get it directly out of the local storage, as the key name seams to be not consistent.

Can't get it to work with Azure AD B2C

Can someone provide me some help in order to get it to work with Azure AD B2C Login?
The tutorial in the Readme.md doesn't help to get this to work.

I've changed the mode to spa instead of universal and set the clientId in the Msal.js file that I created.

And then I get this in the console:
VM1305:1 GET https://login.microsoftonline.com/common/discovery/instance?api-version=1.0&authorization_endpoint=https://{TENANTNAME}.b2clogin.com/{TENANTNAME}.onmicrosoft.com/{LOGINREGISTERFLOWNAME}/oauth2/v2.0/authorize 400 (Bad Request)

I would also like to have this package to open the login window so I can use NuxtJS still in universal mode if that's possible.

POST-based graph APIs are not supported

I would like to use the graph APIs exposed via vue-msal, but it doesn't support POST-based APIs. For example, the /me/getMemberGroups API (to check for group membership) is a POST-only API, and even has a required body. The current APIs in vue-msal are hard-coded to GET-only:

    createRequest(endpoint, index = 0) {
        const request = {
            url: '',
            method: 'GET',
            id: `defaultID-${index}`
        };
        endpoint = this.getEndpointObject(endpoint);
        if (endpoint.url) {
            Object.assign(request, endpoint);
        }
        else {
            throw ({ error: 'invalid endpoint', endpoint: endpoint });
        }
        return request;
    }

It would also be nice to do this with the "callAfterInit" feature as well.

How to set policy (user flow)?

it would be better to have options like

tenantConfig = {
        tenant: "hellowrold.onmicrosoft.com",
        clientID: '{ your-client-id }',
        signInPolicy: "B2C_1_signin",
        signUpPolicy: "B2C_1_signup",
        redirectUri:"http://localhost:4200",
        b2cScopes:["https://hellowrold.onmicrosoft.com/access-api/user_impersonation"]
 };

Can access token null, because the scope can't read properly

Hello,
There is a question for access token.

My config setting is like that
image

And I got the result with accesstoken and scopes is null, like the image below
image

I noticed that the masl updated, do we need also update the code of vue-msal to match it? Or are there any way to resolve the issue? Thank you
image

ctx.isAuthenticated() is false inside onAuthentication

Hi,

Thanks for putting vue-msal together. It is generally working for me. However I noticed that the isAuthenticated() value is false within the onAuthentication handler:

onAuthentication: (ctx, error, response) => {
  console.log({ ctx, error, response });
  console.log(">>> isAuthenticated = " + ctx.isAuthenticated());
}

With this code I can see there is no error, but isAuthenticated is still false. If I peak at the the response I can see a valid response.

The next strange thing about all of this is if I call ctx.acquireToken() within onAuthentication, the returned promise of acquireToken is simply never resolved:

onAuthentication: (ctx, error, response) =>
{
  console.log({ ctx, error, response });
  console.log(">>> isAuthenticated = " + ctx.isAuthenticated());

  // Nothing is printed beyond this point, even though acquireToken yields a valid promise...
  ctx.acquireToken().then(
    () => {
      console.log(">>> success");
    },
    () => {
      console.log(">>> error");
    },
    () => {
      console.log(">>> rejected");
    });
}

So using vue-msal, what would be an appropriate technique to get the auth token immediately after a successful authentication? I am trying to make my code reactive based on that state. If I wait to call acquireToken, for example, from a click event, everything works. But I need to get the token ASAP.

Thanks!

callback after refreshing expired access token?

I need to set the access token on axios instance, and i'm doing this onAuthentication.
However I can't find a callback when the accessToken is refreshed.

on app mount/create, $msal.data.accessToken is empty.

Nuxt mode requirement

When using nuxt, mode: 'spa' is required in nuxt.config.js. mode: universal won't work.
Although this might be obvious for some, begginers might take a long time to figure this out (as it happened with me).

I suggest this to be added in on the Nuxt Usage section.

postLogoutRedirectUri not wired up by vue-msal

vue-msal documentation indicates it supports 'postLogoutRedirectUri'. Unfortunately, vue-msal has not wired it up like it has other auth properties. I have been able to easily work-around this by simply:

beforeSignOut: function(ctx){
    ctx.lib.config.auth.postLogoutRedirectUri = ctx.auth.postLogoutRedirectUri;
}

Receiving error after changing app to https

I have been using this library successfully with a website running locally on http, I have now updated my redirecturi (et al) to use https, as well as vue cli to https, and I get the following error:

{
    "error": {
        "code": "InvalidAuthenticationToken",
        "message": "CompactToken parsing failed with error code: 80049217",
        "innerError": {
            "date": "2022-05-25T01:13:55",
            "request-id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
            "client-request-id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
        }
    }
}

This was working without issue before switching to https. (redirecturl in AAD being http://localhost:8080/signedin => https://localhost:8080/signedin), and occurs on new browsers, clearing localstorage and cookies, and incognito.

my msal config:

import msal from "vue-msal"
Vue.use(msal, {
  auth: {
    clientId: C.AADappDetails.clientId,
    authority: `https://login.microsoftonline.com/${C.AADappDetails.tenant}/`,
    redirectUri: C.AADappDetails.redirectUri,
    postLogoutRedirectUri: C.AADappDetails.logoutRedirectUri,
    requireAuthOnInitialize: true,
    beforeSignOut:()=>{
      store.commit("auth/resetState")
      console.log("signout")
    },
    onToken:()=>{
        //todo, update vuex
         console.log("ontoken")
    }
  },
  request:{
    scopes: C.AADappDetails.scopes
  },
  framework:{
    globalMixin: true
  },
  graph: {
    callAfterInit: true,
    endpoints: {
      groups: '/me/transitiveMemberOf/microsoft.graph.group?$count=true&$select=id',
      profile: '/me',
      photo: { url: '/me/photo/$value', responseType: 'blob', force: true }
    }
  }
});

and my vue.config.js

const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
  transpileDependencies: true,
  devServer: {
    https: true,
    host: 'localhost',
    port: 8080,
    http2: true, 
    liveReload: true, 
  }
})

Thanks ,
J

Failing on build

Hello,

Thanks for setting this up!

I still new to javascript and I seem to have trouble getting the build to succeed, although I am able to successfully launch the server in dev. I get the error:

ERROR in build.js from UglifyJs Unexpected token: name (msalPlugin) [./node_modules/vue-msal/dist/plugin.js:15,0][build.js:13535,6]

I'm using Vue version 2.5.11.

Don’t include src folder

I have “no implicit any” turned on in typescript and as long as you include your src files (not just .d.ts files) I will get complication errors. Apparently skipLibCheck only applies to .d.ts files and there is no way I can prevent the error by adding the directory to exclude, because I import the module.

Nuxt plugin: window is not defined

When using nuxt dont add '@/plugins/msal' to your nuxt.config.js, instead add { src: '@/plugins/msal', ssr: false }. It will not work when server-side rendered, as there is no window.

Getting 401 request on graph call, something wrong with token

If I use the /me, it is working. If I change the URL to https://graph.microsoft.com/v1.0/me/calendars, it giving a 401 error.

let result = await this.$msal.msGraph('https://graph.microsoft.com/v1.0/me/calendars');

I've done the following steps.

  1. accepted all premission and scopes on user level
  2. generated new tokens, with wait this.$msal.acquireToken({ scopes: ["user.read", "Calendars.ReadWrite", "Calendars.ReadWrite.Shared"] }), still no luck
  3. tokens aren't accepted for the calendar call.
  4. used the newly genereated tokens to do direct API request.
  5. using a token from the https://developer.microsoft.com/en-us/graph/graph-explorer is working
  6. token is showing the right scopes.
  7. changed the 'authority', as I'm a guest user on the tenant where the application is created.

Vue.use(msal, { auth: { clientId: 'a8d208xee-dc-x4x03x8xx-XXXX-fxx17d8e8d0119', requireAuthOnInitialize: true, authority: 'https://login.microsoftonline.com/72046464-XXXX-XXX-85exxc-594e7bb34df4/' }, request: { scopes: ["user.read", "Calendars.ReadWrite", "Calendars.ReadWrite.Shared"] } });

Screenshot 2020-10-07 at 12 19 32

{ "error": { "code": "NoPermissionsInAccessToken", "message": "The token contains no permissions, or permissions can not be understood.", "innerError": { "requestId": "df85ed16-f3a7-49ff-83e4-e62bf0b4fe6d", "date": "2020-10-07T10:38:53", "request-id": "df85ed16-XXXX-49ff-XXX-e62bf0b4fe6d", "client-request-id": "df85ed16-f3a7-XXXX-XXXXX-e62bf0b4fe6d" } } }

Typescript support

Hello, how correctly use this library with typescript? I would like to see vue.$msal.

I'm using like this

import { MSALBasic } from 'vue-msal/lib/src/types'
declare module 'vue/types/vue' {
  interface Vue {
    $msal: MSALBasic
  }
}

Is it correct?

Also if i import vue-msal like this:
import msal from 'vue-msal/lib/plugin'

I got many implicit errors

Typescript support

I'm not sure if it's better to comment on #4, or create a new one - but I, too, am having trouble getting this to work with Typescript.

My main.ts file looks like this:

import Vue from "vue";
import msal from "vue-msal";

Vue.use(msal, {
  auth: {
    clientId: "clientid"
  {
});

new Vue({
    //...
    created() {
        if (!this.$msal.isAuthenticated()) {
            this.$msal.signIn();
        }
    }
});

I get the error message of Could not find a declaration file for module 'vue-msal'. Type 'npm install @types/vue-msal' if it exists or add a new declaration (.d.ts) file containing 'declare module 'vue-msal';'. I have created the file /src/vue-msal.shims.d.ts:

declare module 'vue-msal';
Doing so resolves the first error. However, now I am receiving an error on the this.$msal line of Property '$msal' does not exist on type 'CombinedVueInstance<Vue, Data, Methods, Computed, Readonly<Record<PropNames, any>>>'. Based on a bit of Googling, the solution seems to be "Augmenting Types for Use with Plugins"

I created another shim file /src/vue.msal.shims.d.ts with the content:

import Vue from 'vue';
import msal from 'vue-msal';

declare module 'vue/type/vue'{
  interface Vue{
    $msal: msal;
  }
}

Now I have an issue where the $msal: msal; line in the shims file has an error of Cannot use namespace 'msal' as a type.. At this point I'm lost as to what I need to put in this shim to make everything work. Do you have an pointers that might help?

Thank you.

Get access token for API?

Apologies if this doesn't fit here, but I'm having a hell of a time figuring out how to use this to get a token to call an API. The API is hosted in the same Azure AD tenant as my web app that's using vue-msal. I've been able to authenticate properly but the token audience always comes back as the GUID for MS Graph (00000003-0000-0000-c000-000000000000). All the other info in the token seems correct, but when I pass the access_token as a bearer token to the API, I get a 401 Unauthorized response.

Here's the config I'm using.

Vue.use(msal, {
  auth: {
    clientId: '62...fa', // my API client id
    tenantId: 'f1...ca', // my AD tenant id
    requireAuthOnInitialize: true
  }
});

I'm very inexperienced at auth in general, so I may even be asking the wrong questions. Anyone able to steer me in the right direction here?

Typscript declaration file for module missing

Vue.js run error:

Could not find a declaration file for module 'vue-msal'.
Try npm install @types/vue-msal if it exists or add a new declaration (.d.ts) file containing declare module 'vue-msal';

when running the npm command the following error is displayed.

npm install @types/vue-msal
npm ERR! code E404
npm ERR! 404 Not Found - GET https://registry.npmjs.org/@types%2fvue-msal - Not found
npm ERR! 404
npm ERR! 404 '@types/vue-msal@latest' is not in the npm registry.
npm ERR! 404 You should bug the author to publish it (or use the name yourself!)
npm ERR! 404
npm ERR! 404 Note that you can also install from a
npm ERR! 404 tarball, folder, http url, or git url.

msGraph - post|update requests

$msal works great for me with Graph connections to get data from Sharepoint.

But is there a way how to send a Graph post request or at least to use client for Graph post and update request?

Thanks.

Setting up without vue-msal

It appears obvious that the maintainers of this repo have long abandoned it. It's been nearly 1.5 years since the last update, and Azure's MSAL version is already in 2.18.0.

I recently also tried to set up MSAL for a Vue.js. app and stumbled into this package, as well as the vue-msal-2 package, but had no luck in getting them to work. So, I went and installed the @azure/msal-browser (latest version) and set it up manually, which was relatively easy for an app running on Nuxt.js. You can do this as well. No need for a wrapper. I'll try and write down the steps I took for anyone who stumbles into this repo.

Steps to set up @azure/msal-browser

Initialize

First thing you need to do is initialize MSAL. You need to set up a configuration object and pass it to the msa.PublicClientApplication function. Note that these settings are for my use case, and I will refer you to the msal-browser package documentation to figure out your needs. My use case is I have an Azure AD B2C tenant and an application there for an SPA that uses a redirect method instead of a popup window.

The example below is a nuxt.js plugin I set up in the plugins folder (/plugins/msal.js) which injects the msal into your Nuxt application, making it available there. No need to copy paste the whole thing, just grab what you need here.

import * as msal from "@azure/msal-browser";

export default ({ app }, inject) => {
    const msalConfig = {
        auth: {
            clientId: process.env.OAUTH_CLIENT_ID,
            authority: `https://${process.env.AZURE_B2C_TENANT}.b2clogin.com/${process.env.AZURE_B2C_TENANT}.onmicrosoft.com/${process.env.AZURE_B2C_POLICY}`,
            knownAuthorities: [`${process.env.AZURE_B2C_TENANT}.b2clogin.com`],
            redirectUri: process.env.AZURE_B2C_REDIRECT_URI,
            navigateToLoginRequestUrl: false,
            postLogoutRedirectUri: process.env.AZURE_B2C_TENANT,
        },
        cache: {
            cacheLocation: 'localStorage',
            storeAuthStateInCookie: true,
        }
    }

    const msalInstance = new msal.PublicClientApplication(msalConfig);

   // This is for Nuxt but you can easily pass it to a Vue instance as well using a plugin
    inject('msal', msalInstance);
}

Set up a function to handle the redirect from Azure

I set up a helper function to be called on the sign in process page, which basically handles a redirect promise (if available), fetches the user accounts and makes a silent token request. MSAL holds the token in localStorage (or sessionStorage) so it doesn't have to make trips to the server if the token is still viable.

import { AuthenticationResult,PublicClientApplication } from '@azure/msal-browser';

export function handleLoginRedirect(msal: PublicClientApplication, clientId: string): Promise<AuthenticationResult> {
    return new Promise((resolve, reject) => {
        msal.handleRedirectPromise().then(() => {
            const accounts = msal.getAllAccounts();

            if (accounts.length > 0) {
                msal.setActiveAccount(accounts[0]);

                const request = {
                    scopes: ['openid', 'profile', clientId],
                    account: accounts[0],
                };

                msal.acquireTokenSilent(request).then(tokenResponse => {
                    resolve(tokenResponse);
                }).catch((e) => {
                    reject(e);
                });
            } else {
                reject(new Error('no accounts found'));
            }
        });
    });
}

Once you have a token you can redirect the user to some other page, or if it fails you can throw him back to the login page.

  handleLoginRedirect(app.$msal, this.$config.AZURE_B2C_OAUTH_CLIENT_ID).then(() => {
        return redirect('/dashboard');
    }).catch(async () => {
        await app.$msal.loginRedirect({ scopes: ['openid', 'profile'] });
    });

Auth middleware (optional)

You can set up a middleware for Nuxt.js to make sure there's a viable token in play before allowing users to route to some pages.

import { handleLoginRedirect } from '~/assets/functions/auth';
import { Middleware } from '@nuxt/types';

const authMiddleware: Middleware = ({ app. $config }) => {
    handleLoginRedirect(app.$msal, $config.AZURE_B2C_OAUTH_CLIENT_ID).catch(async () => {
        await app.$msal.loginRedirect({ scopes: ['openid', 'profile'] });
    });
};

export default authMiddleware;

The example above should try and fetch the token and simply ignore it's response, but in case there's no token or account, msal will throw an error and you can redirect the user to a login page.

I hope that helps and people won't waste time on this repo!

router

Hi all, two questions..

what is the best way to integrate this functionality into the router. Its current implementation seems to really position it as view/component level authentication. I'm not quite clear on the best way to call the signin/signout/isAuthenticated as part of beforeEnter calls pre routes.

also, any recommendations on how to handle the returned href. Are people creating a route to process the returned query string with the token?

Ability to override options in signIn() calls

Hi @mvertopoulos
Nice work with the lib! However, I miss an ability to override options for authentication requests (signIn method).
My case is using AD B2C and necessity to invoke different policies (user flows). As you likely know, for B2C a policy name is embedded into authority URL. Thus ultimately I need to pass different authority value for loginRedirect() call of MSAL object which is possible using their request parameter. However, vue-msal lacks this capability in signIn() method.
The most straightforward solution would be to add a request parameter to signIn() which is simply forwarded to loginRedirect() of underlying msal object. Similarly to that existing in acquireToken() method. If you agree with such implementation I can make a pull request with these changes.
As a side note. What was the reasoning for splitting original authority option from msal into tenantName and tenantId? IMO it makes some confusion for users of msal. I needed to look into source code to figure out which part of authority to place in which of those options.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.