Tuesday, 16 November 2021

Interactively authenticate Microsoft Graph .NET SDK with Azure Identity library

In this post, we will have a look at the new recommended way of using the Azure Identity library to authenticate to the Microsoft Graph .NET SDK. Since v4 of the Microsoft Graph SDK, using the Azure.Identity library is the preferred way to auth with the Graph over the previous Microsoft.Graph.Auth method. You can read more about it here: https://github.com/microsoftgraph/msgraph-sdk-dotnet/blob/dev/docs/upgrade-to-v4.md#azure-identity

Specifically, we will have a look at Interactive browser based authentication where the user would enter their credentials and the Azure Identity library will fetch the access token based on them.

Before going through the code, let us check out the Azure AD App registration which would be used to authenticate to the Graph API

Since we are going to authenticate from a .NET Desktop console application, we will select Desktop as a platform and add the default redirect URIs. In addition, we will also add http://localhost to the list.

The supported account types can be as per your requirements. I have selected the app to be multi tenant.

Select the Allow public client flows to "Yes":  


Select the needed scopes:


Once all of this in place, we can use the following code to open a browser window and authenticate to the Microsoft Graph once the user enters their credentials:

using Azure.Identity;
using Microsoft.Graph;
using System;
using System.Threading.Tasks;
namespace MSAL.Token.POC
{
class Program
{
static async Task Main(string[] args)
{
var scopes = new[] { "User.Read" };
// Multi-tenant apps can use "common",
// single-tenant apps must use the tenant ID from the Azure portal
var tenantId = "common";
// Value from app registration
var clientId = "0f19c30b-d163-4fac-8fba-6a381f7b6c02";
// using Azure.Identity;
var options = new InteractiveBrowserCredentialOptions
{
TenantId = tenantId,
ClientId = clientId,
AuthorityHost = AzureAuthorityHosts.AzurePublicCloud,
// MUST be http://localhost or http://localhost:PORT
// See https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/wiki/System-Browser-on-.Net-Core
RedirectUri = new Uri("http://localhost"),
};
// https://docs.microsoft.com/dotnet/api/azure.identity.interactivebrowsercredential
var interactiveCredential = new InteractiveBrowserCredential(options);
//Get access token
var token = interactiveCredential.GetToken(new Azure.Core.TokenRequestContext(new[] { "https://graph.microsoft.com/.default" }));
Console.WriteLine("Access token: " + token.Token);
Console.WriteLine("Expires On: " + token.ExpiresOn);
//Get graph client based on interactiveCredential and scope.
var graphClient = new GraphServiceClient(interactiveCredential, scopes);
var me = await graphClient.Me.Request().GetAsync();
Console.WriteLine(me.DisplayName);
Console.ReadLine();
}
}
}
If this is the first time logging into this tenant, you will need to grant permissions to the app:


Once the authentication happens, you will see a similar message in the browser:



and our console window logs the current access token along with the expiry time and also uses the Graph SDK to get the display name of the current user:


Hope that helps!

Tuesday, 9 November 2021

Building a Microsoft Teams Bot: Deep linking to a Teams message from an Adaptive Card button

This is the second article in my "Building a Microsoft Teams Bot" series. In this series, I am planning to write down some interesting things I came across while creating a Microsoft Teams Bot app which is now available on AppSource: https://appsource.microsoft.com/en-us/product/office/WA200002297

Click here to see the previous article in the series: Building a Microsoft Teams Bot: Posting an Adaptive Card carousel as a welcome message

Todays article is around how to create deep links to teams messages from Adaptive cards. This can be useful in scenarios when you want to send users to a specific Teams chat message when they click on an Adaptive Card button:


If you are building the deep link manually, it can be grabbed from the "Copy link" button from the ellipsis menu in a Teams message:


Deep links to personal chats are in a different format compared to channel messages.
 
For Teams messages, the deep link format is:

https://teams.microsoft.com/l/message/{ChannelId}/{messageId}

For personal chats, the deep link format is:

https://teams.microsoft.com/l/message/19:{userAadObjectId}_{botId}@unq.gbl.spaces/{messageId}?context=%7B%22contextType%22:%22chat%22%7D

For bots and messaging extensions, this deep link can be built from parts of the payload sent to the Bot from the Teams platform when the bot is invoked:

view raw deeplink.cs hosted with ❤ by GitHub
Once you have the desired deep link, the next step is to assign it as the URI to an Adaptive Card button:
{
"type": "AdaptiveCard",
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"version": "1.3",
"body": [
{
"type": "ActionSet",
"actions": [
{
"type": "Action.OpenUrl",
"title": "Go to message",
"url": "https://teams.microsoft.com/l/message/19:14ba23dc1081438fb77da64db402bc27@thread.tacv2/1636213162472"
}
]
}
]
}

And that's it! Now you can easily add buttons to your adaptive cards which take the user to specific messages in Teams personal chats or channels. 

Hope this was helpful!

Monday, 1 November 2021

Working with MSAL and multiple Azure AD accounts in a React SPA

I came across an interesting scenario recently: I was working with a React SPA which used Azure AD for authenticating users, and it needed to work with multiple accounts logged in simultaneously. Specifically, we were building an Azure AD multi tenant application which needed to login to multiple M365 and Azure tenants and allow the user to manage all tenants at the same time.

The good thing was that MSAL v2 does support working with multiple accounts at the same time. So in this post, let's see how we are able to do that in a React SPA with MSAL js.

Before looking at the code, we would need to create a multi tenant Azure AD app which would be used to sign in to the different tenants. Step by step instructions can be found here: https://docs.microsoft.com/en-us/azure/active-directory/develop/tutorial-v2-javascript-spa#register-your-application


