Search code examples
asp.net-coreoauth-2.0asp.net-identityidentityserver4razor-pages

OAuth authentication with local provider


In the ASP.NET Core 2.2 application, I'm defining OAuth provider. The provider is able to sign-in user and obtain an authorize_code, exchange authorize_code for a token and get user details and cookie of Identity server is stored in the user agent. But after redirect to Authorize page (/user in my case) I am redirected back to Identity provider, but the cookie is already assigned to the application, so application gets an authorize_code, exchange it for a token and get user details and redirected to Authorize page and the whole weel start again and again and in the end user agent cancel redirects, so I'm stuck in recursion and I don't know why.

Here is used configuration

services.AddAuthentication(options =>
    {
        options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
        options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
        options.DefaultChallengeScheme = "app";
        options.DefaultScheme = "app";
    })
    .AddCookie()
    .AddOAuth("app", options =>
    {
        options.ClientId = "appclient";
        options.ClientSecret = "3f97501d279b44f3bd69e8eec64cf336";

        options.CallbackPath = new PathString("/app-signin");

        options.Scope.Add("app_identity");
        options.Scope.Add("profile");
        options.Scope.Add("email");

        options.AuthorizationEndpoint = "http://dev/services/identity/connect/authorize";
        options.TokenEndpoint = "http://dev/services/identity/connect/token";
        options.UserInformationEndpoint = "http://dev/services/identity/api/user-profile";

        options.ClaimActions.MapJsonKey(ClaimTypes.NameIdentifier, "id");
        options.ClaimActions.MapJsonKey(ClaimTypes.GivenName, "firstName");
        options.ClaimActions.MapJsonKey(ClaimTypes.Surname, "lastName");
        options.ClaimActions.MapJsonKey(ClaimTypes.Email, "email");

        options.Events = new OAuthEvents
        {
            OnCreatingTicket = async context =>
            {
                var request = new HttpRequestMessage(HttpMethod.Get, context.Options.UserInformationEndpoint);
                request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
                request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", context.AccessToken);

                var response = await context.Backchannel.SendAsync(request, 
                                   HttpCompletionOption.ResponseHeadersRead, 
                                   context.HttpContext.RequestAborted);
                response.EnsureSuccessStatusCode();

                var user = JObject.Parse(await response.Content.ReadAsStringAsync());
                context.RunClaimActions(user);
            }
        };
    });

services.Configure<CookiePolicyOptions>(options =>
{
    options.CheckConsentNeeded = context => true;
    options.MinimumSameSitePolicy = SameSiteMode.None;
});

services
    .AddMvc()
    .AddRazorPagesOptions(options => options.Conventions.AuthorizePage("/User"))
    .SetCompatibilityVersion(CompatibilityVersion.Version_2_2);

Application logs in output following

    info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1]
    Request starting HTTP/1.1 GET http://localhost:5001/User
info: Microsoft.AspNetCore.Routing.EndpointMiddleware[0]
    Executing endpoint 'Page: /User'
info: Microsoft.AspNetCore.Mvc.RazorPages.Internal.PageActionInvoker[3]
    Route matched with {page = "/User"}. Executing page /User
info: Microsoft.AspNetCore.Authorization.DefaultAuthorizationService[2]
    Authorization failed.
info: Microsoft.AspNetCore.Mvc.RazorPages.Internal.PageActionInvoker[3]
    Authorization failed for the request at filter 'Microsoft.AspNetCore.Mvc.Authorization.AuthorizeFilter'.
info: Microsoft.AspNetCore.Mvc.ChallengeResult[1]
    Executing ChallengeResult with authentication schemes ().
info: Microsoft.AspNetCore.Authentication.OAuth.OAuthHandler`1[[Microsoft.AspNetCore.Authentication.OAuth.OAuthOptions, Microsoft.AspNetCore.Authentication.OAuth, Version=2.2.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60]][12]
    AuthenticationScheme: app was challenged.
info: Microsoft.AspNetCore.Mvc.RazorPages.Internal.PageActionInvoker[4]
    Executed page /User in 64.6437ms
info: Microsoft.AspNetCore.Routing.EndpointMiddleware[1]
    Executed endpoint 'Page: /User'
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2]
    Request finished in 129.4168ms 302
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1]
    Request starting HTTP/1.1 GET http://localhost:5001/app-signin?code=311966dfed306bdf5228501edcf7b493e208a0cf720d3ef39f281aabfa04bfb8&scope=wph_identity%20profile%20email&state=CfDJ8Ncai4ZNyK9Bno-T0jQD3hVJUzq-6K_bCKipeVryVM_FhwOvgc_d5ueapYYg7XRTSuFeLbrnZB4K8Zpni8KAk6cg1wWGwDoGXJrb3jp9zwhGQS24bPZUrsVFVxdt9v0loPkJjX6p226E3TisaJkaAxa_bzxyO_JKWtoHKsBLOmbuLh92rvxzde6S9BrElgvOuAYqsJ87UidQbCm8RrqZh78aC1vo5XcdIYEZs6eQjGFt
info: Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationHandler[10]
    AuthenticationScheme: Cookies signed in.
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2]
    Request finished in 321.3221ms 302
...

The output just repeats for 4 more times. I believe its just some misconfiguration, but I can't figure out what is configured badly.


Solution

  • Caused by SameSite cookie restriction. Adding .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, opt => opt.Cookie.SameSite = SameSiteMode.None) helped.