The identity of the calling application could not be established

You are getting the following error from Microsoft Graph or downstream services that uses Microsoft Graph…

The identity of the calling application could not be established

This error is thrown because the “oid” and “sub” claim is missing from the access token. This is because the servicePrincipal does not exist in the tenant or the tenant is not aware of the application.

Partner Scenario

If this is a Partner application, make sure you follow the Partner pre-consent process.

And do not forget to add your application/servicePrincipal to the AdminAgents group.

https://github.com/microsoft/Partner-Center-Explorer/blob/master/docs/Preconsent.md

Here is an updated script for using Microsoft Graph PowerShell

Connect-MgGraph

$AppId = 'INSERT-APPLICATION-ID-HERE'

$g = Get-MgGroup -All -Filter "displayName eq 'AdminAgents'"
$s = Get-MgServicePrincipal -All -Filter "appId eq '$AppId'"

$params = @{
	"@odata.id" = "https://graph.microsoft.com/v1.0/directoryObjects/$($s.id)"
}

New-MgGroupMemberByRef -GroupId $g.id -BodyParameter $params

Non Partner Scenario

Otherwise, the fastest way to resolve this is to add the servicePrincipal to the tenant. But you will still need to consent to the permissions the application may need to use.

You can build an Admin consent URL and will look something like this…

https://login.microsoftonline.com/common/adminconsent?client_id=INSERT-APPLICATION-ID-HERE

Sign in with a Global Administrator account of the tenant in which you are trying to access resources on.

How to sign in as a guest user in Graph Explorer

Besides tenant members, it is also possible to use Graph Explorer signed in as a tenant’s guest user.

