Skip to content

Latest commit

 

History

History
199 lines (146 loc) · 9.66 KB

File metadata and controls

199 lines (146 loc) · 9.66 KB
languages page_type description products
javascript
powershell
html
sample
Learn how to sign-in users to your web app, call Azure storage, and call Microsoft Graph.
nodejs
express
ms-graph
azure
azure-storage
azure-active-directory

Tutorial: Enable authentication in App Service and call Microsoft Graph (as the web app)

About this sample

Overview

This sample demonstrates an Node.js & Express web app that uses authentication to limit access to users in your organization​ and then calls Microsoft Graph as the app. The web app authenticates a user and displays some of the user's profile information. The web app then displays a list of users in the Azure Active Directory tenant. This sample is a companion to the Access Microsoft Graph from a secured app as the app tutorial on docs.microsoft.com.

Scenario

You want to limit access to your web app running on Azure App Service to people in your organization. App Service provides built-in authentication and authorization support, so you can sign in users and access data by writing minimal or no code in your web app.

You also want to call Microsoft Graph from the web app, as the app (not the signed-in user). A safe way to give your web app access to data is to use a system-assigned managed identity. A managed identity from Azure Active Directory allows App Service to access resources through role-based access control (RBAC), without requiring app credentials. After assigning a managed identity to your web app, Azure takes care of the creation and distribution of a certificate. You don't have to worry about managing secrets or app credentials.

How to run this sample

To run this sample, you'll need:

Step 1: Clone or download this repository

Clone or download this repository. From your shell or command line:

git clone https://github.com/Azure-Samples/ms-identity-easyauth-nodejs-storage-graphapi.git
cd ms-identity-easyauth-nodejs-storage-graphapi
cd 3-WebApp-graphapi-managed-identity

Step 2: Deploy the web app and configure App Service authentication

This project has one WebApp project. To deploy it to Azure App Service, you'll need to:

  • configure a deployment user
  • create an Azure App Service plan
  • create a web app
  • publish the web app to Azure

For information on how to do this from VS Code using the App Service Extension, see the tutorial.

After you've deployed the web app to Azure, configure the Azure App Service authentication/authorization module. Also verify that only users in your organization can access the web site.

Step 3: Enable managed identity on an app

Read this article to learn how to enable a managed identity on a web app.

Step 4: Grant access to Microsoft Graph

When accessing the Microsoft Graph, the managed identity needs to have proper permissions for the operation it wants to perform. Currently, there's no option to assign such permissions through the Azure portal. Use PowerShell or the Azure CLI to add the requested Microsoft Graph API permissions to the managed identity service principal object. For more information, read Grant access to Microsoft Graph in the tutorial on docs.microsoft.com.

Step 5: Visit the web app

Open a browser and navigate to the deployed web app (replace web-app-name with the name of your web app): https://web-app-name.azurewebsites.net. Once authenticated, select the Users button at the center of the page. This will take you to the page where you can see the users data in your tenant via calling Microsoft Graph.

About the code

This sample is built using the @azure/identity and @azure-samples/microsoft-identity-express packages for authentication and authorization.

Add authentication to your web app

The signIn middleware in routes/mainRoutes.js receives the App Service authentication headers from the incoming request, and then initializes a session variable with the user account, which indicates that the user has successfully signed-in.

    router.get('/login', msid.signIn({
        postLoginRedirect: '/home',
    }));

The isAuthenticated middleware checks the user's session variable to make sure the user is still signed in during route transitions:

router.get('/id', msid.isAuthenticated(), mainController.getIdPage);

When the user selects the sign-out button on the navigation bar, the signOut middleware wipes clean the user's session variable, and redirects the app to home page:

    router.get('/logout', msid.signOut({
        postLogoutRedirect: '/home',
    }));

Display name of the signed-in user

When you access the web app running on Azure, you see sign-in/sign-out and ID buttons at the top of the page. The ID page displays the contents of the singed-in user's ID token. To do so, we access the user's account via the session variable, which is populated when the user signs-in. The code for this is found in the controllers/mainController.js file:

exports.getIdPage = (req, res, next) => {
    const claims = {
        name: req.session.account.idTokenClaims.name,
        preferred_username: req.session.account.idTokenClaims.preferred_username,
        oid: req.session.account.idTokenClaims.oid,
        sub: req.session.account.idTokenClaims.sub
    };

    res.render('id', { isAuthenticated: req.session.isAuthenticated, appServiceName: appServiceName, claims: claims });
}

The claims object is then displayed on the views/id.ejs file:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" status="width=device-width, initial-scale=1.0">
    <title>ID</title>
</head>
<body>
    <%- include('includes/navbar', {isAuthenticated: isAuthenticated, appServiceName: appServiceName}); %>
        <div class="table-area-div">
            <table class="table" style="table-layout: fixed">
                <thead class="thead-dark">
                    <tr>
                        <th scope="col">Claim</th>
                        <th scope="col">Value</th>
                    </tr>
                </thead>
                <tbody>
                    <% for (const [key, value] of Object.entries(claims)) { %>
                        <tr>
                            <td><%= key %></td>
                            <td><%= value %></td>
                        </tr>
                    <% } %>
                </tbody>
            </table>
        </div>
</body>
</html>

Call Microsoft Graph as the app

The call to Microsoft Graph is performed in the controllers/graphController.js file getUsersPage controller. The DefaultAzureCredential class from @azure/identity package is used to get a token credential for your code to authorize requests to Azure Storage. Create an instance of the DefaultAzureCredential class, which uses the managed identity to fetch tokens and attach them to the service client. The following code example gets the authenticated token credential and uses it to create a service client object, which gets the users in the group.

const graphHelper = require('../utils/graphHelper');
const { DefaultAzureCredential } = require("@azure/identity");

exports.getUsersPage = async(req, res, next) => {

    const defaultAzureCredential = new DefaultAzureCredential();
    
    try {
        const tokenResponse = await defaultAzureCredential.getToken("https://graph.microsoft.com/.default");

        const graphClient = graphHelper.getAuthenticatedClient(tokenResponse.token);

        const users = await graphClient
            .api('/users')
            .get();

        res.render('users', { user: req.session.user, users: users });   
    } catch (error) {
        next(error);
    }
}

To query Microsoft Graph, the sample uses the Microsoft Graph JavaScript SDK. The code for this is located in utils/graphHelper.js:

getAuthenticatedClient = (accessToken) => {
    // Initialize Graph client
    const client = graph.Client.init({
        // Use the provided access token to authenticate requests
        authProvider: (done) => {
            done(null, accessToken);
        }
    });

    return client;
}

Resources

Read the Access Microsoft Graph from a secured app as the app tutorial.