Call the Microsoft Graph API from a JavaScript Single Page Application (SPA)

This guide demonstrates how a JavaScript Single Page Application (SPA) can sign in personal, work and school accounts, get an access token, and call the Microsoft Graph API or other APIs that require access tokens from the Azure Active Directory v2 endpoint.

How this guide works

How the sample app generated by this guide works

More Information

The sample application created by this guide enables a JavaScript SPA to query the Microsoft Graph API or a Web API that accepts tokens from Azure Active Directory v2 endpoint. For this scenario, after a user signs-in, an access token is requested and added to HTTP requests via the authorization header. Token acquisition and renewal are handled by the Microsoft Authentication Library (MSAL).

Libraries

This guide uses the following library:

Library Description
msal.js Microsoft Authentication Library for JavaScript Preview
Note

msal.js targets the Azure Active Directory v2 endpoint - which enables personal, school and work accounts to sign in and acquire tokens. The Azure Active Directory v2 endpoint has some limitations. If you are interested only in school and work accounts, use adal.js and the V1 endpoint. To understand differences between the v1 and v2 endpoints read the v1-v2 comparison.

Setting up your web server or project

Prefer to download this sample's project instead?

or

And then skip to the Configuration step to configure the code sample before executing it.

Prerequisites

A local web server such as Python http.server, http-server, .NET Core, or IIS Express integration with Visual Studio 2017 is required to run this guided setup.

Instructions in this guide are based on both Python and Visual Studio 2017, but feel free to use any other development environment or Web Server.

Create your project

Option 1: Visual Studio

If you are using Visual Studio and are creating a new project, follow the steps below to create a new Visual Studio solution:

  1. In Visual Studio: File > New > Project
  2. Under Visual C#\Web, select ASP.NET Web Application (.NET Framework)
  3. Name your application and click OK
  4. Under New ASP.NET Web Application, select Empty

Option 2: Python/ other web servers

Make sure you have installed Python, then follow the step below:

  • Create a folder to host your application.

Create your single page application’s UI

  1. Create an index.html file for your JavaScript SPA. If you are using Visual Studio, select the project (project root folder), right click and select: Add > New Item > HTML page and name it index.html
  2. Add the following code to your page:

    <!DOCTYPE html>
    <html>
    <head>
    <!-- bootstrap reference used for styling the page -->
    <link href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
    <title>JavaScript SPA Guided Setup</title>
    </head>
    <body style="margin: 40px">
    <button id="callGraphButton" type="button" class="btn btn-primary" onclick="callGraphApi()">Call Microsoft Graph API</button>
    <div id="errorMessage" class="text-danger"></div>
    <div class="hidden">
        <h3>Graph API Call Response</h3>
        <pre class="well" id="graphResponse"></pre>
    </div>
    <div class="hidden">
        <h3>Access Token</h3>
        <pre class="well" id="accessToken"></pre>
    </div>
    <div class="hidden">
        <h3>ID Token Claims</h3>
        <pre class="well" id="userInfo"></pre>
    </div>
    <button id="signOutButton" type="button" class="btn btn-primary hidden" onclick="signOut()">Sign out</button>
    
    <!-- This app uses cdn to reference msal.js (recommended). 
         You can also download it from: https://github.com/AzureAD/microsoft-authentication-library-for-js -->
    <script src="https://secure.aadcdn.microsoftonline-p.com/lib/0.1.1/js/msal.min.js"></script>
    
    <!-- The 'bluebird' and 'fetch' references below are required if you need to run this application on Internet Explorer -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/bluebird/3.3.4/bluebird.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/fetch/2.0.3/fetch.min.js"></script>
    
    <script type="text/javascript" src="msalconfig.js"></script>
    <script type="text/javascript" src="app.js"></script>
    </body>
    </html>
    

Use the Microsoft Authentication Library (MSAL) to sign-in the user

  1. Create a file named app.js. If you are using Visual Studio, select the project (project root folder), right click and select: Add > New Item > JavaScript File:
  2. Add the following code to your app.js file:
// Graph API endpoint to show user profile
var graphApiEndpoint = "https://graph.microsoft.com/v1.0/me";

