Identityserver4: Question: Why is my HttpContext.User not authenticated?

Created on 20 Mar 2018  路  8Comments  路  Source: IdentityServer/IdentityServer4

I'm in the process of migrating from .NET Core 1.1 to 2.0 and while updating the IdentityServer4 configuration, I got stuck with the following situation.

My Startup config:

public IServiceProvider ConfigureServices(IServiceCollection services)
{ 

  var corsOrigins = configs.SelectMany(c => c.AllowedCorsOrigins).ToList();
  var cors = new DefaultCorsPolicyService(_loggerFactory.CreateLogger<DefaultCorsPolicyService>())
            {
                AllowedOrigins = corsOrigins
            };
  services.AddSingleton<ICorsPolicyService>(cors);

  services.AddIdentityServer()
                .AddSigningCredential(cert)
                .AddInMemoryApiResources(IdentityServerConfig.GetApiResources())
                .AddInMemoryIdentityResources(IdentityServerConfig.GetIdentityResources())
                .AddProfileService<ProfileService>();

  services.AddAuthentication()
    .AddIdentityServerAuthentication(options =>
       {
                    options.ApiName = "api1";
                    options.Authority = Configuration.GetValue<string>("Authority");
                    options.ClaimsIssuer = Configuration.GetValue<string>("Authority");
                    options.RequireHttpsMetadata = Environment.IsProduction();
                    options.SupportedTokens = SupportedTokens.Both;
      });
  services.AddMvc();
// ...

public void Configure(IApplicationBuilder app, IHostingEnvironment env, IClientService clientService) 
{
  app.UseIdentityServer();

  app.UseMvcWithDefaultRoute();
}

The situation:

The login and call to my ProfileService works fine, but when I'm sending a request to the protected API, my ClaimsPrincipal (accessed via HttpContext.User) is not authenticated, and his claims are empty.

Log output:

After login (with implicit flow client)
info: IdentityServer4.Endpoints.AuthorizeEndpoint[0]
      ValidatedAuthorizeRequest
      {
        "ClientId": "app",
        "RedirectUri": "http://localhost:3000/callback",
        "AllowedRedirectUris": [
          "http://localhost:3000/callback",
          "http://localhost:3000/silent_renew.html",
          "http://192.168.0.44:3000/callback",
          "http://192.168.0.44:3000/silent_renew.html"
        ],
        "SubjectId": "1#3",
        "ResponseType": "token",
        "ResponseMode": "fragment",
        "GrantType": "implicit",
        "RequestedScopes": "api1",
        "State": "0bedf473c2fd48d8859655eaf3b6affe",
        "SessionId": "b9376a8bf95b08631ffdc169ef76b745",
        "Raw": {
          "client_id": "app",
          "redirect_uri": "http://localhost:3000/callback",
          "response_type": "token",
          "scope": "api1",
          "state": "0bedf473c2fd48d8859655eaf3b6affe"
        }
      }
dbug: IdentityServer4.Services.DefaultConsentService[0]
      Client is configured to not require consent, no consent is required
dbug: IdentityServer4.ResponseHandling.AuthorizeResponseGenerator[0]
      Creating Implicit Flow response.
dbug: IdentityServer4.Services.DefaultClaimsService[0]
      Getting claims for access token for client: app
dbug: IdentityServer4.Services.DefaultClaimsService[0]
      Getting claims for access token for subject: 1#3
And after an API request:
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1]
      Request starting HTTP/1.1 OPTIONS http://localhost:5000/api/data
dbug: Microsoft.AspNetCore.Cors.Infrastructure.CorsService[1]
      The request is a preflight request.
dbug: Microsoft.AspNetCore.Cors.Infrastructure.CorsService[2]
      The request has an origin header: 'http://localhost:3000'.
info: Microsoft.AspNetCore.Cors.Infrastructure.CorsService[4]
      Policy execution successful.
dbug: Microsoft.AspNetCore.Server.Kestrel[9]
      Connection id "0HLCDV66PUEO9" completed keep alive response.
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2]
      Request finished in 21.7776ms 204
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1]
      Request starting HTTP/1.1 GET http://localhost:5000/api/data application/json
dbug: Microsoft.AspNetCore.Cors.Infrastructure.CorsService[2]
      The request has an origin header: 'http://localhost:3000'.
info: Microsoft.AspNetCore.Cors.Infrastructure.CorsService[4]
      Policy execution successful.
dbug: IdentityServer4.Hosting.CorsPolicyProvider[0]
      CORS request made for path: /api/data from origin: http://localhost:3000 but was ignored because path was not for an allowed IdentityServer CORS endpoint
dbug: Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationHandler[9]
      AuthenticationScheme: idsrv was not authenticated.
dbug: Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationHandler[9]
      AuthenticationScheme: idsrv was not authenticated.

Any help would be greatly appreciated. Thanks!

question

Most helpful comment

Got it!

