Search code examples
azureasp.net-coreazure-active-directoryazure-authentication

Call ASP .NET Core API in Azure from daemon application


I have limited experience on Azure AD and authentication mechanism. So far I cannot figure out why is not working. Here is the scenario:

I have an ASP net core 2.1 application deployed in azure web app service. For authentication I’m using Open ID connect with .AddOpenIdConnect and provide client_id, secret_id, etc. When users are accessing my web API they are redirected to Microsoft login.

Now I need to expose an API to a third party application (scheduled web job) which is not in Azure.

I tried to use this sample from Microsoft, only the console app, as I already have the WebApp in Azure. Running the sample I’m able to get the token, but when I call my API the response is the HTML to Microsoft login page.

On Azure portal on

Enterprise Application -> daemon-console -> Activity -> Service Principal sign-ins

I can see the success sign in.

Note: for testing I run the web app on my local machine and from the console application I’m calling API https://localhost:44306/api/test.

Asp .net core app:

services.AddAuthentication(option =>
{
    option.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    option.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    option.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie(option =>
{
    option.Cookie.Name = "myWebApp"; 
    option.Cookie.SecurePolicy = CookieSecurePolicy.Always;
    option.Cookie.SameSite = SameSiteMode.None;
})
.AddOpenIdConnect(option =>
{
    option.ClientId = client_id;
    option.ClientSecret = client_secret;
    option.Authority = authority;
    option.SignedOutRedirectUri = "http://localhost:44306/";
    option.CorrelationCookie.Name = "myWebAppCorrelation"; 
    option.CorrelationCookie.SecurePolicy = CookieSecurePolicy.Always;
    option.NonceCookie.Name = "WebAppNonce";
    option.NonceCookie.SecurePolicy = CookieSecurePolicy.Always;
    option.Resource = "https://graph.windows.net";
    option.ResponseType = "id_token code";
})

Console app trying to access the API ( code extracted from Microsoft sample )

app = ConfidentialClientApplicationBuilder.Create(config.ClientId)
    .WithClientSecret(config.ClientSecret)
    .WithAuthority(new Uri(config.Authority))
    .Build();
    
result = await app.AcquireTokenForClient(scopes).ExecuteAsync(); // ok

var httpClient = new HttpClient();
var defaultRequestHeaders = httpClient.DefaultRequestHeaders;
if (defaultRequestHeaders.Accept == null || !defaultRequestHeaders.Accept.Any(m => m.MediaType == "application/json"))
{
    httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
}
defaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", result.AccessToken);

HttpResponseMessage response = await httpClient.GetAsync(webApiUrl);
if (response.IsSuccessStatusCode) // ok 
{
    string json = await response.Content.ReadAsStringAsync(); // here I'm getting the HTML to login page
    var result = JsonConvert.DeserializeObject<List<JObject>>(json);
    Console.ForegroundColor = ConsoleColor.Gray;
    processResult(result);
}

The only difference between the sample code and my scenario is that the web app from sample is using services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddMicrosoftIdentityWebApi(Configuration) but I cannot use .AddMicrosoftIdentityWebApi in Asp .Net core 2.1

Does anyone has an idea where the issue might be ? Do I need to add another authentication scheme ?


Solution

  • You need to support JWT authentication in addition to cookie authentication. So you need to add AddJwtBearer. You also need to extend the authentication check because you are now supporting multiple schemes.

    This is how I would do it:

    // public void ConfigureServices(IServiceCollection services)
    services.AddAuthentication(option =>
    {
        option.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
        option.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
        option.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
    })
    .AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, cfg => {
        cfg.Authority = authority;
        cfg.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
        {
            ValidAudience = /*see scopes of your deamon app*/
        };
    })
    .AddCookie(option =>
    {
        // ...
    })
    .AddOpenIdConnect(option =>
    {
        // ...
    })
    
    
    // public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    
    app.UseAuthentication();
    // https://github.com/aspnet/Security/issues/1847
    app.Use(async (context, next) =>
    {
        if (!context.User.Identity.IsAuthenticated)
        {
            var result = await context.AuthenticateAsync(JwtBearerDefaults.AuthenticationScheme);
            if (result.Succeeded)
            {
                context.User = result.Principal;
            }
        }
        await next();
    });
    app.UseAuthorization();