Tutorial: Sign in users and call the Microsoft Graph API from a JavaScript single-page app (SPA) using auth code flow

In this tutorial, you build a JavaScript single-page application (SPA) that signs in users and calls Microsoft Graph by using the authorization code flow with PKCE. The SPA you build uses the Microsoft Authentication Library (MSAL) for JavaScript v2.0.

In this tutorial:

  • Perform the OAuth 2.0 authorization code flow with PKCE
  • Sign in personal Microsoft accounts as well as work and school accounts
  • Acquire an access token
  • Call Microsoft Graph or your own API that requires access tokens obtained from the Microsoft identity platform

MSAL.js 2.0 improves on MSAL.js 1.0 by supporting the authorization code flow in the browser instead of the implicit grant flow. MSAL.js 2.0 does NOT support the implicit flow.

Prerequisites

How the tutorial app works

Diagram showing the authorization code flow in a single-page application

The application you create in this tutorial enables a JavaScript SPA to query the Microsoft Graph API by acquiring security tokens from the the Microsoft identity platform. In this scenario, after a user signs in, an access token is requested and added to HTTP requests in the authorization header. Token acquisition and renewal are handled by the Microsoft Authentication Library for JavaScript (MSAL.js).

This tutorial uses the following library:

msal.js the Microsoft Authentication Library for JavaScript v2.0 browser package

Get the completed code sample

Prefer to download this tutorial's completed sample project instead? Clone the ms-identity-javascript-v2 repository.

git clone https://github.com/Azure-Samples/ms-identity-javascript-v2

To run the downloaded project on your local development environment, start by creating a localhost server for your application as described in step 1 of create your project. Once done, you can configure the code sample by skipping to the configuration step.

To continue with the tutorial and build the application yourself, move on to the next section, Create your project.

Create your project

Once you have Node.js installed, create a folder to host your application, for example msal-spa-tutorial.

Next, implement a small Express web server to serve your index.html file.

  1. First, change to your project directory in your terminal and then run the following npm commands:
    npm init -y
    npm install @azure/msal-browser
    npm install express
    npm install morgan
    npm install yargs
    
  2. Next, create file named server.js and add the following code:
const express = require('express');
const morgan = require('morgan');
const path = require('path');

const DEFAULT_PORT = process.env.PORT || 3000;

// initialize express.
const app = express();

// Initialize variables.
let port = DEFAULT_PORT;

// Configure morgan module to log all requests.
app.use(morgan('dev'));

// Setup app folders.
app.use(express.static('app'));

// Set up a route for index.html
app.get('*', (req, res) => {
    res.sendFile(path.join(__dirname + '/index.html'));
});

// Start the server.
app.listen(port);
console.log(`Listening on port ${port}...`);

