Implementing Complex Authentication Scenarios in Microsoft Graph

Introduction

In this blog post, I will explore with you some of the complex authentication scenarios that developers may encounter when using Microsoft Graph and how to implement them using the Microsoft Graph SDKs and the Microsoft Authentication Library (MSAL) for various platforms. I will cover the following topics:

  • What is MSAL, and how does it work with Microsoft Graph?
  • How do you use MSAL to acquire tokens for Microsoft Graph?
  • How do you handle conditional access policies and multi-factor authentication?
  • How can MSAL be used to implement single sign-on (SSO) and single sign-out (SSO) across multiple applications?
  • How do we use MSAL to implement device code flow and integrated Windows authentication?
  • How do we use MSAL to implement on-behalf-of flow and client credentials flow?

What is MSAL, and how does it work with Microsoft Graph?

MSAL is a set of libraries that enable developers to acquire tokens from the Microsoft identity platform endpoint in order to access secured resources such as Microsoft Graph. MSAL abstracts the complexity of authentication protocols and provides a consistent interface across different platforms and languages. MSAL supports various authentication flows, such as interactive, silent, device code, integrated Windows authentication, on-behalf-of, and client credentials.

MSAL works with Microsoft Graph by leveraging the OAuth 2.0 and OpenID Connect protocols, which are the industry standards for securing web APIs and web applications. MSAL helps developers to obtain access tokens and refresh tokens from the Microsoft identity platform endpoint, and use them to call Microsoft Graph API endpoints. MSAL also handles token caching, token renewal, and token validation.

MSAL is available for various platforms and languages, such as .NET, Java, Python, and JavaScript. MSAL is also integrated with the Microsoft Graph SDKs, which are wrappers around the Microsoft Graph API that provide a fluent and easy-to-use interface for developers. The Microsoft Graph SDKs handle the authentication process by using MSAL internally and allow developers to focus on the business logic of their applications.

How do you use MSAL to acquire tokens for Microsoft Graph?

The basic steps to use MSAL to acquire tokens for Microsoft Graph are as follows:

  1. Create an application registration in the Azure portal and configure the required permissions and settings for Microsoft Graph. You can follow the Microsoft article to learn how to register the app in the Azure portal. https://learn.microsoft.com/en-us/entra/identity-platform/quickstart-register-app.
  2. Install the MSAL library and the Microsoft Graph SDK for your platform and language of choice.
  3. Initialize an MSAL client application object with the application ID, authority, and redirect URI of your application registration.
  4. Use the MSAL client application object to acquire tokens for Microsoft Graph, either interactively or silently, depending on the authentication flow and the user context.
  5. Use the access token to call the Microsoft Graph SDK methods or directly call the Microsoft Graph API endpoints.

.NET

// Install the Microsoft.Identity.Client and Microsoft.Graph NuGet packages
using Microsoft.Identity.Client;
using Microsoft.Graph;
using System.Net.Http.Headers;
using System.Threading.Tasks;

class Program
{
    static async Task Main(string[] args)
    {
        // Create an MSAL client application object
        var app = PublicClientApplicationBuilder.Create("your-application-id")
            .WithAuthority("https://login.microsoftonline.com/your-tenant-id")
            .WithRedirectUri("https://localhost")
            .Build();

        // Acquire an access token interactively
        var scopes = new[] { "user.read", "mail.read" };
        var result = await app.AcquireTokenInteractive(scopes).ExecuteAsync();

        // Use the access token to call the Microsoft Graph SDK
        var graphClient = new GraphServiceClient(
            new DelegateAuthenticationProvider(request =>
            {
                request.Headers.Authorization =
                    new AuthenticationHeaderValue("Bearer", result.AccessToken);
                return Task.CompletedTask;
            }));

        var user = await graphClient.Me.Request().GetAsync();
        var messages = await graphClient.Me.MailFolders.Inbox.Messages.Request().GetAsync();
    }
}

Java

// Install the msal4j and microsoft-graph libraries
import com.microsoft.aad.msal4j.*;
import com.microsoft.graph.authentication.*;
import com.microsoft.graph.models.*;
import com.microsoft.graph.requests.*;

import java.net.URI;
import java.util.Collections;
import java.util.Set;

public class GraphExample {

    public static void main(String[] args) throws Exception {
        // Create an MSAL client application object
        PublicClientApplication app = PublicClientApplication.builder("your-application-id")
                .authority("https://login.microsoftonline.com/your-tenant-id")
                .build();

        // Acquire an access token interactively
        Set<String> scopes = Collections.singleton("user.read mail.read");
        InteractiveBrowserProviderParams params = InteractiveBrowserProviderParams
                .builder(new URI("http://localhost"))
                .scopes(scopes)
                .build();
        IAuthenticationResult result = app.acquireToken(params).get();

        // Use the access token to call the Microsoft Graph SDK
        IAuthenticationProvider authProvider = new TokenCredentialAuthProvider(scopes,
                new StaticTokenCredential(result.accessToken()));
        GraphServiceClient<IUser> graphClient = GraphServiceClient
                .<IUser>builder()
                .authenticationProvider(authProvider)
                .buildClient();

        User user = graphClient.me().buildRequest().get();
        IMailFolderCollectionPage messages = graphClient.me().mailFolders("Inbox").messages()
                .buildRequest().get();
    }
}

