最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

asp.net mvc - Migrating AD Graph and MSAL Authorization Code - Issue with Delegation in Migrated Code - Stack Overflow

programmeradmin4浏览0评论

I'm working on migrating a application that uses Azure AD Graph and MSAL Authorization to the newer Microsoft Graph API and MSAL.NET. Below is the code snippet that retrieves direct reports for a user:

public async Task<List<Microsoft.Azure.ActiveDirectory.GraphClient.User>> GetDirectReports(string objectId) 
{ 
    List<Microsoft.Azure.ActiveDirectory.GraphClient.User> reports = new List<Microsoft.Azure.ActiveDirectory.GraphClient.User>(); 
    try 
    { 
        var client = AuthenticationHelper.GetClient(); 
        IUser user = await client.Users.GetByObjectId(objectId).ExecuteAsync(); 
        var userFetcher = user as IUserFetcher; 
        IPagedCollection<IDirectoryObject> directReports = await userFetcher.DirectReports.ExecuteAsync(); 
        do 
        { 
            List<IDirectoryObject> directoryObjects = directReports.CurrentPage.ToList(); 
            foreach (IDirectoryObject directoryObject in directoryObjects) 
            { 
                if (directoryObject is Microsoft.Azure.ActiveDirectory.GraphClient.User) 
                { 
                    reports.Add((Microsoft.Azure.ActiveDirectory.GraphClient.User)directoryObject); 
                } 
            } 
            directReports = await directReports.GetNextPageAsync(); 
        } while (directReports != null); 
    } 
    catch (Exception e) 
    { 
        if (Request.QueryString["reauth"] == "True") 
        { 
            HttpContext.GetOwinContext()
                .Authentication.Challenge(OpenIdConnectAuthenticationDefaults.AuthenticationType); 
        } 
        ViewBag.ErrorMessage = "Authorization error occurred."; 
    } 
}

I attempted to migrate this code to use Microsoft Graph API and MSAL.NET, but I'm facing issues with delegation and authorization. Specifically, the migrated code does not seem to handle delegated permissions properly, and I'm getting errors when trying to fetch direct reports.

Here is the migrated code snippet:

public async Task<List<User>> GetDirectReports(string userId)
{
    List<User> reports = new List<User>();
    try
    {
        var graphClient = new GraphServiceClient(new DelegateAuthenticationProvider(async (requestMessage) =>
        {
            var token = await GetAccessTokenAsync();
            requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
        }));

        var directReports = await graphClient.Users[userId].DirectReports.Request().GetAsync();
        do
        {
            foreach (var report in directReports.CurrentPage)
            {
                if (report is User user)
                {
                    reports.Add(user);
                }
            }
            directReports = directReports.NextPageRequest != null ? await directReports.NextPageRequest.GetAsync() : null;
        } while (directReports != null);
    }
    catch (ServiceException ex)
    {
        if (ex.StatusCode == System.Net.HttpStatusCode.Unauthorized)
        {
            // Handle reauthorization logic
        }
        else
        {
            throw;
        }
    }
    return reports;
}