Create the SPA UI

  1. Create an app folder in your project directory, and in it create an index.html file for your JavaScript SPA. This file implements a UI built with the Bootstrap 4 Framework and imports script files for configuration, authentication, and API calls.

    In the index.html file, add the following code:

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
      <title>Microsoft identity platform</title>
      <link rel="SHORTCUT ICON" href="./favicon.svg" type="image/x-icon">
    
      <!-- msal.min.js can be used in the place of msal.js; included msal.js to make debug easy -->
      <script src="https://alcdn.msauth.net/browser/2.15.0/js/msal-browser.js"
        integrity="sha384-dFzMiVGB5HpWZ+5w5VSif6jhWfNeplSw9ACYmQKZcY2azuT9kCxVWVI9HyfGdkHV"
        crossorigin="anonymous"></script>
    
      <!-- To help ensure reliability, Microsoft provides a second CDN -->
      <script type="text/javascript">
        if (typeof Msal === 'undefined') document.write(unescape("%3Cscript src='https://alcdn.msftauth.net/browser/2.15.0/js/msal-browser.js' type='text/javascript' crossorigin='anonymous' %3E%3C/script%3E"));
      </script>
    
      <!-- adding Bootstrap 4 for UI components  -->
      <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css"
        integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
      <link rel="SHORTCUT ICON" href="https://c.s-microsoft.com/favicon.ico?v2" type="image/x-icon">
    </head>
    
    <body>
      <nav class="navbar navbar-expand-lg navbar-dark bg-primary">
        <a class="navbar-brand" href="/">Microsoft identity platform</a>
        <div class="btn-group ml-auto dropleft">
          <button type="button" id="SignIn" class="btn btn-secondary" onclick="signIn()">
            Sign In
          </button>
        </div>
      </nav>
      <br>
      <h5 class="card-header text-center">Vanilla JavaScript SPA calling MS Graph API with MSAL.js</h5>
      <br>
      <div class="row" style="margin:auto">
        <div id="card-div" class="col-md-3" style="display:none">
          <div class="card text-center">
            <div class="card-body">
              <h5 class="card-title" id="WelcomeMessage">Please sign-in to see your profile and read your mails</h5>
              <div id="profile-div"></div>
              <br>
              <br>
              <button class="btn btn-primary" id="seeProfile" onclick="seeProfile()">See Profile</button>
              <br>
              <br>
              <button class="btn btn-primary" id="readMail" onclick="readMail()">Read Mails</button>
            </div>
          </div>
        </div>
        <br>
        <br>
        <div class="col-md-4">
          <div class="list-group" id="list-tab" role="tablist">
          </div>
        </div>
        <div class="col-md-5">
          <div class="tab-content" id="nav-tabContent">
          </div>
        </div>
      </div>
      <br>
      <br>
    
      <!-- importing bootstrap.js and supporting js libraries -->
      <script src="https://code.jquery.com/jquery-3.4.1.slim.min.js"
        integrity="sha384-J6qa4849blE2+poT4WnyKhv5vZF5SrPo0iEjwBvKU7imGFAV0wwj1yYfoRSJoZ+n"
        crossorigin="anonymous"></script>
      <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js"
        integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo"
        crossorigin="anonymous"></script>
      <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js"
        integrity="sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6"
        crossorigin="anonymous"></script>
    
      <!-- importing app scripts (load order is important) -->
      <script type="text/javascript" src="./authConfig.js"></script>
      <script type="text/javascript" src="./graphConfig.js"></script>
      <script type="text/javascript" src="./ui.js"></script>
    
      <!-- <script type="text/javascript" src="./authRedirect.js"></script>   -->
      <!-- uncomment the above line and comment the line below if you would like to use the redirect flow -->
      <script type="text/javascript" src="./authPopup.js"></script>
      <script type="text/javascript" src="./graph.js"></script>
    </body>
    
    </html>
    
  2. Next, also in the app folder, create a file named ui.js and add the following code. This file will access and update DOM elements.

    // Select DOM elements to work with
    const welcomeDiv = document.getElementById("WelcomeMessage");
    const signInButton = document.getElementById("SignIn");
    const cardDiv = document.getElementById("card-div");
    const mailButton = document.getElementById("readMail");
    const profileButton = document.getElementById("seeProfile");
    const profileDiv = document.getElementById("profile-div");
    
    function showWelcomeMessage(username) {
        // Reconfiguring DOM elements
        cardDiv.style.display = 'initial';
        welcomeDiv.innerHTML = `Welcome ${username}`;
        signInButton.setAttribute("onclick", "signOut();");
        signInButton.setAttribute('class', "btn btn-success")
        signInButton.innerHTML = "Sign Out";
    }
    
    function updateUI(data, endpoint) {
        console.log('Graph API responded at: ' + new Date().toString());
    
        if (endpoint === graphConfig.graphMeEndpoint) {
            profileDiv.innerHTML = ''
            const title = document.createElement('p');
            title.innerHTML = "<strong>Title: </strong>" + data.jobTitle;
            const email = document.createElement('p');
            email.innerHTML = "<strong>Mail: </strong>" + data.mail;
            const phone = document.createElement('p');
            phone.innerHTML = "<strong>Phone: </strong>" + data.businessPhones[0];
            const address = document.createElement('p');
            address.innerHTML = "<strong>Location: </strong>" + data.officeLocation;
            profileDiv.appendChild(title);
            profileDiv.appendChild(email);
            profileDiv.appendChild(phone);
            profileDiv.appendChild(address);
    
        } else if (endpoint === graphConfig.graphMailEndpoint) {
            if (!data.value) {
                alert("You do not have a mailbox!")
            } else if (data.value.length < 1) {
                alert("Your mailbox is empty!")
            } else {
                const tabContent = document.getElementById("nav-tabContent");
                const tabList = document.getElementById("list-tab");
                tabList.innerHTML = ''; // clear tabList at each readMail call
    
                data.value.map((d, i) => {
                    // Keeping it simple
                    if (i < 10) {
                        const listItem = document.createElement("a");
                        listItem.setAttribute("class", "list-group-item list-group-item-action")
                        listItem.setAttribute("id", "list" + i + "list")
                        listItem.setAttribute("data-toggle", "list")
                        listItem.setAttribute("href", "#list" + i)
                        listItem.setAttribute("role", "tab")
                        listItem.setAttribute("aria-controls", i)
                        listItem.innerHTML = d.subject;
                        tabList.appendChild(listItem)
    
                        const contentItem = document.createElement("div");
                        contentItem.setAttribute("class", "tab-pane fade")
                        contentItem.setAttribute("id", "list" + i)
                        contentItem.setAttribute("role", "tabpanel")
                        contentItem.setAttribute("aria-labelledby", "list" + i + "list")
                        contentItem.innerHTML = "<strong> from: " + d.from.emailAddress.address + "</strong><br><br>" + d.bodyPreview + "...";
                        tabContent.appendChild(contentItem);
                    }
                });
            }
        }
    }
    

