Microsoft Identity is redirecting WebAPI calls to microsoftonline.com

Guisinger, Dan 25 Reputation points
2024-01-02T21:21:26.74+00:00

We are trying to build an application with both razor pages and API endpoints and an Angular app to access the API endpoints.

Web Application #1

I've been able to connect to Entra ID from the razor page side of the primary AspNet Core web project, and it redirects to Microsoft to login, then redirects back to the site and works fine.

Web Application #2
I've also got an Angular site built from the Asp.Net Core SPA template using azure/msal-* npm packages to login to Entra ID. I then try to get the site to make a REST API call back to web application #1 on an API Controller endpoint.

Instead of authenticating with the site using the bearer token passed in from the Angular site, the API on web app #1 is returning a redirect response to Microsoftonline.com for authentication.

Both apps are using the same Entra ID application, just different client IDs.

The two issues I'm trying to solve are:

  1. If the API isn't authenticating, it shouldn't be redirecting to a website, it should return a 401 Not Authorized
  2. Why is the bearer token not being accepted by the Web API?

The AspNetCore website is configuring authentication as follows:

builder.Services.AddRazorPages().AddMvcOptions(options => { }).AddMicrosoftIdentityUI();  builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)         .AddMicrosoftIdentityWebApi(builder.Configuration)         .EnableTokenAcquisitionToCallDownstreamApi()         .AddDownstreamWebApi("DownstreamApi", builder.Configuration.GetSection("DownstreamApi"))         .AddInMemoryTokenCaches();

builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)         .AddMicrosoftIdentityWebApp(builder.Configuration)         .EnableTokenAcquisitionToCallDownstreamApi(initialScopes)         .AddDownstreamWebApi("DownstreamApi", builder.Configuration.GetSection("DownstreamApi"))         .AddInMemoryTokenCaches();

builder.Services.AddAuthorization(options => { options.DefaultPolicy = new AuthorizationPolicyBuilder(JwtBearerDefaults.AuthenticationScheme,         OpenIdConnectDefaults.AuthenticationScheme).RequireAuthenticatedUser().Build(); });

And the API Controller is setup with:

[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]

On the Angular application, it is adding the API to MsalInterceptorConfiguration's protected resource map, and the bearer token appears to be getting attached to the requests to the Web API call.

Any ideas what I am doing wrong? Thanks

ASP.NET Core
ASP.NET Core
A set of technologies in the .NET Framework for building web applications and XML web services.
4,615 questions
ASP.NET API
ASP.NET API
ASP.NET: A set of technologies in the .NET Framework for building web applications and XML web services.API: A software intermediary that allows two applications to interact with each other.
343 questions
0 comments No comments
{count} votes

Accepted answer
  1. Tiny Wang-MSFT 2,731 Reputation points Microsoft Vendor
    2024-01-03T04:31:18.0733333+00:00

    Hello @Guisinger, Dan , I had a test with code below which worked well in my side.

    var builder = WebApplication.CreateBuilder(args);
    
    // Add services to the container.
    builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
        .AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd"))
        .EnableTokenAcquisitionToCallDownstreamApi()
        .AddDownstreamWebApi("DownstreamApi", builder.Configuration.GetSection("DownstreamApi"))
        .AddInMemoryTokenCaches();
    
    builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
        .AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAd"))
        .EnableTokenAcquisitionToCallDownstreamApi()
        .AddDownstreamWebApi("DownstreamApi", builder.Configuration.GetSection("DownstreamApi"))
        .AddInMemoryTokenCaches();
    
    builder.Services.AddAuthorization(options =>
    {
        options.FallbackPolicy = options.DefaultPolicy;
    });
    builder.Services.AddRazorPages()
        .AddMicrosoftIdentityUI();
    

    And for the API and the razor page, we need to use different auth scheme so that they will have different react for the react(401 for the API, auto redirect to sign in page for razor page)

    namespace WebAppRazorAad.Controllers
    {
        [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
        [Route("api/[controller]")]
        [ApiController]
        public class HelloController : ControllerBase
        {
            public string Get() {
                return "success";
            }
        }
    }
    
    
    using Microsoft.AspNetCore.Authentication.OpenIdConnect;
    using Microsoft.AspNetCore.Authorization;
    using Microsoft.AspNetCore.Mvc;
    using Microsoft.AspNetCore.Mvc.RazorPages;
    
    namespace WebAppRazorAad.Pages
    {
        [Authorize(AuthenticationSchemes = OpenIdConnectDefaults.AuthenticationScheme)]
        public class IndexModel : PageModel
        {
            private readonly ILogger<IndexModel> _logger;
    
            public IndexModel(ILogger<IndexModel> logger)
            {
                _logger = logger;
            }
    
            public void OnGet()
            {
    
            }
        }
    }
    

    User's image

    User's image

    I'm sure your issue is resulted from the builder.Services.AddAuthorization clause, if you comment it, requests to the pages which is not adding [Authorize] won't redirect to sign in page, but requests to the protected API would return 401.

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

    If the answer is the right solution, please click "Accept Answer" and kindly upvote it. If you have extra questions about this answer, please click "Comment".

    Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.

    Best regards,

    Tiny


0 additional answers

Sort by: Most helpful

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.