Python

# Install the msal and msgraph-sdk-python-core libraries
import msal
import msgraphcore

# Create an MSAL client application object
app = msal.PublicClientApplication(
    "your-application-id",
    authority="https://login.microsoftonline.com/your-tenant-id"
)

# Acquire an access token interactively
scopes = ["user.read", "mail.read"]
result = app.acquire_token_interactive(scopes)

# Use the access token to call the Microsoft Graph SDK
middleware = msgraphcore.AuthProvider(auth_session_type=None, token=result["access_token"])
graph_client = msgraphcore.GraphSession(middleware)

user = graph_client.get("/me")
messages = graph_client.get("/me/mailfolders/inbox/messages")

JavaScript

// Install the msal and @microsoft/microsoft-graph-client libraries
import * as msal from "msal";
import { Client } from "@microsoft/microsoft-graph-client";

// Create an MSAL client application object
const app = new msal.PublicClientApplication({
    auth: {
        clientId: "your-application-id",
        authority: "https://login.microsoftonline.com/your-tenant-id",
        redirectUri: "http://localhost",
    },
});

// Acquire an access token interactively
const scopes = ["user.read", "mail.read"];
const loginRequest = { scopes };

app.loginPopup(loginRequest).then((result) => {
    // Use the access token to call the Microsoft Graph SDK
    const client = Client.init({
        authProvider: (done) => {
            done(null, result.accessToken);
        },
    });

    client.api("/me").get().then((user) => {
        console.log(user);
    });

    client.api("/me/mailfolders/inbox/messages").get().then((messages) => {
        console.log(messages);
    });
});

How do you handle conditional access policies and multi-factor authentication?

Conditional access policies are rules that define the conditions under which users or applications can access certain resources, such as Microsoft Graph. For example, a conditional access policy may require that users perform multi-factor authentication (MFA) when accessing Microsoft Graph from outside the corporate network or that applications use a certificate to authenticate when accessing Microsoft Graph on behalf of a user.

MSAL supports handling conditional access policies and MFA by providing mechanisms to handle various error scenarios and prompts. For example, MSAL can handle the following situations:

  • The user needs to perform an additional step of authentication, such as entering a verification code or approving a notification on their mobile device.
  • The user needs to consent to the permissions requested by the application.
  • The user needs to change their password or update their account information.
  • The application needs to provide proof of possession of a key, such as a certificate or a thumbprint.

The way MSAL handles these situations depends on the platform and the authentication flow. For interactive flows, such as interactive and device codes, MSAL will either prompt the user to perform the required action or throw an exception with a specific error code and message that indicates the action needed. For silent flows, such as silent and on-behalf-of, MSAL will throw an exception with a specific error code and message that indicates the action needed and optionally provide a suggested action, such as invoking an interactive flow or providing a certificate.

The following code snippets show how to handle conditional access policies and MFA in different platforms and languages. Note that these are simplified examples for illustration purposes, and you may need to modify them according to your specific scenario and requirements.

// Handle conditional access and MFA for interactive flow
try
{
    var result = await app.AcquireTokenInteractive(scopes).ExecuteAsync();
    // Use the access token
}
catch (MsalUiRequiredException ex)
{
    // This exception is thrown when an interactive action is required
    switch (ex.ErrorCode)
    {
        case "invalid_grant":
            // This error code is returned when the user needs to perform an additional step of authentication
            // Invoke an interactive flow with the same parameters as before
            result = await app.AcquireTokenInteractive(scopes)
                .WithPrompt(Prompt.SelectAccount)
                .ExecuteAsync();
            // Use the access token
            break;
        case "consent_required":
            // This error code is returned when the user needs to consent to the permissions requested by the application
            // Invoke an interactive flow with the same parameters as before, but with a prompt for consent
            result = await app.AcquireTokenInteractive(scopes)
                .WithPrompt(Prompt.Consent)
                .ExecuteAsync();
            // Use the access token
            break;
        case "interaction_required":
            // This error code is returned when the user needs to change their password or update their account information
            // Invoke an interactive flow with the same parameters as before, but with a prompt for interaction
            result = await app.AcquireTokenInteractive(scopes)
                .WithPrompt(Prompt.ForceLogin)
                .ExecuteAsync();
            // Use the access token
            break;
        default:
            // Handle other error codes
            break;
    }
}
catch (MsalException ex)
{
    // Handle other exceptions
}

// Handle conditional access and MFA for silent flow
try
{
    var result = await app.AcquireTokenSilent(scopes, account).ExecuteAsync();
    // Use the access token
}
catch (MsalUiRequiredException ex)
{
    // Handle UI required exception for silent flow
}


Similar Articles