Register your application

Follow the steps in Single-page application: App registration to create an app registration for your SPA.

In the Redirect URI: MSAL.js 2.0 with auth code flow step, enter http://localhost:3000, the default location where this tutorial's application runs.

If you'd like to use a different port, enter http://localhost:<port>, where <port> is your preferred TCP port number. If you specify a port number other than 3000, also update server.js with your preferred port number.

Configure your JavaScript SPA

Create a file named authConfig.js in the app folder to contain your configuration parameters for authentication, and then add the following code:

/**
 * Configuration object to be passed to MSAL instance on creation. 
 * For a full list of MSAL.js configuration parameters, visit:
 * https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-browser/docs/configuration.md 
 */
const msalConfig = {
    auth: {
        // 'Application (client) ID' of app registration in Azure portal - this value is a GUID
        clientId: "Enter_the_Application_Id_Here",
        // Full directory URL, in the form of https://login.microsoftonline.com/<tenant>
        authority: "Enter_the_Cloud_Instance_Id_HereEnter_the_Tenant_Info_Here",
        // Full redirect URL, in form of http://localhost:3000
        redirectUri: "Enter_the_Redirect_Uri_Here",
    },
    cache: {
        cacheLocation: "sessionStorage", // This configures where your cache will be stored
        storeAuthStateInCookie: false, // Set this to "true" if you are having issues on IE11 or Edge
    },
    system: {	
        loggerOptions: {	
            loggerCallback: (level, message, containsPii) => {	
                if (containsPii) {		
                    return;		
                }		
                switch (level) {		
                    case msal.LogLevel.Error:		
                        console.error(message);		
                        return;		
                    case msal.LogLevel.Info:		
                        console.info(message);		
                        return;		
                    case msal.LogLevel.Verbose:		
                        console.debug(message);		
                        return;		
                    case msal.LogLevel.Warning:		
                        console.warn(message);		
                        return;		
                }	
            }	
        }	
    }
};

/**
 * Scopes you add here will be prompted for user consent during sign-in.
 * By default, MSAL.js will add OIDC scopes (openid, profile, email) to any login request.
 * For more information about OIDC scopes, visit: 
 * https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-permissions-and-consent#openid-connect-scopes
 */
const loginRequest = {
    scopes: ["User.Read"]
};

/**
 * Add here the scopes to request when obtaining an access token for MS Graph API. For more information, see:
 * https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-browser/docs/resources-and-scopes.md
 */
