Home > Software design >  .Net Core Web API undocumented 204 Response
.Net Core Web API undocumented 204 Response

Time:03-02

I am trying to create a .net core 6 Web Api that calls Microsoft Graph on behalf of what amounts to be an eventual Vue.Js client end user (with office 365 account and Azure Active Directory Registered). I haven't setup the client but I am trying to test the web api to make sure everythings working and my api calls all response with 204 (No Content). Postman also returns the same on those api calls. I suspect I haven't set the Graph Service Client up correctly but I can't find a way to fix it.

I setup authentication in my ConfigureServices like so

            // Enable JWT Bearer Authentication
            services.AddAuthentication(sharedOptions =>
            {
                sharedOptions.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
            }).AddJwtBearer(options =>
            {
                Configuration.Bind("AzureAd", options);
                // Authority will be Your AzureAd Instance and Tenant Id
                options.Authority = $"{Configuration["AzureAd:Instance"]}{Configuration["AzureAd:TenantId"]}/v2.0";

                // The valid audiences are both the Client ID(options.Audience) and api://{ClientID}
                options.TokenValidationParameters.ValidAudiences = new string[] { Configuration["AzureAd:ClientId"], $"api://{Configuration["AzureAd:ClientId"]}" };
            });

            var options = new TokenCredentialOptions
            {
                AuthorityHost = AzureAuthorityHosts.AzurePublicCloud
            };

            var authCodeCredential = new AuthorizationCodeCredential(Configuration["AzureAd:TenantId"], Configuration["AzureAd:ClientId"], Configuration["AzureAd:ClientSecret"],
            $"{Configuration["AzureAd:Instance"]}{Configuration["AzureAd:TenantId"]}/oauth2/v2.0/authorize", options);
            services.AddSingleton<GraphServiceClient>(_ => new GraphServiceClient(authCodeCredential, Configuration.GetValue<string>("DownstreamApi:Scopes")?.Split(', ')));
            
            AddSwagger(services);

With AddSwagger Being

            services.AddOpenApiDocument(document =>
            {
                document.AddSecurity("bearer", Enumerable.Empty<string>(), new NSwag.OpenApiSecurityScheme
                {
                    Type = OpenApiSecuritySchemeType.OAuth2,
                    Description = "Azure AAD Authentication",
                    Flow = OpenApiOAuth2Flow.Implicit,
                    Flows = new NSwag.OpenApiOAuthFlows()
                    {
                        Implicit = new OpenApiOAuthFlow()
                        {
                            Scopes = new Dictionary<string, string>
                            {
                                { $"api://{Configuration["AzureAd:ClientId"]}/user_impersonation", "Access Application" },
                            },
                            AuthorizationUrl = $"{Configuration["AzureAd:Instance"]}{Configuration["AzureAd:TenantId"]}/oauth2/v2.0/authorize",
                            TokenUrl = $"{Configuration["AzureAd:Instance"]}{Configuration["AzureAd:TenantId"]}/oauth2/v2.0/token",
                        },  
                    },
                });

                document.OperationProcessors.Add(new AspNetCoreOperationSecurityScopeProcessor("bearer"));
            });

And then Create and User my GraphClient in the controller like So

        private readonly GraphServiceClient _graphServiceClient;
        public GraphController(GraphServiceClient graphServiceClient)
        {
            this._graphServiceClient = graphServiceClient;
        }

        [Authorize]
        [HttpGet("GetUserDetails")]
        public async Task<User> GetUserDetails()
        {
            try
            {
                User user = await _graphServiceClient.Me.Request().GetAsync();
                return user;
            }
            catch (Exception ex) 
            {
                return null;
            }
        }

CodePudding user response:

I followed this document.

If you want to use graph client to call graph api, then you need to set the auth provider first. This is mentioned in the document I mentioned, you need to add AddMicrosoftGraph.This will make an authenticated GraphServiceClient available to controllers via dependency injection.

services.AddMicrosoftIdentityWebAppAuthentication(Configuration)
                .EnableTokenAcquisitionToCallDownstreamApi(new string[] { "user.read" })
                .AddMicrosoftGraph(options =>
                {
                    options.Scopes = string.Join(' ', new string[] { "user.read" });
                })
                .AddInMemoryTokenCaches();

Or this code instead.

services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
    // Specify this is a web app and needs auth code flow
    //.AddMicrosoftIdentityWebApp(Configuration)
    // Add ability to call web API (Graph)
    // and get access tokens
    .EnableTokenAcquisitionToCallDownstreamApi(new string[] { "user.read" })
    //need to install Microsoft.Identity.Web.MicrosoftGraph
    //Add a GraphServiceClient via dependency injection
    .AddMicrosoftGraph(options =>
    {
        options.Scopes = string.Join(' ', new string[] { "user.read" });
    })
  // Use in-memory token cache
  // See https://github.com/AzureAD/microsoft-identity-web/wiki/token-cache-serialization
  .AddInMemoryTokenCaches();

My packages installed:

<ItemGroup>
    <PackageReference Include="Microsoft.Graph" Version="4.19.0" />
    <PackageReference Include="Microsoft.Identity.Web" Version="1.23.0" />
    <PackageReference Include="Microsoft.Identity.Web.MicrosoftGraph" Version="1.23.0" />
    <PackageReference Include="Microsoft.Identity.Web.UI" Version="1.23.0" />
  </ItemGroup>

CodePudding user response:

The issue ended up being the

 authCodeCredential = new AuthorizationCodeCredential(Configuration["AzureAd:TenantId"], Configuration["AzureAd:ClientId"], Configuration["AzureAd:ClientSecret"],
            $"{Configuration["AzureAd:Instance"]}{Configuration["AzureAd:TenantId"]}/oauth2/v2.0/authorize", options);
            services.AddSingleton<GraphServiceClient>(_ => new GraphServiceClient(authCodeCredential, Configuration.GetValue<string>("DownstreamApi:Scopes")?.Split(', ')));

Where the authorization code being passed in was incorrect. Unsure of how to fix this at the moment but replacing this line of code with a UsernamePasswordCredential instead gave the correct outputs.

  • Related