Once this is in place, we can start looking at the code itself. I have take the MSAL React tutorial as the starting point for this code and modified it to work with multiple accounts. If you want to build it from scratch, this would be a good starting point: https://docs.microsoft.com/en-us/azure/active-directory/develop/tutorial-v2-react

The very first thing we would need is to setup the configuration for our app: 
export const msalConfig = {
auth: {
clientId: "<client-id-of-multi-tenant-aad-app>",
authority: "https://login.microsoftonline.com/organizations",
redirectUri: "http://localhost:3000",
},
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
}
};
// Add scopes here for ID token to be used at Microsoft identity platform endpoints.
export const loginRequest = {
scopes: ["User.Read"]
};
// Add the endpoints here for Microsoft Graph API services you'd like to use.
export const graphConfig = {
graphMeEndpoint: "https://graph.microsoft.com/v1.0/me"
};
view raw msalConfig.ts hosted with ❤ by GitHub
You will notice the authority is set to the /organizations end point. This is because our app is a multi-tenant app which would be used to login to different tenants.

With the config, we will now initiate a PublicClientApplication in the index.tsx file:
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import { PublicClientApplication } from "@azure/msal-browser";
import { MsalProvider } from "@azure/msal-react";
import { msalConfig } from "./authConfig";
const msalInstance = new PublicClientApplication(msalConfig);
ReactDOM.render(
<React.StrictMode>
<MsalProvider instance={msalInstance}>
<App />
</MsalProvider>
</React.StrictMode>,
document.getElementById("root")
);
view raw index.tsx hosted with ❤ by GitHub

Now lets get to the most important App.tsx file: 

import React, { useEffect } from "react";
import { AuthenticatedTemplate, UnauthenticatedTemplate, useMsal, useMsalAuthentication } from "@azure/msal-react";
import Button from "react-bootstrap/Button";
import { InteractionType } from '@azure/msal-browser';
import ProfileContent from "./components/ProfileContent";
const App: React.FunctionComponent = () => {
const request = {
scopes: ["User.Read"],
prompt: 'select_account'
}
const { login, result, error } = useMsalAuthentication(InteractionType.Silent, request);
useEffect(() => {
if(result){
console.log(result);
}
}, [result]);
const { accounts } = useMsal();
function handleLogin() {
login(InteractionType.Redirect, request);
}
return (
<React.Fragment>
<AuthenticatedTemplate>
{accounts.map((account) => {
return <div key={account.homeAccountId}><ProfileContent homeId={account.homeAccountId} name={account.name as string} /></div>
})}
</AuthenticatedTemplate>
<UnauthenticatedTemplate>
<p>No users are signed in!</p>
</UnauthenticatedTemplate>
<Button variant="secondary" onClick={handleLogin}>Sign in new user</Button>
</React.Fragment>
);
}
export default App;
view raw App.tsx hosted with ❤ by GitHub
There are multiple things happening here. Let's unpack them one by one. 

First, we are using the MSAL react useMsalAuthentication hook to setup the authentication and get the login method which we will use later.

What is also important is the prompt: 'select_account' property in the request which would help us login with a new account when we are already signed in with one account.

The AuthenticatedTemplate and UnauthenticatedTemplate MSAL react components help us display different views when at least one user is logged in or no user is logged in respectively.

Next, lets look at the ProfileContent.tsx component:

import React, { useState } from "react";
import { useMsal } from "@azure/msal-react";
import Button from "react-bootstrap/Button";
import { ProfileData } from "./ProfileData";
import { callMsGraph } from "../graph";
import { AccountInfo } from "@azure/msal-common";
import { IPublicClientApplication } from '@azure/msal-browser';
interface IProfileContentProps {
homeId: string;
name: string;
}
const ProfileContent: React.FunctionComponent<IProfileContentProps> = (props: IProfileContentProps) => {
const { instance } = useMsal();
const [graphData, setGraphData] = useState(null);
const account = instance.getAccountByHomeId(props.homeId);
const request = {
scopes: ["User.Read"],
account: account as AccountInfo
};
// Silently acquires an access token which is then attached to a request for Microsoft Graph data
instance.acquireTokenSilent(request).then((response) => {
callMsGraph(response.accessToken).then(response => setGraphData(response));
}).catch((e) => {
instance.acquireTokenPopup(request).then((response) => {
callMsGraph(response.accessToken).then(response => setGraphData(response));
});
});
function handleLogout(instance: IPublicClientApplication, homeId: string) {
const currentAccount = instance.getAccountByHomeId(homeId);
instance.logoutRedirect({
account: currentAccount
});
}
return (
<>
<h5 className="card-title">Welcome {props.name}</h5>
{graphData &&
<ProfileData graphData={graphData} />}
<Button variant="secondary" className="ml-auto" onClick={() => handleLogout(instance, props.homeId)}>Sign out</Button>
</>
);
};
export default ProfileContent;
Based on the homeId of passed in to this component as a property, we are using the PublicClientApplication.acquireTokenSilent method to first get the access token of the relevant user. 

Once the accessToken is fetched, we are making a call to the Microsoft Graph to get the basic details of the user. We are using the callMsGraph function for this.

The ProfileData component takes in all properties fetched from the graph and displays it.

The handleLogout function uses the PublicClientApplication.logoutRedirect function to log out the specific user.

So after everything is in place, we would be able to work with multiple users logged in simultaneously at the same time.

Hope this helps! 

As always, the code for this post can be found on GitHub: https://github.com/vman/ts-msal-react-tutorial