const tokenRequest = {
    scopes: ["User.Read", "Mail.Read"],
    forceRefresh: false // Set this to "true" to skip a cached token and go to the server to get a new token
};

Still in the app folder, create a file named graphConfig.js. Add the following code to provide your application the configuration parameters for calling the Microsoft Graph API:

// Add here the endpoints for MS Graph API services you would like to use.
const graphConfig = {
    graphMeEndpoint: "Enter_the_Graph_Endpoint_Herev1.0/me",
    graphMailEndpoint: "Enter_the_Graph_Endpoint_Herev1.0/me/messages"
};

Modify the values in the graphConfig section as described here:

  • Enter_the_Graph_Endpoint_Here is the instance of the Microsoft Graph API the application should communicate with.
    • For the global Microsoft Graph API endpoint, replace both instances of this string with https://graph.microsoft.com.
    • For endpoints in national cloud deployments, see National cloud deployments in the Microsoft Graph documentation.

The graphMeEndpoint and graphMailEndpoint values in your graphConfig.js should be similar to the following if you're using the global endpoint:

graphMeEndpoint: "https://graph.microsoft.com/v1.0/me",
graphMailEndpoint: "https://graph.microsoft.com/v1.0/me/messages"

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

Pop-up

In the app folder, create a file named authPopup.js and add the following authentication and token acquisition code for the login pop-up:

// Create the main myMSALObj instance
// configuration parameters are located at authConfig.js
const myMSALObj = new msal.PublicClientApplication(msalConfig);

let username = "";

function selectAccount() {

    /**
     * See here for more info on account retrieval: 
     * https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-common/docs/Accounts.md
     */

    const currentAccounts = myMSALObj.getAllAccounts();
    if (currentAccounts.length === 0) {
        return;
    } else if (currentAccounts.length > 1) {
        // Add choose account code here
        console.warn("Multiple accounts detected.");
    } else if (currentAccounts.length === 1) {
        username = currentAccounts[0].username;
        showWelcomeMessage(username);
    }
}

function handleResponse(response) {

    /**
     * To see the full list of response object properties, visit:
     * https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-browser/docs/request-response-object.md#response
     */

    if (response !== null) {
        username = response.account.username;
        showWelcomeMessage(username);
    } else {
        selectAccount();
    }
}

function signIn() {

    /**
     * You can pass a custom request object below. This will override the initial configuration. For more information, visit:
     * https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-browser/docs/request-response-object.md#request
     */

    myMSALObj.loginPopup(loginRequest)
        .then(handleResponse)
        .catch(error => {
            console.error(error);
        });
}

function signOut() {

    /**
     * You can pass a custom request object below. This will override the initial configuration. For more information, visit:
     * https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-browser/docs/request-response-object.md#request
     */

    const logoutRequest = {
        account: myMSALObj.getAccountByUsername(username),
        postLogoutRedirectUri: msalConfig.auth.redirectUri,
        mainWindowRedirectUri: msalConfig.auth.redirectUri
    };

    myMSALObj.logoutPopup(logoutRequest);
}

function getTokenPopup(request) {

    /**
     * See here for more info on account retrieval: 
     * https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-common/docs/Accounts.md
     */
    request.account = myMSALObj.getAccountByUsername(username);
    
    return myMSALObj.acquireTokenSilent(request)
        .catch(error => {
            console.warn("silent token acquisition fails. acquiring token using popup");
            if (error instanceof msal.InteractionRequiredAuthError) {
                // fallback to interaction when silent call fails
                return myMSALObj.acquireTokenPopup(request)
                    .then(tokenResponse => {
                        console.log(tokenResponse);
                        return tokenResponse;
                    }).catch(error => {
                        console.error(error);
                    });
            } else {
                console.warn(error);   
            }
    });
}

function seeProfile() {
    getTokenPopup(loginRequest)
        .then(response => {
            callMSGraph(graphConfig.graphMeEndpoint, response.accessToken, updateUI);
        }).catch(error => {
            console.error(error);
        });
}

