Search code examples
c#oauth-2.0microsoft-graph-apiasp.net-core-webapiazure-ad-msal

DelegateAuthenticationProvider not found after updating Microsoft Graph


I have the following code (copied from Microsoft Learn), that was working fine with Microsoft.Graph 4.54.0

var authProvider = new DelegateAuthenticationProvider(async (request) => {
                // Use Microsoft.Identity.Client to retrieve token
                var assertion = new UserAssertion(token.AccessToken);
                var result = await clientApplication.AcquireTokenOnBehalfOf(scopes, assertion).ExecuteAsync();

                request.Headers.Authorization =
                    new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", result.AccessToken);
            });

I then created a new project using Microsoft.Graph 5.0.0 and Microsoft.Graph.Core 3.0.0, this gives an error that DelegateAuthenticationProvider could not be found.

How do I create an AuthenticationProvider that I can use with the GraphServiceClient?


Solution

  • I haven't figured your issue out because I never used your code before, I just followed this official document to create GraphServiceClient via on-behalf-of flow. I will try to continue. Here's what I already got and it worked.

    using Azure.Core;
    using Azure.Identity;
    using Microsoft.AspNetCore.Http;
    using Microsoft.AspNetCore.Mvc;
    using Microsoft.Extensions.Primitives;
    using Microsoft.Graph;
    
    namespace WebMvcGraph5Ofo.Controllers
    {
        [Route("api/[controller]")]
        [ApiController]
        public class HelloController : ControllerBase
        {
            public async Task<string> Get() {
                StringValues authorizationToken;
                HttpContext.Request.Headers.TryGetValue("Authorization", out authorizationToken);
                var token = authorizationToken.ToString().Replace("Bearer ","");
                var scopes = new[] { "User.Read.All" };
                var tenantId = "tenantId";
                var clientId = "client_id";
                var clientSecret = "client_secret";
                var onBehalfOfCredential = new OnBehalfOfCredential(tenantId, clientId, clientSecret, token);
                var tokenRequestContext = new TokenRequestContext(scopes);
                var token2 = onBehalfOfCredential.GetTokenAsync(tokenRequestContext, new CancellationToken()).Result.Token;
                var graphClient = new GraphServiceClient(onBehalfOfCredential, scopes);
                var user = await graphClient.Users.GetAsync();
                return "hello";
            }
    
            [Route("ClientCredentialFlow")]
            public async Task<string> clientAsync() {
                var scopes = new[] { "https://graph.microsoft.com/.default" };
                var tenantId = "tenantId";
                var clientId = "client_id";
                var clientSecret = "client_secret";
                var clientSecretCredential = new ClientSecretCredential(
                                tenantId, clientId, clientSecret);
                var graphClient = new GraphServiceClient(clientSecretCredential, scopes);
                var users = await graphClient.Users.GetAsync();
                return "world";
            }
            
            [Route("provider")]
            public async Task<string> providerAsync()
            {
                StringValues authorizationToken;
                HttpContext.Request.Headers.TryGetValue("Authorization", out authorizationToken);
                string incomingToken = authorizationToken.ToString().Replace("Bearer ", "");
                TokenProvider provider = new TokenProvider();
                provider.token = incomingToken;
                var authenticationProvider = new BaseBearerTokenAuthenticationProvider(provider);
                var graphServiceClient = new GraphServiceClient(authenticationProvider);
                var user = await graphServiceClient.Users.GetAsync();
                return "!!";
            }
        }
        
        public class TokenProvider : IAccessTokenProvider
        {
            public string token { get; set; }
            public AllowedHostsValidator AllowedHostsValidator => throw new NotImplementedException();
    
            public Task<string> GetAuthorizationTokenAsync(Uri uri, Dictionary<string, object>? additionalAuthenticationContext = null, CancellationToken cancellationToken = default)
            {
                return Task.FromResult(token);
            }
        }
    }
    

    enter image description here

    =========================================================

    Just like what @user2250152 shared,

    In place of the DelegateAuthenticationProvider, custom authentication flows can be done creating an implementation of IAccessTokenProvider, and using with the BaseBearerTokenAuthenticationProvider from the Kiota abstractions as follows

    we can't use DelegateAuthenticationProvider any more to use it directly to new GraphServiceClient(delegateAuthenticationProvider), if we want to generate the auth_provider to new GraphServiceClient, we have to follow to use BaseBearerTokenAuthenticationProvider + IAccessTokenProvider. I had a test like below, it can work but seems not meet the requirement for on-behalf-of flow.

    Just like you know, the whole flow for O-B-O should be, using an access token A to call the web API which is protected by AAD, then the API code use O-B-O flow to generate a new access token B to do other request, such as calling graph API.

    Then the scope or role for token A should look like api://xxxx/scope_name(.default for role), and the scope for token B should be graph API scope such as User.Read.All.

    In my test, I used graph client so that I don't need to generate token B, but it still need to authenticate the graph client. I found that when I pass token A with api://xxxx/scope_name to auth the graph client, I will get error, but when the token A is generated by graph API scope, I will successfully auth the graph client.

    enter image description here