// Graph API scope used to obtain the access token to read user profile
var graphAPIScopes = ["https://graph.microsoft.com/user.read"];

// Initialize application
var userAgentApplication = new Msal.UserAgentApplication(msalconfig.clientID, null, loginCallback, {
    redirectUri: msalconfig.redirectUri
});

//Previous version of msal uses redirect url via a property
if (userAgentApplication.redirectUri) {
    userAgentApplication.redirectUri = msalconfig.redirectUri;
}

window.onload = function () {
    // If page is refreshed, continue to display user info
    if (!userAgentApplication.isCallback(window.location.hash) && window.parent === window && !window.opener) {
        var user = userAgentApplication.getUser();
        if (user) {
            callGraphApi();
        }
    }
}

/**
 * Call the Microsoft Graph API and display the results on the page. Sign the user in if necessary
 */
function callGraphApi() {
    var user = userAgentApplication.getUser();
    if (!user) {
        // If user is not signed in, then prompt user to sign in via loginRedirect.
        // This will redirect user to the Azure Active Directory v2 Endpoint
        userAgentApplication.loginRedirect(graphAPIScopes);
        // The call to loginRedirect above frontloads the consent to query Graph API during the sign-in.
        // If you want to use dynamic consent, just remove the graphAPIScopes from loginRedirect call.
        // As such, user will be prompted to give consent when requested access to a resource that 
        // he/she hasn't consented before. In the case of this application - 
        // the first time the Graph API call to obtain user's profile is executed.
    } else {
        // If user is already signed in, display the user info
        var userInfoElement = document.getElementById("userInfo");
        userInfoElement.parentElement.classList.remove("hidden");
        userInfoElement.innerHTML = JSON.stringify(user, null, 4);

        // Show Sign-Out button
        document.getElementById("signOutButton").classList.remove("hidden");

        // Now Call Graph API to show the user profile information:
        var graphCallResponseElement = document.getElementById("graphResponse");
        graphCallResponseElement.parentElement.classList.remove("hidden");
        graphCallResponseElement.innerText = "Calling Graph ...";

        // In order to call the Graph API, an access token needs to be acquired.
        // Try to acquire the token used to query Graph API silently first:
        userAgentApplication.acquireTokenSilent(graphAPIScopes)
            .then(function (token) {
                //After the access token is acquired, call the Web API, sending the acquired token
                callWebApiWithToken(graphApiEndpoint, token, graphCallResponseElement, document.getElementById("accessToken"));

            }, function (error) {
                // If the acquireTokenSilent() method fails, then acquire the token interactively via acquireTokenRedirect().
                // In this case, the browser will redirect user back to the Azure Active Directory v2 Endpoint so the user 
                // can reenter the current username/ password and/ or give consent to new permissions your application is requesting.
                // After authentication/ authorization completes, this page will be reloaded again and callGraphApi() will be executed on page load.
                // Then, acquireTokenSilent will then get the token silently, the Graph API call results will be made and results will be displayed in the page.
                if (error) {
                    userAgentApplication.acquireTokenRedirect(graphAPIScopes);
                }
            });

    }
}

/**
 * Callback method from sign-in: if no errors, call callGraphApi() to show results.
 * @param {string} errorDesc - If error occur, the error message
 * @param {object} token - The token received from login
 * @param {object} error - The error string
 * @param {string} tokenType - the token type: usually id_token
 */
function loginCallback(errorDesc, token, error, tokenType) {
    if (errorDesc) {
        showError(msal.authority, error, errorDesc);
    } else {
        callGraphApi();
    }
}

/**
 * Show an error message in the page
 * @param {string} endpoint - the endpoint used for the error message
 * @param {string} error - Error string
 * @param {string} errorDesc - Error description
 */
function showError(endpoint, error, errorDesc) {
    var formattedError = JSON.stringify(error, null, 4);
    if (formattedError.length < 3) {
        formattedError = error;
    }
    document.getElementById("errorMessage").innerHTML = "An error has occurred:<br/>Endpoint: " + endpoint + "<br/>Error: " + formattedError + "<br/>" + errorDesc;
    console.error(error);
}

More Information

