What is the Context Behind Our GraphServiceClient Authentication Challenge?
While working on a large-scale data processing pipeline for a FinTech enterprise, we encountered a significant security and architectural debt issue. The system relied on Azure Windows VMs hosting heavy backend batch applications that interacted extensively with Microsoft Graph to read organizational data, process files and generate automated compliance reports.
Historically, this setup utilized an Enterprise App that granted delegated permissions to Graph. The batch jobs initialized a .NET GraphServiceClient using Username and Password authentication. From an engineering standpoint, utilizing the Resource Owner Password Credential (ROPC) flow is highly discouraged, not just because it bypasses modern conditional access policies, but because it introduces credential management overhead and security vulnerabilities.
The mandate was clear: modernize the infrastructure to a completely secretless architecture using a User-Assigned Managed Identity (UMI). This scenario inspired this article to help other engineering teams navigate the nuances of Azure identity routing, especially when decision-makers want to preserve legacy Enterprise Application boundaries without relying on shared or rotating credentials.
How Did the Microsoft Graph Permission Workflow Become a Problem?
The business use case required the VM-based batch processes to operate autonomously (as a daemon) without human intervention. The immediate, straightforward architectural choice is to create a User-Assigned Managed Identity (UMI), attach it to the Azure VMs and grant that UMI direct access to the required Microsoft Graph API endpoints.
However, the enterprise security and architecture teams presented a challenging constraint. They wanted to maintain centralized governance over Microsoft Graph permissions via a specific Azure App Registration/Enterprise App. Their proposed workflow was: grant the UMI access to the Enterprise App and then have the Enterprise App broker the connection to Microsoft Graph with Application-level permissions.
This request aimed to contain all Microsoft Graph access rights within a single, trackable Enterprise App rather than distributing App Roles across multiple User-Assigned Managed Identities. The challenge was figuring out how to construct this routing in .NET using GraphServiceClient without falling back to generating a Client Secret for the Enterprise App.
What Went Wrong When Trying to Route UMI Through an Enterprise App?
As we mapped out the implementation, architectural oversights in the proposed “chained” identity model began to surface. In Microsoft Entra ID (formerly Azure AD), an App Registration and a Managed Identity are both Service Principals under the hood. You cannot natively instruct one Service Principal (the UMI) to simply “assume” or “inherit” the Graph permissions of another Service Principal (the Enterprise App) merely by granting access between them.
Our logging and initial diagnostic tests revealed the following bottlenecks:
- OBO Flow Misconception: We explored the
OnBehalfOfCredentialin Azure.Identity. However, the OBO flow is designed for middle-tier web APIs receiving a delegated user token, which it exchanges for a token to call a downstream API. It does not work for daemon-to-daemon (App-only) chaining without a user context. - Client Secret Dependency: To authenticate as the Enterprise App directly, the traditional route requires passing a Client ID and a Client Secret to a
ClientSecretCredential. This violated the core requirement of a secretless architecture. - Token Audience Mismatch: Attempting to acquire an access token for the Enterprise App using the UMI resulted in tokens where the “audience” (aud) was the Enterprise App, but Microsoft Graph rejected these tokens because Graph requires the audience to be
https://graph.microsoft.com.
What Authentication Approaches Did We Consider for the .NET GraphServiceClient?
To deliver a robust solution, we evaluated the architectural tradeoffs of several approaches. When organizations hire software developer teams with deep Azure expertise, anticipating these Entra ID routing limitations is crucial.
Approach 1: Can We Use the On-Behalf-Of (OBO) Flow?
We initially tested if we could use the UMI to acquire a token, send it to a middle-tier Azure Function represented by the Enterprise App and use OBO. We discarded this because OBO requires a user subject. Since our VMs run unattended batch jobs, there is no user context. The daemon process operates strictly with Application permissions.
Approach 2: Can We Use Key Vault and Certificate-Based Authentication?
To satisfy the requirement of routing through the Enterprise App without passwords/secrets, we considered a certificate workflow. We could generate a certificate, store it in Azure Key Vault, grant the UMI access to read the Key Vault and configure the Enterprise App to trust the certificate. The .NET code would use the UMI to pull the cert, then use ClientCertificateCredential to authenticate as the Enterprise App. While highly secure and “secretless” from a developer perspective, it introduced operational overhead for certificate rotation.
Approach 3: Direct Application Role Assignment to the UMI (The Chosen Path)
We engaged the security stakeholders and demonstrated that a User-Assigned Managed Identity *is* fundamentally an Enterprise Application. By assigning Microsoft Graph Application Permissions directly to the UMI Service Principal, we bypass the unnecessary middleman App Registration. We consolidate governance by using Infrastructure as Code (Terraform/Bicep) to track permissions, rather than using an arbitrary App Registration as a container. This is the Microsoft-recommended best practice for daemon apps on Azure VMs.
How Did We Implement the Final Secretless Authentication Workflow?
We pivoted to the direct UMI approach. The technical complexity here is that the Azure Portal UI does not allow you to easily assign Microsoft Graph Application permissions (App Roles) directly to a Managed Identity. It must be done via PowerShell or Microsoft Graph API.
How Do We Assign Graph Permissions to the UMI via Script?
Before touching the .NET code, we ran a robust provisioning script to bind the Graph roles (e.g., Mail.Read, Files.Read.All) to the UMI.
# Retrieve the UMI Service Principal
$umiSp = Get-MgServicePrincipal -Filter "DisplayName eq 'umi-batch-processor-prod'"
# Retrieve Microsoft Graph Service Principal
$graphSp = Get-MgServicePrincipal -Filter "AppId eq '00000003-0000-0000-c000-000000000000'"
# Find the specific App Role ID (e.g., User.Read.All)
$appRoleId = ($graphSp.AppRoles | Where-Object {$_.Value -eq "User.Read.All"}).Id
# Grant the permission
New-MgServicePrincipalAppRoleAssignment -ServicePrincipalId $umiSp.Id `
-PrincipalId $umiSp.Id `
-ResourceId $graphSp.Id `
-AppRoleId $appRoleId
How Do We Configure the .NET GraphServiceClient?
Once the UMI was granted the correct rights and assigned to the Azure VM, the .NET implementation became remarkably clean. By using the Azure.Identity and Microsoft.Graph libraries, we entirely eliminated credential management.
using Azure.Identity;
using Microsoft.Graph;
// The Client ID of the User-Assigned Managed Identity
string umiClientId = "12345678-abcd-efgh-ijkl-9876543210ab";
string[] scopes = new[] { "https://graph.microsoft.com/.default" };
// ManagedIdentityCredential automatically handles the token acquisition via the VM's IMDS endpoint
var credential = new ManagedIdentityCredential(clientId: umiClientId);
// Instantiate the GraphServiceClient
var graphClient = new GraphServiceClient(credential, scopes);
// Validation: Fetch data to verify authorization
var users = await graphClient.Users
.GetAsync((requestConfiguration) =>
{
requestConfiguration.QueryParameters.Select = new string[] { "id", "displayName", "mail" };
requestConfiguration.QueryParameters.Top = 10;
});
foreach (var user in users.Value)
{
Console.WriteLine($"Found user: {user.DisplayName}");
}
Security Considerations: By scoping the UMI specifically to the VMs that require it and strictly limiting the Graph App Roles via PowerShell, we achieved a zero-trust, zero-secret authentication workflow. There are no expiring secrets to track and lateral movement is contained.
What Are the Key Lessons for Engineering Teams Designing Azure Identity Workflows?
When enterprise tech leaders hire dotnet developers for enterprise modernization, architectural clarity around identity management separates senior engineers from junior ones. Here is what other teams should take away from this challenge:
- Managed Identities Are Service Principals: Treat your UMIs exactly like Enterprise Apps. They can hold Microsoft Graph App Roles directly. Do not try to chain them into other App Registrations unless using workload identity federation.
- Understand the On-Behalf-Of Flow Limits: OBO is strictly for user-delegated token chaining (User -> API A -> API B). It cannot be used for daemon-to-daemon application token exchange.
- Embrace Infrastructure as Code for Governance: If security teams want to track permissions centrally, rely on Terraform or Bicep deployments to document UMI permissions rather than using an App Registration UI as a makeshift folder for scopes.
- Master the Graph API for Administration: The Azure Portal lacks UI capabilities for many advanced Managed Identity configurations. Engineering teams must be proficient in Microsoft Graph PowerShell to assign App Roles to identities.
- The Key Vault Certificate Fallback: If architectural constraints absolutely force the use of a separate App Registration, bypass client secrets by having the UMI pull a certificate from Azure Key Vault to authenticate the App Registration.
How Can We Help Modernize Your Enterprise Application Identity Workflows?
Overcoming legacy authentication models requires more than just replacing connection strings; it requires a deep understanding of cloud-native identity fabrics like Microsoft Entra ID. Complex architectural constraints—like mapping specific identity routing without compromising zero-trust principles—demand seasoned engineering foresight.
Whether you need to untangle legacy monolithic security practices or design scalable backend infrastructure, having the right architectural minds makes the difference. If your organization is looking to build resilient systems and wants to hire remote engineering teams that bring structured delivery and deep cloud expertise, contact us. We provide pre-vetted, highly capable developers who understand how to deliver enterprise-grade software securely.
Social Hashtags
#AzureManagedIdentity #MicrosoftGraph #AzureSecurity #DotNetDevelopment #PasswordlessAuthentication #MicrosoftEntraID #GraphAPI #GraphServiceClient #AzureVM #ManagedIdentity #CloudSecurity #ZeroTrustSecurity #SecretlessAuthentication #AzureIdentity #EnterpriseArchitecture #CSharp #DevSecOps #CloudNative #DotNet #WorkloadIdentity
Frequently Asked Questions
Yes, DefaultAzureCredential loops through multiple credential types, including Environment Variables, Managed Identity and Azure CLI. However, in a production VM environment, explicitly using ManagedIdentityCredential with the specific UMI Client ID prevents unexpected identity resolution conflicts and improves startup performance.
Access tokens are issued for a specific audience (the resource being called). If a UMI requests a token for an App Registration, the token's audience is the App Registration. Microsoft Graph will reject this token because it expects the audience to be https://graph.microsoft.com.
Microsoft Entra ID provides Service Principal sign-in logs. You can route these logs to Azure Log Analytics workspaces to monitor exactly when the UMI authenticates and track the API calls made to Microsoft Graph.
Yes. By assigning the Sites.Selected Application permission to the UMI, you can use the Microsoft Graph API to grant the UMI read or write access exclusively to specific SharePoint site collections, enforcing the principle of least privilege.
Organizations should seek engineers who understand the nuances of OAuth 2.0 flows, Managed Identities and Azure Resource Manager (ARM). Expertise in automating identity provisioning via scripts and transitioning legacy ROPC workflows to modern, token-based authentication is critical.
Success Stories That Inspire
See how our team takes complex business challenges and turns them into powerful, scalable digital solutions. From custom software and web applications to automation, integrations, and cloud-ready systems, each project reflects our commitment to innovation, performance, and long-term value.

Recruitment Operations Automation with Offshore Software Dev Team

Government Liaison Operations Digitization with Dedicated Offshore Team

