The problem was I was trying to access HttpContext.User from a custom middleware before the call to the controller. There, the user was null. I had to call await httpContext.AuthenticateAsync("Bearer") to get the user data.

Very informative talk - thanks for your help!

All 8 comments

Does the API controller/action have [Authorize(Scheme="Bearer")]?

The controller does have the [Authorize] attribute, and after changing it to [Authorize("Bearer")] or [Authorize(AuthenticationSchemes = "Bearer")] nothing changed.

I'm wondering about the debug line CORS request made for path: /api/data from origin: http://localhost:3000 but was ignored because path was not for an allowed IdentityServer CORS endpoint. Maybe I'm missing something in the CORS config?

My CORS config in Startup's Configure
            app.UseCors(builder =>
            {
                // Allowed CORS origins only for trusted clients
                var corsOrigins = new List<string>();
                foreach (var client in clientService.GetClients())
                {
                    foreach (var corsOrigin in client.AllowedCorsOrigins)
                    {
                        corsOrigins.Add(corsOrigin);
                    }
                }
                var origins = new string[corsOrigins.Count];
                corsOrigins.CopyTo(origins, 0);

                builder.WithOrigins(origins); // contains all of the trusted origins
                builder.AllowAnyHeader();
                builder.AllowAnyMethod();
            });

           // ...

           app.UseIdentityServer(); ...

When I change services.AddAuthentication() to services.AddAuthentication("Bearer"), the problem gets reversed. Now an already existing token is accepted by the API, but the login fails at the call to await HttpContext.SignInAsync() with an exception::

info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1]
      Executing action method API.Quickstart.Account.AccountController.Login (API) with arguments (API.Quickstart.Account.LoginInputModel, login) - ModelState is Valid
dbug: IdentityServer4.Hosting.IdentityServerAuthenticationService[0]
      Augmenting SignInContext
dbug: IdentityServer4.Hosting.IdentityServerAuthenticationService[0]
      Adding idp claim with value: local
dbug: IdentityServer4.Hosting.IdentityServerAuthenticationService[0]
      Adding amr claim with value: pwd
dbug: IdentityServer4.AccessTokenValidation.IdentityServerAuthenticationHandler[9]
      AuthenticationScheme: Bearer was not authenticated.
info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2]
      Executed action API.Quickstart.Account.AccountController.Login (API) in 3552.4471ms
fail: Microsoft.AspNetCore.Server.Kestrel[13]
      Connection id "0HLCEEULOM4AO", Request id "0HLCEEULOM4AO:00000003": An unhandled exception was thrown by the application.
System.InvalidOperationException: No IAuthenticationSignInHandler is configured to handle sign in for the scheme: Bearer
   at Microsoft.AspNetCore.Authentication.AuthenticationService.<SignInAsync>d__13.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at IdentityServer4.Hosting.IdentityServerAuthenticationService.<SignInAsync>d__7.MoveNext() in C:\local\identity\server4\IdentityServer4\src\IdentityServer4\Hosting\IdentityServerAuthenticationService.cs:line 72
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.AspNetCore.Http.AuthenticationManagerExtensions.<SignInAsync>d__11.MoveNext() in C:\local\identity\server4\IdentityServer4\src\IdentityServer4\Extensions\HttpContextAuthenticationExtensions.cs:line 278
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.AspNetCore.Http.AuthenticationManagerExtensions.<SignInAsync>d__3.MoveNext() in C:\local\identity\server4\IdentityServer4\src\IdentityServer4\Extensions\HttpContextAuthenticationExtensions.cs:line 104

We showed hosting an API in IS4 in our NDC talk from January in London. https://vimeo.com/254635632

Got it!

The problem was I was trying to access HttpContext.User from a custom middleware before the call to the controller. There, the user was null. I had to call await httpContext.AuthenticateAsync("Bearer") to get the user data.

Very informative talk - thanks for your help!

I'm also facing same issue. Below is how I configuration..

// removed

services.AddIdentityServer();
services.AddAuthentication(options =>
{
      options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
      options.DefaultSignInScheme = IdentityServerAuthenticationDefaults.AuthenticationScheme;
      options.DefaultSignOutScheme = IdentityServerAuthenticationDefaults.AuthenticationScheme;
      options.DefaultChallengeScheme = IdentityServerAuthenticationDefaults.AuthenticationScheme;
      options.DefaultForbidScheme = IdentityServerAuthenticationDefaults.AuthenticationScheme;
      options.DefaultScheme = IdentityServerAuthenticationDefaults.AuthenticationScheme;
})
.AddIdentityServerAuthentication(JwtBearerDefaults.AuthenticationScheme,
       options =>
       {
            options.TokenValidationParameters = GetTokenValidationParameters();
        }, null);)

This is closed. I was doing same mistake.

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

eshorgan picture eshorgan  路  3Comments

osmankibar picture osmankibar  路  3Comments

agilenut picture agilenut  路  3Comments

leksim picture leksim  路  3Comments

not-good-with-usernames picture not-good-with-usernames  路  3Comments