After a user clicks the ‘Call Microsoft Graph API’ button for the first time, callGraphApi method calls loginRedirect to sign in the user. This method results in redirecting the user to the Microsoft Azure Active Directory v2 endpoint to prompt and validate the user's credentials. As a result of a successful sign-in, the user is redirected back to the original index.html page, and a token is received, processed by msal.js and the information contained in the token is cached. This token is known as the ID token and contains basic information about the user, such as the user display name. If you plan to use any data provided by this token for any purposes, you need to make sure this token is validated by your backend server to guarantee that the token was issued to a valid user for your application.

The SPA generated by this guide does not make use directly of the ID token – instead, it calls acquireTokenSilent and/or acquireTokenRedirect to acquire an access token used to query the Microsoft Graph API. If you need a sample that validates the ID token, take a look at this sample application in GitHub – the sample uses an ASP.NET Web API for token validation.

Getting a user token interactively

After the initial sign-in, you do not want the ask users to reauthenticate every time they need to request a token to access a resource – so acquireTokenSilent should be used most of the time to acquire tokens. There are situations however that you need to force users interact with Azure Active Directory v2 endpoint – some examples include:

  • Users may need to reenter their credentials because the password has expired
  • Your application is requesting access to a resource that the user needs to consent to
  • Two factor authentication is required

Calling the acquireTokenRedirect(scope) result in redirecting users to the Azure Active Directory v2 endpoint (or acquireTokenPopup(scope) result on a popup window) where users need to interact with by either confirming their credentials, giving the consent to the required resource, or completing the two factor authentication.

Getting a user token silently

The acquireTokenSilent method handles token acquisitions and renewal without any user interaction. After loginRedirect (or loginPopup) is executed for the first time, acquireTokenSilent is the method commonly used to obtain tokens used to access protected resources for subsequent calls - as calls to request or renew tokens are made silently. acquireTokenSilent may fail in some cases – for example, the user's password has expired. Your application can handle this exception in two ways:

  1. Make a call to acquireTokenRedirect immediately, which results in prompting the user to sign in. This pattern is commonly used in online applications where there is no unauthenticated content in the application available to the user. The sample generated by this guided setup uses this pattern.

  2. Applications can also make a visual indication to the user that an interactive sign-in is required, so the user can select the right time to sign in, or the application can retry acquireTokenSilent at a later time. This is commonly used when the user can use other functionality of the application without being disrupted - for example, there is unauthenticated content available in the application. In this case, the user can decide when they want to sign in to access the protected resource, or to refresh the outdated information.

Call the Microsoft Graph API using the token you just obtained

Add the following code to your app.js file:

/**
 * Call a Web API using an access token.
 * @param {any} endpoint - Web API endpoint
 * @param {any} token - Access token
 * @param {object} responseElement - HTML element used to display the results
 * @param {object} showTokenElement = HTML element used to display the RAW access token
 */
function callWebApiWithToken(endpoint, token, responseElement, showTokenElement) {
    var headers = new Headers();
    var bearer = "Bearer " + token;
    headers.append("Authorization", bearer);
    var options = {
        method: "GET",
        headers: headers
    };

    fetch(endpoint, options)
        .then(function (response) {
            var contentType = response.headers.get("content-type");
            if (response.status === 200 && contentType && contentType.indexOf("application/json") !== -1) {
                response.json()
                    .then(function (data) {
                        // Display response in the page
                        console.log(data);
                        responseElement.innerHTML = JSON.stringify(data, null, 4);
                        if (showTokenElement) {
                            showTokenElement.parentElement.classList.remove("hidden");
                            showTokenElement.innerHTML = token;
                        }
                    })
                    .catch(function (error) {
                        showError(endpoint, error);
                    });
            } else {
                response.json()
                    .then(function (data) {
                        // Display response as error in the page
                        showError(endpoint, data);
                    })
                    .catch(function (error) {
                        showError(endpoint, error);
                    });
            }
        })
        .catch(function (error) {
            showError(endpoint, error);
        });
}

More information on making a REST call against a protected API

In the sample application created by this guide, the callWebApiWithToken() method is used to make an HTTP GET request against a protected resource that requires a token and then return the content to the caller. This method adds the acquired token in the HTTP Authorization header. For the sample application created by this guide, the resource is the Microsoft Graph API me endpoint – which displays the user's profile information.

