Identity Server 3 - 401 on Ajax Calls instead of 302
Asked Answered
A

2

9

I have a web api / mvc hybrid app and I have configured it to use cookie authentication. This works fine for the mvc portion of the application. The web api does enforce the authorization, but instead of returning a 401 - Unauthorised it returns a 302 - Found and redirects to the login page. I would rather it returns a 401. I have attempted to hook into the CookieAuthenticationProvider.OnApplyRedirect delegate, but this doesn't seem to be called. What have I missed? My current setup is below:

AntiForgeryConfig.UniqueClaimTypeIdentifier = Constants.ClaimTypes.Subject;
JwtSecurityTokenHandler.InboundClaimTypeMap = new Dictionary<string, string>();

app.UseCookieAuthentication(new CookieAuthenticationOptions
{
    AuthenticationType = "Cookies",
    ExpireTimeSpan = TimeSpan.FromMinutes(20),
    SlidingExpiration = true,
    CookieHttpOnly = true,
    CookieSecure = CookieSecureOption.Never, //local non ssl-dev only
    Provider = new CookieAuthenticationProvider
    {
        OnApplyRedirect = ctx =>
        {
            if (!IsAjaxRequest(ctx.Request))
            {
                ctx.Response.Redirect(ctx.RedirectUri);
            }
        }
    }
});

app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
    Authority = IdentityConfig.Authority,
    ClientId = IdentityConfig.SoftwareClientId,
    Scope = "openid profile roles",
    RedirectUri = IdentityConfig.RedirectUri,
    ResponseType = "id_token",
    SignInAsAuthenticationType = "Cookies"
});
Anesthetic answered 25/1, 2016 at 16:39 Comment(0)
G
14

In your example the UseCookieAuthentication no longer controls this, instead the UseOpenIdConnectAuthentication does. This involves using the Notifications property and intercepting OpenID Connect authentication requests.

Try out the following for inspiration:

app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
    Authority = IdentityConfig.Authority,
    ClientId = IdentityConfig.SoftwareClientId,
    Scope = "openid profile roles",
    RedirectUri = IdentityConfig.RedirectUri,
    ResponseType = "id_token",
    SignInAsAuthenticationType = "Cookies",
    Notifications = new OpenIdConnectAuthenticationNotifications
    {
        RedirectToIdentityProvider = notification =>
        {
            if (notification.ProtocolMessage.RequestType == OpenIdConnectRequestType.AuthenticationRequest)
            {
                if (IsAjaxRequest(notification.Request) && notification.Response.StatusCode == (int)HttpStatusCode.Unauthorized)
                {
                    notification.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
                    notification.HandleResponse();
                    return Task.FromResult(0);
                }
            }
            return Task.FromResult(0);
        }
    }
});
Giannini answered 25/1, 2016 at 19:6 Comment(2)
Is there a typo here? You test to see if status == Unauthorized, and if so set it to Unauthorized? Isn't that a no-op?Repugnant
You are catching responses that are flagged as unauthorized before they are redirected off to an identity provider for authentication. The setting of the status code is probably redundant though.Giannini
B
1

In my case the IsAjaxRequest did not do the trick. Instead I rely on all routes to the WebAPI being under "/api", so instead of the IsAjaxRequest I do:

RedirectToIdentityProvider =  context => {
    if (context.ProtocolMessage.RequestType == OpenIdConnectRequestType.Authentication){
        if (context.Request.Path.StartsWithSegments(new PathString("/api")) && context.Response.StatusCode == (int)HttpStatusCode.Unauthorized){
            context.HandleResponse();
            return Task.CompletedTask;
        }
    }
    return Task.CompletedTask;
}
Bikol answered 15/6, 2020 at 12:5 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.