Unable to call graphAPI using On-behalf-of flow from Asp.Net Web API

864 Views Asked by At

I'm trying to implement On-behalf-of user in Asp.net Web API (.net 5). I receive an access_token from the Mobile APP, send it to my Web API. The Web API uses this token to call the GRAPH API to get the user's profile details. Below is my code Startup.cs file

 services.AddAuthentication("JwtBearer")
                        .AddJwtBearer("JwtBearer", options =>
                        {
                            options.MetadataAddress = $"https://login.microsoftonline.com/{Configuration["b2bAzureAppIdentity:TenantId"]}/v2.0/.well-known/openid-configuration";
                            options.TokenValidationParameters = new TokenValidationParameters()
                            {
                                ValidIssuer = $"https://sts.windows.net/{Configuration["b2bAzureAppIdentity:TenantId"]}/",

                                // as audience, both the client id and the identifierUri are allowed (sematically equivalent)
                                ValidAudiences = new[] { Configuration["b2bAzureAppIdentity:AppIdUri"], Configuration["b2bAzureAppIdentity:ClientId"] }
                            };
                        }).AddMicrosoftIdentityWebApi(Configuration, "b2bAzureAppIdentity")
                        .EnableTokenAcquisitionToCallDownstreamApi()
                        .AddMicrosoftGraph(Configuration.GetSection("DownstreamApi"))
                        .AddInMemoryTokenCaches();

controller.cs

 [HttpGet("GetMyDetails")]
    [AuthorizeForScopes(Scopes = new string[] { "user.read" })]
    public async Task<IActionResult> GetMyDetails()
    {
        var user = await _graphServiceClient.Me.Request().GetAsync();
        return new OkObjectResult(user.Photo);
    }

Appsettings is in the below format

 "b2bAzureAppIdentity": {
"Instance": "https://login.microsoftonline.com/",
"Domain": "",
"TenantId": "",
"ClientId": "",
"ClientSecret": "",
"AppIdUri": ""},
 "DownstreamApi": {
"BaseUrl": "https://graph.microsoft.com/v1.0",
"Scopes": "user.read"},

In Azure, the API permissions and scope are set correctly, this is evident because when I make calls from postman, I'm able to get the accesstoken for on_behalf_of and use it to get the user's profile details by calling https://graph.microsoft.com/v1.0/me

In the controller at this line

var user = await _graphServiceClient.Me.Request().GetAsync();

I get an error: "No account or login hint was passed to the AcquireTokenSilent call. "

I have googled for this error and the solution says that the user should consent the scope however it has already been consented by the admin in Azure portal. Also, the fact that this is working in Postman makes be believe that the configurations for the APP and the API are correct. enter image description here

Has anyone faced similar issue?

1

There are 1 best solutions below

0
On BEST ANSWER

This is happening because the access_token received is not sent along with the request to get the user detail. Here's an example of how to implement on behalf of provider:

// Create a client application.
IConfidentialClientApplication confidentialClientApplication = ConfidentialClientApplicationBuilder
                .Create(clientId)
                .WithTenantId(tenantID)
                // The Authority is a required parameter when your application is configured 
                // to accept authentications only from the tenant where it is registered.
                .WithAuthority(authority)
                .WithClientSecret(clientSecret)
                .Build();
                
// Use the API reference to determine which scopes are appropriate for your API request.
// e.g. - https://learn.microsoft.com/en-us/graph/api/user-get?view=graph-rest-1.0&tabs=http
var scopes = new string[] { "User.Read" };
// Create an authentication provider.
ClientCredentialProvider authenticationProvider = new OnBehalfOfProvider(confidentialClientApplication, scopes);

var jsonWebToken = actionContext.Request.Headers.Authorization.Parameter;
var userAssertion = new UserAssertion(jsonWebToken);
// Configure GraphServiceClient with provider.
GraphServiceClient graphServiceClient = new GraphServiceClient(authenticationProvider);
// Make a request
var me = await graphServiceClient.Me.Request().WithUserAssertion(userAssertion).GetAsync();

In this case the token is added to the request in the call to WithUserAssertion.

Please let me know if this helps, and if you have further questions.