Share point authentication create hand shake and below are the autherization block in startauth.cs

 app.UseOpenIdConnectAuthentication(
                new OpenIdConnectAuthenticationOptions
                {
                    ClientId = clientId,
                    Authority = Authority,
                    RedirectUri = postLogoutRedirectUri,
                    PostLogoutRedirectUri = postLogoutRedirectUri,
                    Scope = $"openid profile {scopes}", // Include Microsoft Graph scopes
                    TokenValidationParameters = new TokenValidationParameters
                    {
                        RoleClaimType = "roles",
                    },
                    Notifications = new OpenIdConnectAuthenticationNotifications
                    {
                        AuthorizationCodeReceived = async (context) =>
                        {
                            var code = context.Code;
                            var userObjectId = context.AuthenticationTicket.Identity.FindFirst(
                                ";).Value;

                            IConfidentialClientApplication Iconfidentialapp;

                            if (!string.IsNullOrEmpty(certName))
                            {
                                // Load the certificate
                                X509Certificate2 cert = LoadCertificate(certName);
                                if (cert == null)
                                {
                                    throw new Exception("Certificate not found.");
                                }

                                // Create the confidential client application with the certificate
                                Iconfidentialapp = ConfidentialClientApplicationBuilder.Create(clientId)
                                    .WithAuthority(Authority)
                                    .WithCertificate(cert)
                                    .Build();
                            }
                            else
                            {
                                // Create the confidential client application with the client secret
                                Iconfidentialapp = ConfidentialClientApplicationBuilder.Create(clientId)
                                    .WithAuthority(Authority)
                                    .WithClientSecret(clientSecret)
                                    .Build();
                            }

                            // Acquire a token using the authorization code
                            var result = await Iconfidentialapp.AcquireTokenByAuthorizationCode(scopes.Split(' '), code)
                                .ExecuteAsync();

                            // Store the token in a helper class or session
                            AuthenticationHelper.token = result.AccessToken;
                        }
                    }
                });

Issues Encountered:

Severity Code Description Project File Line Suppression State Error CS1061 'DirectReportsRequestBuilder' does not contain a definition for 'Request' and no accessible extension method 'Request' accepting a first argument of type 'DirectReportsRequestBuilder' could be found (are you missing a using directive or an assembly reference?)

Severity Code Description Project File Line Suppression State Error CS0246 The type or namespace name 'DelegateAuthenticationProvider' could not be found (are you missing a using directive or an assembly reference?)

Am I doing right or any other method or any other way to achieve the requirement.

Referred MS articles:

I'm working on migrating a application that uses Azure AD Graph and MSAL Authorization to the newer Microsoft Graph API and MSAL.NET. Below is the code snippet that retrieves direct reports for a user:

public async Task<List<Microsoft.Azure.ActiveDirectory.GraphClient.User>> GetDirectReports(string objectId) 
{ 
    List<Microsoft.Azure.ActiveDirectory.GraphClient.User> reports = new List<Microsoft.Azure.ActiveDirectory.GraphClient.User>(); 
    try 
    { 
        var client = AuthenticationHelper.GetClient(); 
        IUser user = await client.Users.GetByObjectId(objectId).ExecuteAsync(); 
        var userFetcher = user as IUserFetcher; 
        IPagedCollection<IDirectoryObject> directReports = await userFetcher.DirectReports.ExecuteAsync(); 
        do 
        { 
            List<IDirectoryObject> directoryObjects = directReports.CurrentPage.ToList(); 
            foreach (IDirectoryObject directoryObject in directoryObjects) 
            { 
                if (directoryObject is Microsoft.Azure.ActiveDirectory.GraphClient.User) 
                { 
                    reports.Add((Microsoft.Azure.ActiveDirectory.GraphClient.User)directoryObject); 
                } 
            } 
            directReports = await directReports.GetNextPageAsync(); 
        } while (directReports != null); 
    } 
    catch (Exception e) 
    { 
        if (Request.QueryString["reauth"] == "True") 
        { 
            HttpContext.GetOwinContext()
                .Authentication.Challenge(OpenIdConnectAuthenticationDefaults.AuthenticationType); 
        } 
        ViewBag.ErrorMessage = "Authorization error occurred."; 
    } 
}

I attempted to migrate this code to use Microsoft Graph API and MSAL.NET, but I'm facing issues with delegation and authorization. Specifically, the migrated code does not seem to handle delegated permissions properly, and I'm getting errors when trying to fetch direct reports.

Here is the migrated code snippet:

public async Task<List<User>> GetDirectReports(string userId)
{
    List<User> reports = new List<User>();
    try
    {
        var graphClient = new GraphServiceClient(new DelegateAuthenticationProvider(async (requestMessage) =>
        {
            var token = await GetAccessTokenAsync();
            requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
        }));

        var directReports = await graphClient.Users[userId].DirectReports.Request().GetAsync();
        do
        {
            foreach (var report in directReports.CurrentPage)
            {
                if (report is User user)
                {
                    reports.Add(user);
                }
            }
            directReports = directReports.NextPageRequest != null ? await directReports.NextPageRequest.GetAsync() : null;
        } while (directReports != null);
    }
    catch (ServiceException ex)
    {
        if (ex.StatusCode == System.Net.HttpStatusCode.Unauthorized)
        {
            // Handle reauthorization logic
        }
        else
        {
            throw;
        }
    }
    return reports;
}

Share point authentication create hand shake and below are the autherization block in startauth.cs

 app.UseOpenIdConnectAuthentication(
                new OpenIdConnectAuthenticationOptions
                {
                    ClientId = clientId,
                    Authority = Authority,
                    RedirectUri = postLogoutRedirectUri,
                    PostLogoutRedirectUri = postLogoutRedirectUri,
                    Scope = $"openid profile {scopes}", // Include Microsoft Graph scopes
                    TokenValidationParameters = new TokenValidationParameters
                    {
                        RoleClaimType = "roles",
                    },
                    Notifications = new OpenIdConnectAuthenticationNotifications
                    {
                        AuthorizationCodeReceived = async (context) =>
                        {
                            var code = context.Code;
                            var userObjectId = context.AuthenticationTicket.Identity.FindFirst(
                                "http://schemas.microsoft/identity/claims/objectidentifier").Value;

                            IConfidentialClientApplication Iconfidentialapp;

                            if (!string.IsNullOrEmpty(certName))
                            {
                                // Load the certificate
                                X509Certificate2 cert = LoadCertificate(certName);
                                if (cert == null)
                                {
                                    throw new Exception("Certificate not found.");
                                }

                                // Create the confidential client application with the certificate
                                Iconfidentialapp = ConfidentialClientApplicationBuilder.Create(clientId)
                                    .WithAuthority(Authority)
                                    .WithCertificate(cert)
                                    .Build();
                            }
                            else
                            {
                                // Create the confidential client application with the client secret
                                Iconfidentialapp = ConfidentialClientApplicationBuilder.Create(clientId)
                                    .WithAuthority(Authority)
                                    .WithClientSecret(clientSecret)
                                    .Build();
                            }

                            // Acquire a token using the authorization code
                            var result = await Iconfidentialapp.AcquireTokenByAuthorizationCode(scopes.Split(' '), code)
                                .ExecuteAsync();

                            // Store the token in a helper class or session
                            AuthenticationHelper.token = result.AccessToken;
                        }
                    }
                });

Issues Encountered:

Severity Code Description Project File Line Suppression State Error CS1061 'DirectReportsRequestBuilder' does not contain a definition for 'Request' and no accessible extension method 'Request' accepting a first argument of type 'DirectReportsRequestBuilder' could be found (are you missing a using directive or an assembly reference?)

Severity Code Description Project File Line Suppression State Error CS0246 The type or namespace name 'DelegateAuthenticationProvider' could not be found (are you missing a using directive or an assembly reference?)

Am I doing right or any other method or any other way to achieve the requirement.

Referred MS articles: https://learn.microsoft/en-us/graph/migrate-azure-ad-graph-client-libraries

Share Improve this question edited Mar 17 at 10:15 Nachiappan R asked Mar 17 at 8:31 Nachiappan RNachiappan R 1841 gold badge1 silver badge20 bronze badges 9
  • How are you generating the access token? – Rukmini Commented Mar 17 at 8:37
  • Share point handshake happened and I have modified my question by adding authorization block. – Nachiappan R Commented Mar 17 at 10:17
  • What is the error now? – Rukmini Commented Mar 17 at 10:18
  • Please refer issue encountered section in my question. – Nachiappan R Commented Mar 17 at 10:25
  • Try passing graphClient.Users[userId].DirectReports.GetAsync() without Request() – Rukmini Commented Mar 17 at 10:26
 |  Show 4 more comments

1 Answer 1

Reset to default 0

To make use Microsoft Graph API to call the APIs, you can make use of MSAL (Microsoft Authentication Library) to acquire a token. Refer this Microsoft Document and choose a Microsoft Authentication provider : Choose a Microsoft Graph authentication provider - Microsoft Graph | Microsoft Learn

Grant User.Read.All API permission:

And make use of below code:

using Azure.Identity;
using Microsoft.Graph;
using Newtonsoft.Json;
using System;
using System.Threading.Tasks;

public class GraphAPIExample
{
    private static string[] scopes = new[] { "User.Read.All" };  
    private static string tenantId = "TenantID";  
    private static string clientId = "ClientID"; 
    private static Uri redirectUri = new Uri("http://localhost");  
    private static InteractiveBrowserCredentialOptions options = new InteractiveBrowserCredentialOptions
    {
        TenantId = tenantId,
        ClientId = clientId,
        AuthorityHost = AzureAuthorityHosts.AzurePublicCloud,
        RedirectUri = redirectUri
    };

    public static async Task Main(string[] args)
    {
        try
        {
            var interactiveCredential = new InteractiveBrowserCredential(options);
            var graphClient = new GraphServiceClient(interactiveCredential, scopes);

            string userId = "UserID";  

            await GetDirectReportsForUserAsync(graphClient, userId);
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Error: {ex.Message}");
        }
    }

    public static async Task GetDirectReportsForUserAsync(GraphServiceClient graphClient, string userId)
    {
        try
        {
            var directReportsResponse = await graphClient.Users[userId].DirectReports.GetAsync();
            string jsonResponse = JsonConvert.SerializeObject(directReportsResponse, Formatting.Indented);
            Console.WriteLine("Full Direct Reports Response:");
            Console.WriteLine(jsonResponse);
        }
        catch (ServiceException ex)
        {
            Console.WriteLine($"Error calling Graph API: {ex.Message}");
        }
    }
}

If you want to use Authorization code flow make use of below code:

var scopes = new[] { "User.Read" };
var tenantId = "TenantID";
var clientId = "ClientID";
var clientSecret = "Secret";
var authorizationCode = "AUTH_CODE_FROM_REDIRECT";

var options = new AuthorizationCodeCredentialOptions
{
    AuthorityHost = AzureAuthorityHosts.AzurePublicCloud,
};

var authCodeCredential = new AuthorizationCodeCredential(
    tenantId, clientId, clientSecret, authorizationCode, options);
var graphClient = new GraphServiceClient(authCodeCredential, scopes);

To get authorizationCode run the below in the browser and sign in and copy the code value:

https://login.microsoftonline/TenantID/oauth2/v2.0/authorize?
&client_id=ClientID
&response_type=code
&redirect_uri=https://jwt.ms
&response_mode=query
&scope=User.Read.All
&state=12345
发布评论

评论列表(0)

  1. 暂无评论