Mr Perfect
Mr Perfect

Reputation: 685

How to call downstream API in .Net Core using on behalf of flow?

Net core application and react. I have react app which calls my middle tier api and middle tier api calls down stream api. I have registered three apps in azure ad. I have made all the configurations. In middle tier API I am validating the token recieved from react app and trying to get token for downstream api as below.

public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllers();
            services.AddTransient<DownStreamAPIService>();
            services.AddHttpClient();
            services.AddOptions();
            var azureAd = Configuration.GetSection("AzureAd").Get<AzureAd>();
            IdentityModelEventSource.ShowPII = true;

            services.AddMicrosoftIdentityWebApiAuthentication(Configuration)
                .EnableTokenAcquisitionToCallDownstreamApi()
                .AddInMemoryTokenCaches();

            services.AddAuthentication(x =>
            {
                x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
            })
            .AddJwtBearer(options =>
            {
                options.SaveToken = true;
                options.RequireHttpsMetadata = true;
                options.Authority = $"{azureAd.Instance}/{azureAd.TenantId}/v2.0";
                options.Audience = $"{azureAd.ClientId}";

                options.TokenValidationParameters = new TokenValidationParameters
                {
                    ValidateAudience = true,
                    ValidateLifetime = true,
                    ValidateIssuer = true,
                    ValidateIssuerSigningKey = false,
                    ValidateActor = false
                };
            });

            services.AddCors(options =>
            {
                options.AddPolicy(
                    "CorsPolicy",
                    builder =>
                    {
                        builder
                        .WithOrigins("https://localhost:3000")
                            .AllowAnyHeader()
                            .AllowAnyMethod()
                            .AllowCredentials();
                    });
            });
        } 

Below is DownStreamAPIService class

public async Task<JArray> GetApiDataAsync()
        {
            var client = _clientFactory.CreateClient();

            // user_impersonation access_as_user access_as_application .default
            var scope = _configuration["DownStreamAPI:ScopeForAccessToken"];
            var accessToken = await _tokenAcquisition.GetAccessTokenForUserAsync(new[] { scope }).ConfigureAwait(false);

            client.BaseAddress = new Uri(_configuration["DownStreamAPI:ApiBaseAddress"]);
            client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
            client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

            var response = await client.GetAsync("weatherforecast").ConfigureAwait(false);
            if (response.IsSuccessStatusCode)
            {
                var responseContent = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
                var data = JArray.Parse(responseContent);

                return data;
            }

            throw new ApplicationException($"Status code: {response.StatusCode}, Error: {response.ReasonPhrase}");
        }

Below is appsettings.json file

 "AzureAd": {
    "Instance": "https://login.microsoftonline.com/",
    "Domain": "mydomain.onmicrosoft.com",
    "TenantId": "c5fff990-0e0a-4bf6-9c04-79ab98e05931",
    "ClientId": "43dg8b3c-8743-4d6e-84b4-00fe04d93222"
  },
  "WebOriginUrl": "https://localhost:3000",
  "DownStreamAPI": {
    "ScopeForAccessToken": "api://723fac69-4038-4e16-92cc-3f57b7cc2381/access_downstream_api_as_user",
    "ApiBaseAddress": "https://localhost:44316"
  }

When I run the app I get below error

Scheme already exists: Bearer

In my middle tier app I am validating the token and trying to obtain new token for downstream API. i am just confused weather validating token required in middle tier api or not. I am using on behalf of flow. Can soneone help if there is anything wrong in this design. Any help would be appreciated. Thanks

Upvotes: 1

Views: 4767

Answers (1)

Jim Xu
Jim Xu

Reputation: 23111

When we use the AddMicrosoftIdentityWebApiAuthentication to configure Azure AD, the project will use Bearer scheme to do auth. We do not need to configure it again. For more details, please refer to here and here. So please remove the code

services.AddAuthentication(x =>
            {
                x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
            })
            .AddJwtBearer(options =>
            {
                options.SaveToken = true;
                options.RequireHttpsMetadata = true;
                options.Authority = $"{azureAd.Instance}/{azureAd.TenantId}/v2.0";
                options.Audience = $"{azureAd.ClientId}";

                options.TokenValidationParameters = new TokenValidationParameters
                {
                    ValidateAudience = true,
                    ValidateLifetime = true,
                    ValidateIssuer = true,
                    ValidateIssuerSigningKey = false,
                    ValidateActor = false
                };
            });

if you want to validate the token, you can use the following code

 services.Configure<JwtBearerOptions>(JwtBearerDefaults.AuthenticationScheme, options =>
            {
                options.SaveToken = true;
                options.RequireHttpsMetadata = true;

                options.TokenValidationParameters = new TokenValidationParameters
                {
                    ValidateAudience = true,
                    ValidAudiences = new string[] { "872ebcec-c24a-4399-835a-201cdaf7d68b", "api://872ebcec-c24a-4399-835a-201cdaf7d68b" },
                    ValidateLifetime = true,
                    ValidateIssuer = true,
                    ValidateIssuerSigningKey = false,
                    ValidateActor = false,
                    ValidIssuers = new string[] { "https://sts.windows.net/{tenantId}", "https://login.microsoftonline.com/{tenantId}/v2.0" }
                };
            });

Upvotes: 3

Related Questions