In Graph Explorer, add “?tenant=”{tenantname.onmicrosoft.com}” to the URL in the address bar (eg https://developer.microsoft.com/en-us/graph/graph-explorer?tenant=contoso.onmicrosoft.com), and press enter. This will explicitly make Graph Explorer to consider only that tenant when signing in.

Now sign into Graph Explorer as usual, with a guest user’s credentials. Afterwards, try making this GET request: “https://graph.microsoft.com/v1.0/me”. In the response, the user’s “userPrincipalName” value should contain “EXT”, meaning the user currently signed in is a guest user in the tenant.

Using the Application.ReadWrite.OwnedBy API permission

You have an application, when authenticated, and you want to be able to update its own properties such as the Client Secret or Certificate.

The Application.ReadWrite.OwnedBy allows the application to manage applications in which it is a owner of. Otherwise meaning if you want to update its own properties, it would be have to an owner of itself. You can do this using the Microsoft Graph API:

https://docs.microsoft.com/en-us/graph/api/application-post-owners?view=graph-rest-1.0&tabs=http

For more information about Application.ReadWrite.OwnedBy:

https://docs.microsoft.com/en-us/graph/permissions-reference#application-permissions-4

Let’s get started!

Grab the Application Object ID (Not the Application ID):

This is the application you want to add the owner to…

  1. Within the Azure portal @ https://portal.azure.com
  2. Navigate to Azure Active Directory, then to App Registrations (not Enterprise applications)
  3. Find your app and observe the Object ID as shown below. We will need this later.

Grab the ServicePrincipal Object ID

This will be the owner

  1. Within the Azure portal @ https://portal.azure.com
  2. Navigate to Azure Active Directory, then to Enterprise applications (not App registrations)
  3. Find your app (this is the owner) and observe the Object ID as shown below. We will need this later.

If you want to use Microsoft Graph Explorer:

  1. Go to https://developer.microsoft.com/en-us/graph/graph-explorer
  2. Sign in with a user who has the permissions to update application owners, such as a Global or Application Administrator.
  3. The Microsoft Graph API call will look something like this:
POST https://graph.microsoft.com/v1.0/applications/{application-object-id}/owners/$ref

Content-type: application/json
{

  "@odata.id": "https://graph.microsoft.com/v1.0/directoryObjects/{service-principal-id}"
}

So, in Microsoft Graph Explorer, it would look something like this:

If you get a Forbidden – 403 message.

"error": {
  {
    "code": "Authorization_RequestDenied",
    "message": "Insufficient privileges to complete the operation.",
    "innerError": {
    "date": "2021-12-09T17:41:54",
    "request-id": "b1909fc0-aa5c-4b43-8a1f-xxxxxxxxxxxx",
    "client-request-id": "836e08bb-a12d-4ade-c761-xxxxxxxxxxxx"
  }
}

You may need to consent to API permissions for Microsoft Graph Explorer. Click on Modify permissions and consent to one of the following permissions:

If you want to use Microsoft Graph PowerShell:

Connect-MgGraph -Scopes Application.ReadWrite.All

# Owner
$OwnerServicePrincipalObjectId = "96858eb3-xxxx-xxxx-xxxx-33a6b0dc2430"

# Application to add owner to
$ApplicationObjectId = "b7463aa1-xxxx-xxxx-xxxx-0963d6c00485"

$Owner = @{
    "@odata.id" = "https://graph.microsoft.com/v1.0/directoryObjects/$($OwnerServicePrincipalObjectId)"
}

New-MgApplicationOwnerByRef -ApplicationId $ApplicationObjectId -BodyParameter $Owner

Azure Active Directory: How to get the signed in users groups when there is a groups overage claim in an Access token.

Azure AD has a maximum number of groups that can be returned in an access token when you have selected to include the groups claim for your access token. This post will show you how to reproduce the scenario and then how to get the users groups using Microsoft Graph when a groups overage claim is present in the token instead of actual groups.

For a JWT token, Azure has a limit of 200 groups that can be present in the token. If the user is a member of more than 200 groups when requesting an access token for the resource that has the groups claim configured on it, instead of getting the groups you will get an overage claim, which is basically a URL that should be called to get the groups list instead. Please read about the “groups” claim in this document. The current groups claim is using the AAD Graph endpoint ( https://graph.windows.net…. ) and since this endpoint is being deprecated, we are transforming the claim to be the Microsoft Graph endpoint instead.

Configure access tokens for the groups claim

Optional claims can be configured from the Azure Portal to include Groups. Please see this document to enable the groups claim. For access tokens, you would need to configure the groups claim in the app registration for your API. If this is a 1st party app ( Microsoft App ) you will not be able to configure the groups claims — you will only be able to configure this with your own app registration. If you want the claim in the client application, you would have to configure it in the ID token instead.

Reproduce the scenario

Step 1: Configure an app registration for this sample app.
Since the application will be performing both a public client flow and a confidential client flow, you will need to configure a web redirect ( for the public client flow ) and a client secret ( for the confidential client flow ). As well, the confidential client version will need the Microsoft Graph application permission of Group.Read.All. The reason being, the confidential client must go to the users endpoint and look up the groups based on a user id, which we get from the initial sign-in token. The public client will just go to the ‘me’ endpoint, since there is a user context.

Step 2: Run the PowerShell script to recreate the scenario ( if needed )

The sample project has a text file called Create_TestGroup.ps1.txt. You can download it from the Git Repository here. To run the script, remove the .txt extension. Also, before running it, you will need an object id of a user to add to the test groups. You must be in a directory role that can create groups and add users to the groups. The sample will create 255 groups with a format of “TEST_0001”, “TEST_0002”, etc. and the object id will be added that you provide to each group.

At the bottom of the script, you will see that it will log you into Azure, then run the command to create the groups and then log you back out. There is a sample cleanup method that is commented out at line 53:

Sample project

The sample project can be located in this Git Repository.

The sample project must first be configured to work with your tenant. Once that is configured, you will need to update the “appsettings.json” file with the appropriate values:

For the AppScopes, the key is that you must have a scope for which the groups claim has been configured. Normally, this would be an api in your tenant but in this case, adding the Azure SQL database with the user_impersonation permission works for this scenario and the scope I have works with that API. This is because the groups claim has been configured on that API already.

For the Graph Scopes, add the application permissions “Group.Read.All” ( needed to get the group display name ) and “User.Read.All” ( needed to get the groups list using the client credentials flow ) You must provide admin consent for those permissions. The delegated permission “User.Read” should already be there but if not, please add that as well.

Once the app registration is configured, plug in the client id ( application id ), client secret, tenant id into the .net applications appsettings.json file.

Running the application

The application is a console application so authentication will happen in a browser window. Once you have signed in, you can close that browser window and you will then be brought back to the .Net console application.

The access token is presented so you can copy that to the clipboard and go to https://jwt.ms and paste it there to see the encoded token. It will just be a user token. However, if the groups overage claim is present in that token because the user is a member of too many groups, then the console app will display the original group overage url as well as the new group overage url that will be used in the http client request. That endpoint will not be used in the graph sdk request. The next step in the flow of the application is to get an access token for Microsoft Graph. You can get an access token using the currently signed in users refresh token ( a delegated token ) or get an access token using the client credentials grant flow ( an application token ). Make a selection for the type of token you want to get.

Next, you will then be given a chance as to choice which method to use to get the groups. This sample is going to demonstrate both using the .Net HTTP Client and also the Graph SDK client.

The groups will then be displayed in the console window:

About the Code

This application is using MSAL.Net ( Microsoft.Identity.Client ) for authenticating the user and acquiring access tokens. It is using System.Net.Http for the http client and Microsoft.Graph sdk for the graph client. To parse the JSON, System.Text.Json and for getting the claims from the token, it is using System.IdentityModel.Tokens.Jwt.

Getting the groups overage claim in the token is done with the JwtSecurityToken provider. The premise of the method I have to get the claim is simple, if there exists in the token the claims “_claim_names” and “_claim_sources” then there is a group overages claim in the token and I simply get the user id ( oid ) and build the url to call for the groups list and return that value. For educational purposes only, I am displaying the original value in the console. If either of those 2 values do not exist, then the try/catch block will handle the error and a string.empty value is returned instead to let the caller know that there is not a groups overage claim in the token.

		/// <summary>
		/// Looks for a groups overage claim in an access token and returns the value if found.
		/// </summary>
		/// <param name="accessToken"></param>
		/// <returns></returns>
		private static string Get_GroupsOverageClaimURL(string accessToken)
        {
			JwtSecurityToken token = new JwtSecurityTokenHandler().ReadJwtToken(accessToken);
			string claim = string.Empty;
			string sources = string.Empty;
			string originalUrl = string.Empty;
			string newUrl = string.Empty;

            try
            {
				// use the user id in the new graph url since the old overage link is for aad graph which is being deprecated.
				userId = token.Claims.First(c => c.Type == "oid").Value;

				// getting the claim name to properly parse from the claim sources but the next 3 lines of code are not needed,
				// just for demonstration purposes only so you can see the original value that was used in the token.
				claim = token.Claims.First(c => c.Type == "_claim_names").Value;
				sources = token.Claims.First(c => c.Type == "_claim_sources").Value;
				originalUrl = sources.Split("{\"" + claim.Split("{\"groups\":\"")[1].Replace("}","").Replace("\"","") + "\":{\"endpoint\":\"")[1].Replace("}","").Replace("\"", "");
				
				// make sure the endpoint is specific for your tenant -- .gov for example for gov tenants, etc.
				newUrl = $"https://graph.microsoft.com/v1.0/users/{userId}/memberOf?$orderby=displayName&$count=true";

				Console.WriteLine($"Original Overage URL: {originalUrl}");
				//Console.WriteLine($"New URL: {newUrl}");


			} catch {
				// no need to do anything because the claim does not exist
			} 

			return newUrl;
        }

There is a public client application configuration for signing in a user and getting access tokens and a Confidential Client Application for signing in as an application and getting access tokens ( the client credentials grant flow ). I am using just a simple ManualTokenProvider for the Graph Service Client to pass the service an access token verses having graph obtain an access token.

There is also an appsettings file and a class to store those settings ( AzureConfig.cs ) during runtime. The public static property AzureSettings will pull the settings from teh config file using a configuration builder ( similar to the asp.netcore applications ). I had to add this in since it isn’t native to a console application.

		static AzureConfig _config = null;
		public static AzureConfig AzureSettings
		{
			get
			{
				// only load this once when the app starts.
				// To reload, you will have to set the variable _config to null again before calling this property
				if (_config == null)
				{
					_config = new AzureConfig();
					IConfiguration builder = new ConfigurationBuilder()
						.SetBasePath(System.IO.Directory.GetCurrentDirectory())
						.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
						.Build();

					ConfigurationBinder.Bind(builder.GetSection("Azure"), _config);
				}

				return _config;
			}
		}

For the Authentication provider for the Graph service client, I will just be using a custom manual token provider so that I can set the access token for the client since I am already obtaining access tokens using MSAL.

using Microsoft.Graph;

using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;

namespace MSAL.Net_GroupOveragesClaim.Authentication
{
    class ManualTokenProvider : IAuthenticationProvider
    {
        string _accessToken;

        public ManualTokenProvider ( string accessToken)
        {
            _accessToken = accessToken;
        }

        async Task IAuthenticationProvider.AuthenticateRequestAsync(HttpRequestMessage request)
        {
            request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", _accessToken);
            request.Headers.Add("ConsistencyLevel", "eventual");
        }
    }
}

The HTTP method has 2 parts, the method “Get_Groups_Http_Method” will call “Graph_Request_viaHTTP” to get the list of groups and then display’s that list in the console window.

		/// <summary>
		/// Entry point to make the request to Microsoft graph using the .Net HTTP Client
		/// </summary>
		/// <param name="graphToken"></param>
		/// <returns></returns>
		private static async Task Get_Groups_HTTP_Method(string graphToken, string url)
        {
			List<Group> groupList = new List<Group>();
						
			groupList = await Graph_Request_viaHTTP(graphToken, url);
			foreach (Group g in groupList)
			{
				Console.WriteLine($"Group Id: {g.Id} : Display Name: {g.DisplayName}");
			}
		}
		/// <summary>
		/// Calls Microsoft Graph via a HTTP request.  Handles paging in the request
		/// </summary>
		/// <param name="user_access_token"></param>
		/// <returns>List of Microsoft Graph Groups</returns>
		private static async Task<List<Group>> Graph_Request_viaHTTP(string user_access_token, string url)
        {
			string json = string.Empty;
			//string url = "https://graph.microsoft.com/v1.0/me/memberOf?$orderby=displayName&$count=true";
			List<Group> groups = new List<Group>();

			// todo: check for the count parameter in the request and add if missing

			/*
			 * refer to this documentation for usage of the http client in .net
			 * https://docs.microsoft.com/en-us/dotnet/api/system.net.http.httpclient?view=net-5.0
			 * 
			 */

			// add the bearer token to the authorization header for this request
			_httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue( "Bearer", user_access_token);
			
			// adding the consistencylevel header value if there is a $count parameter in the request as this is needed to get a count
			// this only needs to be done one time so only add it if it does not exist already.  It is case sensitive as well.
			// if this value is not added to the header, the results will not sort properly -- if that even matters for your scenario
			if(url.Contains("&$count", StringComparison.OrdinalIgnoreCase))
            {
                if (!_httpClient.DefaultRequestHeaders.Contains("ConsistencyLevel"))
                {
					_httpClient.DefaultRequestHeaders.Add("ConsistencyLevel", "eventual");
                }
            }
			
			// while loop to handle paging
			while(url != string.Empty)
            {
				HttpResponseMessage response = await _httpClient.GetAsync(new Uri(url));
				url = string.Empty; // clear now -- repopulate if there is a nextlink value.

				if (response.IsSuccessStatusCode)
				{
					json = await response.Content.ReadAsStringAsync();

					// Console.WriteLine(json);

					using (JsonDocument document = JsonDocument.Parse(json))
					{
						JsonElement root = document.RootElement;
						// check for the nextLink property to see if there is paging that is occuring for our while loop
						if (root.TryGetProperty("@odata.nextLink", out JsonElement nextPage))
                        {
							url = nextPage.GetString();
                        }
						JsonElement valueElement = root.GetProperty("value"); // the values

						// loop through each value in the value array
						foreach (JsonElement value in valueElement.EnumerateArray())
						{
							if (value.TryGetProperty("@odata.type", out JsonElement objtype))
							{
								// only getting groups -- roles will show up in this graph query as well.
								// If you want those too, then remove this if filter check
								if (objtype.GetString() == "#microsoft.graph.group")
								{
									Group g = new Group();

									// specifically get each property you want here and populate it in our new group object
									if (value.TryGetProperty("id", out JsonElement id)) { g.Id = id.GetString(); }
									if (value.TryGetProperty("displayName", out JsonElement displayName)) { g.DisplayName = displayName.GetString(); }

									groups.Add(g);
								}
							}
						}
					}
				} else
                {
					Console.WriteLine($"Error making graph request:\n{response.ToString()}");
                }
			} // end while loop
	
			return groups;
        }

In a similar fashion, the Graph sdk has an entry method ( “Get_Groups_GraphSDK_Method” ) that will call “Get_GroupList_GraphSDK” to get the list of groups and then display them in the console window.

		/// <summary>
		/// Entry point to make the request to Microsoft Graph using the Graph sdk and outputs the list to the console.
		/// </summary>
		/// <param name="graphToken"></param>
		/// <returns></returns>
		private static async Task Get_Groups_GraphSDK_Method(string graphToken, bool me_endpoint)
        {
			List<Group> groupList = new List<Group>();

			groupList = await Get_GroupList_GraphSDK(graphToken, me_endpoint);
			foreach (Group g in groupList)
			{
				Console.WriteLine($"Group Id: {g.Id} : Display Name: {g.DisplayName}");
			}
		}

To get the group list, there is logic to determine if we are going to use the “me” endpoint to get the group list or the “users” endpoint. If you used the client credentials grant flow to get the access token for Microsoft Graph, then it will be using the “users” endpoint. If not ( i.e. a delegated flow was used for the access token ) then it will be using the “users” endpoint.

		/// <summary>
		/// Calls the Me.MemberOf endpoint in Microsoft Graph and handles paging
		/// </summary>
		/// <param name="graphToken"></param>
		/// <returns>List of Microsoft Graph Groups</returns>
		private static async Task<List<Group>> Get_GroupList_GraphSDK(string graphToken, bool use_me_endpoint)
        {

			GraphServiceClient client;

			Authentication.ManualTokenProvider authProvider = new Authentication.ManualTokenProvider(graphToken);

			client = new GraphServiceClient(authProvider);
			IUserMemberOfCollectionWithReferencesPage membershipPage = null;

			HeaderOption option = new HeaderOption("ConsistencyLevel","eventual");

			if (use_me_endpoint)
            {
                if (!client.Me.MemberOf.Request().Headers.Contains(option))
                {
					client.Me.MemberOf.Request().Headers.Add(option);
                }

				membershipPage = await client.Me.MemberOf
					.Request()
					.OrderBy("displayName&$count=true") // todo: find the right way to add the generic query string value for count
					.GetAsync();
            } else
            {
                if (!client.Users[userId].MemberOf.Request().Headers.Contains(option))
                {
					client.Users[userId].MemberOf.Request().Headers.Add(option);
                }

				membershipPage = await client.Users[userId].MemberOf
					.Request()
					.OrderBy("displayName&$count=true")
					.GetAsync();
            }

			List<Group> allItems = new List<Group>();			
			
			if(membershipPage != null)
            {
				foreach(DirectoryObject o in membershipPage)
                {
					if(o is Group)
                    {
						allItems.Add((Group)o);
                    }
                }

				while (membershipPage.AdditionalData.ContainsKey("@odata.nextLink") && membershipPage.AdditionalData["@odata.nextLink"].ToString() != string.Empty)
                {
					membershipPage = await membershipPage.NextPageRequest.GetAsync();
					foreach (DirectoryObject o in membershipPage)
					{
						if (o is Group)
						{
							allItems.Add(o as Group);
						}
					}
				}

            }

             return allItems;

		}

Regardless of the method used, the code will handle paging since, by default, only 100 records per page will be returned. Paging is determined via the “@odata.nextLink” value. If there is a value for that property, then that full url is called for the next page of data. See this document for more information about paging.

Summary

Access tokens have a limit to how many groups will appear in the token. When the limit is exceeded, Azure will provide the “groups overage claim” which is a URL to call in order to get the list of groups that normally would have been present in the token. This blog post gives 2 examples how you can obtain the group list for the currently signed in user.

Why /memberOf Microsoft Graph API returning null fields for some attributes.

What do below API calls do?

https://graph.microsoft.com/v1.0/me/memberOf

https://graph.microsoft.com/v1.0/users/{id | userPrincipalName}/memberOf

These API calls gives us the list of groups and directory roles that the user is a direct member of.

API Call: GET

JSON response:

{
    "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#directoryObjects",
    "value": [
        {
            "@odata.type": "#microsoft.graph.group",
            "id": "b0a133d4-3f3d-4990-be22-879151155f19",
            "deletedDateTime": null,
            "classification": null,
            "createdDateTime": null,
            "creationOptions": [],
            "description": null,
            "displayName": null,
            "expirationDateTime": null,
            "groupTypes": [],
            "isAssignableToRole": null,
            "mail": null,
            "mailEnabled": null,
            "mailNickname": null,
            "membershipRule": null,
            "membershipRuleProcessingState": null,
        }
}

What is the reason behind seeing null values?

When we make a call to https://graph.microsoft.com/v1.0/me/memberOf or https://graph.microsoft.com/v1.0/users/{id | userPrincipalName}memberOf in certain situations we find null values for few attributes as shown in the above JSON response. In the above response, we only see the value for GroupId and all other attributes are of null values. When an application queries the membership of a container object and does not have permission to read a certain type, members of that type are returned but with limited information. The application receives a 200 response and a collection of objects. Complete information is returned for the object types that the application has permissions to read. For the object types which the application does not have permission to read, only the object type and ID are returned.

Resolution:

When an application queries a relationship that returns a directoryObject type collection, if it does not have permission to read a certain derived type, members of that type are returned but with limited information. To read all the other group information, you would also need to configure at least Directory.Read.All permission in your application.

References:

List memberOf

Limited information returned for inaccessible member objects

Receiving Error “AADSTS900439 – USGClientNotSupportedOnPublicEndpoint”

Problem:

This blog provides more information about the error “AADSTS900439 – USGClientNotSupportedOnPublicEndpoint”.  This error typically occurs when a user uses a public cloud endpoint to sign in to an application registered in Azure Government sovereign cloud.

It is known fact that the official Azure Active Directory (AAD) Authority for Azure Government changed from `https://login-us.microsoftonline.com` to `https://login.microsoftonline.us`. This change also applied to Microsoft 365 GCC High and DoD, which Azure Government AAD also services.

Azure AD will now start enforcing the correct sign in endpoint.  One can no longer sign in to an application registered in a Azure Government cloud using the public .com endpoint.  The error happens due to this violation.

Here is the documentation on Azure Government Endpoint Mappings, which shows the mapping between some Azure services and Azure Government endpoints. Few to mention:

NameAzure Gov Endpoint
Portalhttps://portal.azure.us
Microsoft Graph APIhttps://graph.microsoft.us/
Active Directory Endpoint and Authorityhttps://login.microsoftonline.us

Each national cloud environment is unique and different than the Microsoft global environment. It is important to be aware of some of these below key differences when you develop applications for national cloud environments.

For example:- Registering Applications – App Registration Endpoints, Acquiring Tokens – AAD Authentication Endpoints, and calling the Microsoft Graph API can be different.

This article provides information about the different Microsoft Graph national cloud deployments and the capabilities that are available to developers within each.

Here is the sample for implementation : https://blogs.aaddevsup.xyz/2020/06/configure-net-application-to-call-microsoft-graph-in-a-national-cloud-tenant/

References:

Segment Users in Azure AD

If you have been using Microsoft Graph API to add or modify users in Azure Active Directory (Azure AD) you may have noticed that when you create a new user it lives with all the other users, some of which may have nothing to do with your application. Ideally, you may want a sub-directory or business unit of sorts. Fortunately, there are ways to segment these users in a more practical way. There are currently two ways to do this: Groups and Administrative Units.

GROUPS

Groups are pretty straight forward and the name says it all. You can create a basic group using the Azure AD portal. See here for detailed instructions on how to create a basic group and add members.

ADMINISTRATIVE UNITS

An administrative unit is an Azure AD resource that can be a container for other Azure AD resources. In this preview release, these resources can be only users. For example, an administrative unit-scoped User account admin can update profile information, reset passwords, and assign licenses for users only in their administrative unit.

You can use administrative units to delegate administrative permissions over subsets of users and applying policies to a subset of users. You can use administrative units to delegate permissions to regional administrators or to set policy at a granular level.

Check out these demo scripts to help get you started and the MS Graph API administrativeUnit resource type. You can find more information about Administrative Units here.

NOTE:

ADMINISTRATIVE UNITS IS CURRENTLY IN PREVIEW AND CAN ONLY BE DONE VIA POWERSHELL CMDLETS OR WITH MS GRAPH BETA REST ENDPOINT AT THE MOMENT.

How to Use the ADAL .NET library to Call the Microsoft Graph API in a Console Application (Using Authorization Code Flow)

Introduction

This post is to show how to use the ADAL .NET library to acquire a token interactively in a console application. It will go through setting up an Azure Active Directory Application, setting up the .net console application, acquiring an access token, and then make a HTTP request using the token acquired from the ADAL .net library to get 5 users using the Microsoft Graph API.

 

Setting up your Azure Active Directory Application Registration

First we will need to setup an Azure Active Directory Application Registration in your Azure Portal. You can do this by going to the Azure Portal > Azure Active Directory > App Registrations > Add Application.

 

 

 

Then set the Application Registration to a native app, and put in your own respective values for redirect url and name. The redirect URL will not matter, it will only be important to remember for when you are asking for an access token, and the name is up to you.

Note: It is important to make it a Native Application, otherwise you will get errors saying that there is no Client Secret

 

After you have created the Application, keep track of the Application ID for later. You will need it to authenticate with this AAD Application Registration, then go to required permissions >Add > select an API > Microsoft Graph API > select view users’ basic profile > and press grant permissions.

For more information on which permissions you need and how to grant permissions in different ways, please refer to these posts :

https://blogs.msdn.microsoft.com/aaddevsup/2018/05/21/finding-the-correct-permissions-for-a-microsoft-or-azure-active-directory-graph-call/

https://blogs.msdn.microsoft.com/aaddevsup/2018/05/08/receiving-aadsts90094-the-grant-requires-admin-permission/

 

Setting up the console application

To start, let’s create a new project, install the correct NuGet packages, and fix reference assembly issues.

In this tutorial I will be using a new project with the template Console App (.net framework).

 

 

 

Now that we have a fresh new console .net application, we will need to install some libraries in order to get an access token from Azure Active Directory. The ADAL libraries are Microsoft maintained libraries that we will need to get for our .net console application.

We can do this by going to our NuGet Manager and picking up the correct libraries.

In order to do this you will want to right click on the solution, and go to Manage NuGet Packages for Solution.

In this example we will be using the NuGet pacakages below. You can search and then download them via the Browse section of the NuGet Manager.

Id Versions
— ——–
Microsoft.IdentityModel.Clients.ActiveDirectory {4.3.0}
Microsoft.IdentityModel.JsonWebTokens {5.3.0}
Microsoft.IdentityModel.Logging {5.3.0}
Microsoft.IdentityModel.Tokens {5.3.0}
Newtonsoft.Json {11.0.2}
System.IdentityModel.Tokens.Jwt {5.3.0}

 

 

 

 

The ADAL libraries are the Microsoft.IdentityModel libraries, however the other two libraries are being used for formatting JWT tokens and JSON tokens to be a readable print. This is to make our lives easier later on once we get the access token and JSON response.

After you have installed all the NuGet libraries, you may still need to fix a reference assembly for System.Web.Script.Serialization.

In order to do this, right click on the references in the solution explorer and then click add reference. We will then want to add the System.Web.Extensions, as shown in the picture below.

 

Variable Initialization

Now that we have all the correct libraries and references installed into our solution, you will want to declare some variables for your AAD token acquisition.

You’ll need to declare the variables listed below. In this example we will be accessing the Microsoft Graph API, so the resource URI has already been declared.

You will need to replace the client id(Application ID), redirect URI, and tenant id with your own respective information. You can get the tenant ID from your Azure Active Directory portal in the overview section. You will need to get the client id and redirect URI from the first steps that we followed when creating the AAD Application.

After you have initialized the variables we can setup our main method with some empty methods. I have setup my main method as below. First we will get our access token, and then make the call to the Microsoft Graph with the access token to retrieve 5 users.

Once we have the variables declared and our main skeleton method setup, we can start on our Access Token Call method, shown below. First we initialize the authcontext which is the variable that maintains the environment for the ADAL library. In this setup we try to acquire the access token three times if it fails. Once you have initialized the authcontext, you can simply make the call :

authContext.AcquireTokenAsync(resourceUri, clientId, new Uri(redirectUri), new PlatformParameters(PromptBehavior.Auto));

Which will get the access token. In this method we also pretty print the JWT token, so that you can read the claims in the console.

The pretty print method can be found here :

That defines the getAccessToken() method.

 

Now, we will need to flesh out the getMembers() method.
This method can be seen below, first we add the access token we just got to the headers of the HTTP client, then we make the Microsoft graph call using a getAsync method call from the HTTP client class. The documentation on the Microsoft Graph list users call can be read here :

https://developer.microsoft.com/en-us/graph/docs/api-reference/v1.0/api/user_list

In addition to getting the users, we have set the OData V4 $top requirement, in order to get only the first 5 users. In order to continue getting the next 5 users, you can utilize the skip token that is sent back in the response body.
More documentation on this can be found here :
https://developer.microsoft.com/en-us/graph/docs/overview/query_parameters
https://developer.microsoft.com/en-us/graph/docs/concepts/paging

 

After getting the JSON response, we utilize the prettify JSON method in order to clean up and easily print out the JSON response.

 


Now we can run our console application, and we will get 5 users from the Microsoft Graph API.

Note: The account you login with will need the permissions to read all basic user profiles in order to properly call the Microsoft Graph API call as this is an on behalf of the user flow.

Conclusion

After going through all of this we have setup an AAD App registration that we can connect to using ADAL .NET. We have filled out the code to acquire an access token using ADAL and then make a HTTP request using the HTTP Client to get 5 users from the Microsoft Graph API.

If you have any issues with the code, feel free to open up a GitHub issue on the repository to address any coding issues. If you are having an issues with AAD Access, please open up a support ticket and one of our support engineers will reach out as soon as possible.

The full GitHub Repository of this code can be found here : https://github.com/frankhu1234/ADAL-.NET-Console-Application

Unable to Modify User Email, Phone Number, Password or Other Personal Information for Azure Active Directory Users

Introduction

This post is in regards to the issues in regards to users having issues modifying Azure Active Directory User attributes such as mail, phone number, resetting passwords, or other personal attributes in user accounts. This will review the reason behind these changes and how to resolve the issue. For many users this was something that was working before and only recently stopped working properly.

 

Reason Behind Change

There was a recent change to three different attributes that made changing the attributes require the same elevated privileges that password reset requires. The only properties that are being affected are the attributes : mobilePhone, businessPhones/telephoneNumber, and otherMails attributes. User profile changes can be made with User.ReadWrite.All except for the 3 aforementioned properties.

 

Fix/Resolution

In order to resolve this issue you will need to set the Service Principal or User that is trying to make the change to a Helpdesk Admins, User Account Admins and Company Admins depending on the user you are trying to modifies role is. Only these three admins can make changes to these three attributes in Azure Active Directory now.

Please note the level of power you are giving the service principal by setting the service principal or user to one of the aforementioned roles, realize that you are giving the user/service principal the ability to perform tasks at that level. This should be done with caution.

 

Microsoft Graph Scenario

Most users experiencing this issue are Microsoft Graph or Azure Active Directory users that are utilizing the Grant Type Client Credentials in order to make modifications to the three mentioned User Attributes. Having the Directory.readwrite.all permission is now not sufficient to make modifications to these user attributes anymore. You will get a 403 error saying insufficient permissions. In order to resolve this issue you can set the Service Principal/Enterprise Application as one of the admin roles in the resolution stated in the last paragraph.

For help on giving a Service Principal an Admin Role please go through this post : https://blogs.msdn.microsoft.com/aaddevsup/2018/08/29/how-to-add-an-azure-ad-role-to-a-enterprise-application-service-principal/

 

Conclusion

Here we have gone over the User Attribute change’s reasoning, how to resolve the issue, the Microsoft/AAD Graph Scenario, and a link explaining how to give a Service Principal/Enterprise Application an Admin role. If you have anymore questions in regards to this issue, feel free to comment below on this issue and I will try to get back to you as soon as possible. If you have any dire issues feel free to open a support ticket for Azure Active Directory Developer, and one of our support engineers will reach out to you to resolve the issue as soon as possible.

Using Postman to call the Microsoft Graph API using Authorization Code Flow

Introduction

This article will help guide you through utilizing Postman to call a Microsoft Graph Call using the authorization code flow. This is part of a 5 part blog on accessing the Microsoft Graph API utilizing grant types : authorization code, implicit flow, client credentials, password, and refresh token flow. We will be utilizing the same Microsoft Graph call to reduce extraneous details on having to include setting up and finding the correct permissions for every Microsoft Graph Calls while still maintaining the consistency of setting up for the entire Microsoft Graph Call from start to finish.

 

Setting Up the AAD Application

The first step to getting access to the Microsoft Graph REST API is to setup an AAD Application Registration.

First we are going to want to create the AAD Application registrations in the portal. For this we will need to configure the application to be able to work with Postman so that we can make the call to the Microsoft Graph API. First we go to the Azure Active Directory Blade, go to App Registrations, and then create a new application registration.

2018-03-31 19_16_00-Create - Microsoft Azure - Internet Explorer

From there we are going to want to create a web app with any name. Here I have set the name as web app and then we want to set the callback url to : https://www.getpostman.com/oauth2/callback and set the application type to web app/ API.

Note: that you can set whatever URLs you would like

 

image

You will have to click out of the sign-on URL to make it check whether or not if it’s correct.

After that we have created our web app, we will want to create a secret. Please keep track of the secret as you won’t be able to see the secret again. You will have to press save in order for the secret to generate.

image

With this information in hand, we will be able to move forward and connect to this AAD registration. But without the correct permissions we won’t be able to get an access token to make calls to the Microsoft Graph API.

 

Finding Which Permissions We Need for a Microsoft Graph Call Using Authorization Code Flow

Assuming we would like to have granular control on what the AAD Application registration has access to and what it doesn’t have access to. We are going to want to make sure that the AAD Application registration only has the permissions it needs to make the Microsoft Graph API calls that we are wanting to make.

There has been a separate blog post on finding the correct permissions for your graph API call listed below :

https://blogs.msdn.microsoft.com/aaddevsup/2018/05/21/finding-the-correct-permissions-for-a-microsoft-or-azure-active-directory-graph-call/

 

For this Authorization Code flow, we will want to set the required permission for Read all users’ full profiles under Delegated Permissions. You can utilize the Application Permission as well, however you won’t get the permissions based on the user logging in, instead you will receive the permission on behalf of the Application.

 

 

 

Retrieving an Access token Using Authorization Code Grant Type Flow

When using the Authorization Code flow to get the access token, the preview feature of postman when requesting for an HTML page doesn’t properly load the HTML page. In addition to that I’m not sure if the preview feature would even properly add the cookies to Postman, so you won’t be able to make requests to the authorization endpoint and get the authorization code back and send that to the token endpoint.

 

However, Postman does include a way to get an Access token via OAuth2’s Authorization Code Grant type by going to the authorization tab in Postman and then requesting a new access token.

 

After opening up Postman click on the authorization tab shown in the picture below. After that, click on the highlighted drop down menu.

 

image

 

After clicking on the menu, we will want to click on OAuth 2.0

 

image

 

This will now change the User Interface and there will be a “Get a New Access Token” button on the right side now. Click on the button on the right side and that will open a new pop up section.

 

image

 

You will now be able to choose your grant type, this article is meant to follow the grant type authorization code.

 

The callback URL will be your first reply URL for your AAD Application Registration, I have set mine to orange.com.

The Auth URL will be the auth endpoint for the tenant that your AAD Application Registration is in. You can find this in the picture below in your AAD App Registration blade.

Note that you will need to add the resource you are asking access to as a query parameter in your auth url. For example: https://login.microsoftonline.com/8839a17c-5ebf-496f-858e-0bd6c3038589/oauth2/authorize?resource=https://graph.microsoft.com This auth url is asking for authorization to get access to the Microsoft Graph.

image

 

The Access token URL is highlighted in the picture above, the OAuth 2.0 token endpoint URL.

The client ID is the application ID/Client ID for your AAD Application Registration. This is found when you first enter the blade for your AAD Application.

The client secret can be found by following the directions described here : https://blogs.msdn.microsoft.com/aaddevsup/2018/04/25/how-to-get-to-the-keyssecrets-from-azure-active-directory/

Note: There are some issues with Postman and utilizing the “Get New Access Token feature” when the client secret has a # and +. So you will need to continue to get a new secret until it doesn’t have a + or # symbol in the client secret. This issue is described in the GitHub issue : https://github.com/postmanlabs/postman-app-support/issues/4555

The Client Authentication will work using either option, here I’m using “Send Client Credentials in Body”

image

 

 

Now when you click on request token, an interactive pop up will show asking you to login. After you login with your username and password, it will then automatically go through the flow and send the authorization code to the token endpoint. After logging in you will receive the Access token, and it will look like the picture below.

 

image

 

Now that you have the access token you will want to add it to your headers. Postman will do this for you, but you have to remember to scroll down in the “Manage Access Tokens” frame and press “Use Token”.

image

 

 

 

Conclusion

We have gone through the steps to get an access token utilizing postman’s feature to request access tokens from the token endpoint by getting the authorization code from the authorization endpoint. If you would like to learn more about how the OAuth 2.0 flow works in terms of AAD Web Applications please take a look at this documentation that reviews how it works : https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-protocols-oauth-code

 

If you have anymore issues feel free to open a support ticket and one of our engineers will reach out to you to resolve the issue.