function readMail() {
    getTokenPopup(tokenRequest)
        .then(response => {
            callMSGraph(graphConfig.graphMailEndpoint, response.accessToken, updateUI);
        }).catch(error => {
            console.error(error);
        });
}

selectAccount();

Redirect

Create a file named authRedirect.js in the app folder and add the following authentication and token acquisition code for login redirect:

// Create the main myMSALObj instance
// configuration parameters are located at authConfig.js
const myMSALObj = new msal.PublicClientApplication(msalConfig);

let username = "";

/**
 * A promise handler needs to be registered for handling the
 * response returned from redirect flow. For more information, visit:
 * https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-browser/docs/acquire-token.md
 */
myMSALObj.handleRedirectPromise()
    .then(handleResponse)
    .catch((error) => {
        console.error(error);
    });

function selectAccount () {

    /**
     * See here for more info on account retrieval: 
     * https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-common/docs/Accounts.md
     */

    const currentAccounts = myMSALObj.getAllAccounts();

    if (currentAccounts.length === 0) {
        return;
    } else if (currentAccounts.length > 1) {
        // Add your account choosing logic here
        console.warn("Multiple accounts detected.");
    } else if (currentAccounts.length === 1) {
        username = currentAccounts[0].username;
        showWelcomeMessage(username);
    }
}

function handleResponse(response) {
    if (response !== null) {
        username = response.account.username;
        showWelcomeMessage(username);
    } else {
        selectAccount();
    }
}

function signIn() {

    /**
     * You can pass a custom request object below. This will override the initial configuration. For more information, visit:
     * https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-browser/docs/request-response-object.md#request
     */

    myMSALObj.loginRedirect(loginRequest);
}

function signOut() {

    /**
     * You can pass a custom request object below. This will override the initial configuration. For more information, visit:
     * https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-browser/docs/request-response-object.md#request
     */

    const logoutRequest = {
        account: myMSALObj.getAccountByUsername(username),
        postLogoutRedirectUri: msalConfig.auth.redirectUri,
    };

    myMSALObj.logoutRedirect(logoutRequest);
}

function getTokenRedirect(request) {
    /**
     * See here for more info on account retrieval: 
     * https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-common/docs/Accounts.md
     */
    request.account = myMSALObj.getAccountByUsername(username);

    return myMSALObj.acquireTokenSilent(request)
        .catch(error => {
            console.warn("silent token acquisition fails. acquiring token using redirect");
            if (error instanceof msal.InteractionRequiredAuthError) {
                // fallback to interaction when silent call fails
                return myMSALObj.acquireTokenRedirect(request);
            } else {
                console.warn(error);   
            }
        });
}

function seeProfile() {
    getTokenRedirect(loginRequest)
        .then(response => {
            callMSGraph(graphConfig.graphMeEndpoint, response.accessToken, updateUI);
        }).catch(error => {
            console.error(error);
        });
}

function readMail() {
    getTokenRedirect(tokenRequest)
        .then(response => {
            callMSGraph(graphConfig.graphMailEndpoint, response.accessToken, updateUI);
        }).catch(error => {
            console.error(error);
        });
}

How the code works

When a user selects the Sign In button for the first time, the signIn method calls loginPopup to sign in the user. The loginPopup method opens a pop-up window with the Microsoft identity platform endpoint to prompt and validate the user's credentials. After a successful sign-in, msal.js initiates the authorization code flow.

At this point, a PKCE-protected authorization code is sent to the CORS-protected token endpoint and is exchanged for tokens. An ID token, access token, and refresh token are received by your application and processed by msal.js, and the information contained in the tokens is cached.

The ID token contains basic information about the user, like their display name. If you plan to use any data provided by the ID token, your back-end server must validate it to guarantee the token was issued to a valid user for your application.

The access token has a limited lifetime and expires after 24 hours. The refresh token can be used to silently acquire new access tokens.

The SPA you've created in this tutorial calls acquireTokenSilent and/or acquireTokenPopup to acquire an access token used to query the Microsoft Graph API for user profile info. If you need a sample that validates the ID token, see the active-directory-javascript-singlepageapp-dotnet-webapi-v2 sample application on GitHub. The sample uses an ASP.NET web API for token validation.

