/vuegraph

Vue/Vuetify and Microsoft Graph using MSAL.js

Primary LanguageTypeScript

VueGraph

A simple Vue (and Vuetify) starter app that authenticates to AzureAD to make calls to ASP.NET REST and Microsoft Graph APIs with OAuth2 tokens.

How it works

This project doesn't bundle external libraries into the webpack build. It loads the scripts directly from the npm packages (see the ./Pages/Index.cshtml file). This results in super fast build times for webpack, smaller bundles to download from the server, and better cache usage on the client (as when the app bundle is rebuilt, the cached libraries from the CDN don't need to be reloaded).

Loading scripts means globals such as Vue, Vuetify, msal, and axios are implicitly in scope and don't need to be imported. See the shims.d.ts file for how this is set up for TypeScript.

When running in Production mode, the total download for the app is under 800kb, with only around 80kb (unminified) being loaded from the host server, and the rest being 3rd party packages loaded from the CDN.

Webpack builds times are around 4 seconds from cold (on my laptop), and around 250ms when building changes in --watch mode.

Auth flows

As written, this app expects a user to have logged in with AzureAD. On site load if there is no active account then it will redirect the user to the sign-in page at login.microsoftonline.com (see requireAccount call in ./src/main.ts).

Once a user is signed in, further token acquisition may be attempted silently. Occasionally this can fail if tokens have expired or user consent is needed for additional permissions. Ideally this would show a popup to the user, however this can be problematic as the network response is asynchronous, and popups are blocked by browsers unless the result of direct user interaction (e.g. by a click on the page). Doing a redirect is undesirable, as the page may contain unsaved changes or other state that could be lost on performing the redirect flow.

The solution to this here is to use a modal overlay on the app on token failure due to the need for user interaction, which the user must click on to acknowledge, which then triggers the popup flow to authenticate with AzureAD. In pseudo-code this is something like:

async function acquireToken(scopes: string[]) : Promise<Token> {
  try {
    let authResult = await msal.acquireTokenSilent(scopes);
    return authResult.Token;
  }
  catch (err) {
    if (err.name !== "InteractionRequired") throw err;

    let clickHandler = () => msal.acquireTokenPopup(scopes);

    // The below will run the click handler synchronously as a result of the
    // "Sign in" button in the auth modal dialog being clicked.
    let popupResult = await authPopupDialog(clickHandler);
    return popupResult.Token;
  }
}

(The actual code is a little more complex. See aquireTokenSilentOrPopup in ./src/auth.ts)

Note: Be sure to register the popup redirect URL /popupRedirect.html as a reply URL for the app, as well as the root of the origin (e.g. https://example.com/). This demo also requires an 'access_as_user' scope be registered for the app. See the start of ./src/auth.ts.

How this was authored

Install the Vue libraries with:

npm install vue vue-router vuetify

Install the client side AzureAD and REST request helper with:

npm install @azure/msal-browser axios

Install the TypeScript support with:

npm install --save-dev typescript vue-class-component vue-property-decorator

Install the webpack build tooling with:

npm install --save-dev webpack webpack-cli vue-loader vue-template-compiler ts-loader mini-css-extract-plugin

Add a webpack.config.js file as per this project for Vue, TypeScript, and CSS build support.

Likewise see the tsconfig.json at the root of this project for the minimal settings needed.

Add the below to package.json to build with webpack via npm run build

  "scripts": {
    "build": "webpack",
    "build:watch": "webpack --watch"
  },

Add <TypeScriptCompileBlocked>true</TypeScriptCompileBlocked> to the .csproj file to only compile the TypeScript code via webpack, not Visual Studio.

Links

The below links may be useful for more information:

TODO

  • Add an authenticated REST API on the controllers
  • Add the Account page
  • Add the ability to search Graph for users
  • Detail setting the user secret via dotnet user-secrets set "AzureAd:ClientSecret" "bc5412...."
    • This may not be required if the API is just validating the token (and not request or exchanging tokens).