Add a method to sign out the user

Add the following code to your app.js file:

/**
 * Sign-out the user
 */
function signOut() {
    userAgentApplication.logout();
}

Register your application

There are multiple ways to create an application, please select one of them:

Option 1: Register your application (Express mode)

Now you need to register your application in the Microsoft Application Registration Portal:

  1. Register your application via the Microsoft Application Registration Portal
  2. Enter a name for your application and your email
  3. Make sure the option for Guided Setup is checked
  4. Follow the instructions to obtain the application ID and paste it into your code

Option 2: Register your application (Advanced mode)

  1. Go to the Microsoft Application Registration Portal to register an application
  2. Enter a name for your application and your email
  3. Make sure the option for Guided Setup is unchecked
  4. Click Add Platform, then select Web
  5. Add the Redirect URL that correspond to the application's URL based on your web server. See the sections below for instructions on how to set/ obtain the redirect URL in Visual Studio and Python.
  6. Click Save

Visual Studio instructions for obtaining redirect URL

Follow the instructions to obtain your redirect URL:

  1. In Solution Explorer, select the project and look at the Properties window (if you don’t see a Properties window, press F4)
  2. Copy the value from URL to the clipboard:
    Project properties
  3. Switch back to the Application Registration Portal and paste the value as a Redirect URL and click 'Save'

Setting Redirect URL for Python

For Python, you can set the web server port via command line. This guided setup uses the port 8080 for reference but feel free to use any other port available. In any case, follow the instructions below to set up a redirect URL in the application registration information:

  • Switch back to the Application Registration Portal and set http://localhost:8080/ as a Redirect URL, or use http://localhost:[port]/ if you are using a custom TCP port (where [port] is the custom TCP port number) and click 'Save'

Configure your JavaScript SPA

  1. Create a file named msalconfig.js containing the application registration information. If you are using Visual Studio, select the project (project root folder), right-click and select: Add > New Item > JavaScript File. Name it msalconfig.js
  2. Add the following code to your msalconfig.js file:
var msalconfig = {
    clientID: "Enter_the_Application_Id_here",
    redirectUri: location.origin
};
  1. Replace Enter_the_Application_Id_here with the Application Id you just registered

Test your code

Testing with Visual Studio

If you are using Visual Studio, press F5 to run your project: the browser opens and directs you to http://localhost:{port} where you see the Call Microsoft Graph API button.

Testing with Python or another web server

If you are not using Visual Studio, make sure your web server is started and it is configured to listen to a TCP port based on the folder containing your index.html file. For Python, you can start listening to the port by running the in the command prompt/ terminal, from the app's folder:

python -m http.server 8080

Then, open the browser and type http://localhost:8080 or http://localhost:{port} - where the port corresponds to the port that your web server is listening to. You should see the contents of your index.html page with the Call Microsoft Graph API button.

Test your application

After the browser loads your index.html, click the Call Microsoft Graph API button. If this is the first time, the browser redirects you to the Microsoft Azure Active Directory v2 endpoint, where you are prompted to sign in.

Sample screen shot

The very first time you sign in to your application, you are presented with a consent screen similar to the following, where you need to accept:

Consent screen

Expected results

You should see user profile information returned by the Microsoft Graph API call response.

Results

You also see basic information about the token acquired in the Access Token and ID Token Claims boxes.

More information about scopes and delegated permissions

The Microsoft Graph API requires the user.read scope to read the user's profile. This scope is automatically added by default in every application being registered on our registration portal. Some other APIs for Microsoft Graph as well as custom APIs for your backend server may require additional scopes. For example, for Microsoft Graph, the scope Calendars.Read is required to list the user’s calendars. In order to access the user’s calendar in the context of an application, you need to add the Calendars.Read delegated permission to the application registration’s information and then add the Calendars.Read scope to the acquireTokenSilent call. The user may be prompted for additional consents as you increase the number of scopes.

If a backend API does not require a scope (not recommended), you can use the clientId as the scope in the acquireTokenSilent and/or acquireTokenRedirect calls.