Get a user token interactively

After their initial sign-in, your app shouldn't ask users to reauthenticate every time they need to access a protected resource (that is, to request a token). To prevent such reauthentication requests, call acquireTokenSilent. There are some situations, however, where you might need to force users to interact with the Microsoft identity platform. For example:

  • Users need to re-enter their credentials because the password has expired.
  • Your application is requesting access to a resource and you need the user's consent.
  • Two-factor authentication is required.

Calling acquireTokenPopup opens a pop-up window (or acquireTokenRedirect redirects users to the Microsoft identity platform). In that window, users need to interact by confirming their credentials, giving consent to the required resource, or completing the two-factor authentication.

Get a user token silently

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

  1. Make a call to acquireTokenPopup immediately to trigger a user sign-in prompt. 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. Visually indicate 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 technique is commonly used when the user can use other functionality of the application without being disrupted. For example, there might be unauthenticated content available in the application. In this situation, the user can decide when they want to sign in to access the protected resource, or to refresh the outdated information.

Note

This tutorial uses the loginPopup and acquireTokenPopup methods by default. If you're using Internet Explorer, we recommend that you use the loginRedirect and acquireTokenRedirect methods due to a known issue with Internet Explorer and pop-up windows. For an example of achieving the same result by using redirect methods, see authRedirect.js on GitHub.

Call the Microsoft Graph API

Create file named graph.js in the app folder and add the following code for making REST calls to the Microsoft Graph API:

/** 
 * Helper function to call MS Graph API endpoint
 * using the authorization bearer token scheme
*/
function callMSGraph(endpoint, token, callback) {
    const headers = new Headers();
    const bearer = `Bearer ${token}`;

    headers.append("Authorization", bearer);

    const options = {
        method: "GET",
        headers: headers
    };

    console.log('request made to Graph API at: ' + new Date().toString());

    fetch(endpoint, options)
        .then(response => response.json())
        .then(response => callback(response, endpoint))
        .catch(error => console.log(error));
}

In the sample application created in this tutorial, the callMSGraph() method is used to make an HTTP GET request against a protected resource that requires a token. The request then returns the content to the caller. This method adds the acquired token in the HTTP Authorization header. In the sample application created in this tutorial, the protected resource is the Microsoft Graph API me endpoint which displays the signed-in user's profile information.

Test your application

You've completed creation of the application and are now ready to launch the Node.js web server and test the app's functionality.

  1. Start the Node.js web server by running the following command from within the root of your project folder:

    npm start
    
  2. In your browser, navigate to http://localhost:3000 or http://localhost:<port>, where <port> is the port that your web server is listening on. You should see the contents of your index.html file and the Sign In button.

Sign in to the application

After the browser loads your index.html file, select Sign In. You're prompted to sign in with the Microsoft identity platform:

Web browser displaying sign-in dialog

The first time you sign in to your application, you're prompted to grant it access to your profile and sign you in:

Content dialog displayed in web browser

If you consent to the requested permissions, the web applications displays your user name, signifying a successful login:

Results of a successful sign-in in the web browser

Call the Graph API

After you sign in, select See Profile to view the user profile information returned in the response from the call to the Microsoft Graph API:

Profile information from Microsoft Graph displayed in the browser

More information about scopes and delegated permissions

The Microsoft Graph API requires the user.read scope to read a user's profile. By default, this scope is automatically added in every application that's registered in the Azure portal. Other APIs for Microsoft Graph, as well as custom APIs for your back-end server, might require additional scopes. For example, the Microsoft Graph API requires the Mail.Read scope in order to list the user's email.

As you add scopes, your users might be prompted to provide additional consent for the added scopes.

If a back-end API doesn't require a scope, which isn't recommended, you can use clientId as the scope in the calls to acquire tokens.

Help and support

If you need help, want to report an issue, or want to learn about your support options, see Help and support for developers.

Next steps

If you'd like to dive deeper into JavaScript single-page application development on the Microsoft identity platform, see our multi-part scenario series: