10

I've added OpenID Authentication to my ASP.NET Core 2.0 wep app:

services.AddAuthentication(sharedOptions =>
        {
            sharedOptions.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
            sharedOptions.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
        })
            .AddCookie()
            .AddOpenIdConnect(option =>
            {
                option.ClientId = Configuration["AzureAD:ClientId"];
                option.Authority = String.Format(Configuration["AzureAd:AadInstance"], Configuration["AzureAd:Tenant"]);
                option.SignedOutRedirectUri = Configuration["AzureAd:PostLogoutRedirectUri"];
            });

How do I turn on automatic challenge, so controller, resp action with AuthorizeAttribute will return 403 rather than redirect?

EDIT: I ended up with this:

.AddOpenIdConnect(option =>
{
    ...
    option.Events = new OpenIdConnectEvents
    {
        OnRedirectToIdentityProvider = context =>
        {
            bool isAjaxRequest = context.HttpContext.Request.Headers["x-requested-with"] == "XMLHttpRequest";
            if (isAjaxRequest)
            {
                context.HttpContext.Response.StatusCode = StatusCodes.Status401Unauthorized;
                //context.HttpContext.Response.Headers["Location"] = ???request.RedirectUrl;
                context.HandleResponse();
            }
            return Task.CompletedTask;
        }
    };
});

Although I don't want to redirect Ajax request (because why?), I would like to pass the redirect url to the client. How to get the RedirectURL?

Liero
  • 25,216
  • 29
  • 151
  • 297
  • 1
    There is already a solution for this problem on StackOverflow: https://stackoverflow.com/questions/45878166/asp-net-core-2-0-disable-automatic-challenge#45955658 – Raphael Müllner Sep 15 '17 at 04:41
  • 1
    In my case it is slightly different, since I'm using OpenIdConnect and the proposed OnRedirectToLogin is never hit actually. There is OnRedirectToIdentityProvider on the OpenIdOptions however – Liero Sep 18 '17 at 11:13
  • Have you tried the options of the cookie authentication from the solution in the other thread? AFAIK the redirects are always performed by the cookie authentication. – Raphael Müllner Sep 18 '17 at 12:00
  • Yes, the event in cookie authentication was not hit – Liero Sep 18 '17 at 12:47
  • Sorry, have overseen this part of your comment. What if you implement the same logic for the OnRedirectToIdentityProvider event? – Raphael Müllner Sep 18 '17 at 13:10
  • I did and it works, however, I'm not able to catch the redirect uri. That's all and it would be handy if I could – Liero Sep 18 '17 at 13:27
  • Remove DefaultChallengeScheme if you want cookie auth to handle the challenge, it has this ajax logic built in. – Tratcher Sep 18 '17 at 17:27
  • @Tratcher: but if I do remove it, unauthenticated non-ajax request won't be redirected to identity provider, will they? – Liero Feb 01 '18 at 11:50
  • Having the same issue, right now i let the Ajax request handle the 401 response code and do the redirect. But i would like a more direct solution that does not need to wait for an ajax request. – Robban1980 Jul 05 '18 at 04:16

1 Answers1

7

The best solution I've been able to come with so far is:

services.AddAuthentication(sharedOptions =>
{
    sharedOptions.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    sharedOptions.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    sharedOptions.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
  .AddCookie(options =>
  {
      options.Events.OnRedirectToAccessDenied = DontRedirectAjaxOrApiRequestToForbidden;
  })
  .AddOpenIdConnect(options =>
  {
      ...
      options.Events.OnRedirectToIdentityProvider = DontRedirectAjaxRequestToOpenIdProvider;
  });


/// <summary>
/// Unauthenticated ajax or API request returns 403 rather than Redirect to forbidden page
/// </summary>
private static Task DontRedirectAjaxOrApiRequestToForbidden(RedirectContext<CookieAuthenticationOptions> ctx)
{
    bool isAjaxRequest = ctx.HttpContext.Request.Headers["x-requested-with"] == "XMLHttpRequest";
    if (isAjaxRequest || (ctx.Request.Path.StartsWithSegments("/api")))
    {
        ctx.Response.StatusCode = 403;
    }
    else
    {
        ctx.Response.Redirect(ctx.RedirectUri);
    }
    return Task.CompletedTask;
}

/// <summary>
/// Unauthenticated ajax request returns 401 rather than Redirect
/// </summary>
private static Task DontRedirectAjaxRequestToOpenIdProvider(RedirectContext redirectContext)
{
    bool isAjaxRequest = redirectContext.HttpContext.Request.Headers["x-requested-with"] == "XMLHttpRequest";
    if (isAjaxRequest)
    {
        redirectContext.HttpContext.Response.StatusCode = StatusCodes.Status401Unauthorized;
        redirectContext.HttpContext.Response.Headers["Location"] = CookieAuthenticationDefaults.LoginPath.Value;
        redirectContext.HandleResponse();
    }
    return Task.CompletedTask;
}
Liero
  • 25,216
  • 29
  • 151
  • 297