IdentityServer / IdentityServer4

OpenID Connect and OAuth 2.0 Framework for ASP.NET Core
https://identityserver.io
Apache License 2.0
9.23k stars 4.02k forks source link

Claims are not being passed to resource after update to 2.0.0rc1-update1 - using Asp.net Identity #1547

Closed nukec closed 7 years ago

nukec commented 7 years ago

I updated to 2.0.0rc1-update1 and claims stopped being passed into resource API. I am using asp.net Identity.

Authentication is ok, I log in. Then when I try to save something into database, I use sub claim to save the row for logged in user.

This is the method and the object ClaimsPrincipal has no claims: prog

Another thing I thought token is not passed to API, as you can see here, it is trying to access log in controller for authorization(API port is 5002): krop

But token is sent as seen in Fiddler request, but it still thinks it's not authorized: redir

My set up in Identity server is as following:

        public void ConfigureServices(IServiceCollection services)
        {

            services.AddDbContext<ApplicationDbContext>(options =>
                options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

            services.AddAuthentication();

            services.AddIdentity<ApplicationUser, IdentityRole>()
                .AddEntityFrameworkStores<ApplicationDbContext>()
                .AddDefaultTokenProviders()
                .AddIdentityServer();

            services.AddCors(options =>
            {
                options.AddPolicy("CorsPolicy",
                    builder => builder.WithOrigins(Configuration.GetSection("HostSettings:WebAppClientUrl").Value.ToString())
                    .AllowAnyMethod()
                    .AllowAnyHeader()
                    .AllowCredentials());
            });

            services.AddMvc();

            services.AddIdentityServer()
                .AddInMemoryClients(Config.GetClients(Configuration.GetSection("HostSettings:WebAppClientUrl").Value.ToString()))
                .AddInMemoryIdentityResources(Config.GetIdentityResources())
                .AddInMemoryApiResources(Config.GetApiResources())
                .AddAspNetIdentity<ApplicationUser>()
                .AddProfileService<ProfileService>()
                .AddSigningCredential(Certificate.GetCertificate(_environment, Configuration.GetSection("CertificateSettings:Thumbprint").Value.ToString()));

            ConfigureDependencyInjection(services);

            // Register the Swagger generator, defining one or more Swagger documents
            services.AddSwaggerGen(x =>
            {
                x.SwaggerDoc("v1", new Info { Title = "App API", Version = "v1" });

                //Set the comments path for the swagger json and ui.
                var basePath = PlatformServices.Default.Application.ApplicationBasePath;
                var xmlPath = Path.Combine(basePath, "IdentityServer.xml");
                x.IncludeXmlComments(xmlPath);
            });
        }

        public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory)
        {
            //todo this should cofigured via env:...
            loggerFactory.AddConsole(LogLevel.Debug);
            app.UseDeveloperExceptionPage();

            app.UseCors("CorsPolicy");

            app.UseIdentityServer();
            app.UseAuthentication();

            app.UseStaticFiles();

            app.UseMvc(routes =>
            {
                routes.MapRoute(
                    name: "default",
                    template: "{controller=Home}/{action=Index}/{id?}");
            });

            // Enable middleware to serve generated Swagger as a JSON endpoint.
            app.UseSwagger();

            // Enable middleware to serve swagger-ui (HTML, JS, CSS etc.), specifying the Swagger JSON endpoint.
            app.UseSwaggerUI(x =>
            {
                x.RoutePrefix = "docs";
                x.SwaggerEndpoint("/swagger/v1/swagger.json", "API V1");
            });
        }

And API resource set up:

        // This method gets called by the runtime. Use this method to add services to the container.
        public IServiceProvider ConfigureServices(IServiceCollection services)
        {

            services.AddDbContext<ApplicationDbContext>(options =>
                options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

            services.AddIdentity<ApplicationUser, ApplicationRole>()
                .AddEntityFrameworkStores<ApplicationDbContext>()
                .AddDefaultTokenProviders();

            services.AddCors(options =>
            {
                options.AddPolicy("CorsPolicy",
                    builder => builder.WithOrigins(Configuration.GetSection("HostSettings:WebAppClientUrl").Value.ToString())
                        .AllowAnyMethod()
                        .AllowAnyHeader()
                        .AllowCredentials());
            });

            services.AddAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme)
                .AddIdentityServerAuthentication(x =>
                {
                    x.Authority = Configuration.GetSection("IdentityServerSettings:Host").Value;
                    x.AllowedScopes = new List<string> { "Api" };
                    x.ApiSecret = "ApiSecret";
                    x.ApiName = "Api";
                    x.SupportedTokens = SupportedTokens.Both;
                    x.RequireHttpsMetadata = false;

                });

            services.AddAuthorization();

            ConfigureDependencyInjection(services);

            // Register the Swagger generator, defining one or more Swagger documents
            services.AddSwaggerGen(x =>
            {
                x.SwaggerDoc("v1", new Info { Title = "App API", Version = "v1" });

                // UseFullTypeNameInSchemaIds replacement for .NET Core
                x.CustomSchemaIds(y => y.FullName);

                ////Set the comments path for the swagger json and ui.
                //var basePath = PlatformServices.Default.Application.ApplicationBasePath;
                //var xmlPath = Path.Combine(basePath, "Api.xml");
                //x.IncludeXmlComments(xmlPath);
            });

            // Add framework services.
            services
                .AddMvc()
                .AddFluentValidation(x => x.RegisterValidatorsFromAssemblyContaining<CreateViewModelValidator>());

            services.AddMvcCore()
                .AddJsonFormatters(x => x.Converters.Add(new StringEnumConverter()));

            // Add di framework
            var container = new Container(new DependencyInjectionRegistry());
            container.Populate(services);

            services.AddAutoMapper(cfg => cfg.ConstructServicesUsing(container.GetInstance));
            Mapper.AssertConfigurationIsValid();

            return container.GetInstance<IServiceProvider>();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
        {
            app.UseCors("CorsPolicy");

            // Enable middleware to serve generated Swagger as a JSON endpoint.
            app.UseSwagger();

            // Enable middleware to serve swagger-ui (HTML, JS, CSS etc.), specifying the Swagger JSON endpoint.
            app.UseSwaggerUI(x =>
            {
                x.RoutePrefix = "docs";
                x.SwaggerEndpoint("/swagger/v1/swagger.json", "API V1");
            });

            app.UseStaticFiles();

            app.UseAuthentication();

            app.UseMvc(routes =>
            {
                routes.MapRoute(
                    "default",
                    "{controller=Default}/{action=Index}/{id?}");
            });
        }

Logs: IdentityServer> [11:43:12 DBG] Token validation success IdentityServer> { IdentityServer> "ValidateLifetime": true, IdentityServer> "AccessTokenType": "Reference", IdentityServer> "ExpectedScope": "openid", IdentityServer> "TokenHandle": "19a8ffd69760c8979299ce6f0e4f7d8cbec4a40b3c7b278f12ff3adf1208bc73", IdentityServer> "Claims": { IdentityServer> "iss": "http://localhost:5001", IdentityServer> "nbf": 1505990590, IdentityServer> "exp": 1505994190, IdentityServer> "aud": [ IdentityServer> "http://localhost:5001/resources", IdentityServer> "Api" IdentityServer> ], IdentityServer> "client_id": "angular2client", IdentityServer> "scope": [ IdentityServer> "openid", IdentityServer> "profile", IdentityServer> "Api" IdentityServer> ], IdentityServer> "sub": "9b92e298-a027-4c1b-96ce-c060db7dc4b0", IdentityServer> "auth_time": 1505988525, IdentityServer> "idp": "local", IdentityServer> "amr": "pwd", IdentityServer> "preferred_username": "a@test.com", IdentityServer> "email": "a@test.com", IdentityServer> "email_verified": false IdentityServer> } IdentityServer> } IdentityServer> [11:43:12 DBG] Creating userinfo response IdentityServer> [11:43:12 DBG] Scopes in access token: openid profile Api IdentityServer> [11:43:12 DBG] Requested claim types: sub name family_name given_name middle_name nickname preferred_username profile picture website gender birthdate zoneinfo locale updated_at IdentityServer> [11:43:12 INF] Executed DbCommand (22ms) [Parameters=[@user_Id_0='?' (Size = 450)], CommandType='Text', CommandTimeout='30'] IdentityServer> SELECT [uc].[Id], [uc].[ClaimType], [uc].[ClaimValue], [uc].[UserId] IdentityServer> FROM [AspNetUserClaims] AS [uc] IdentityServer> WHERE [uc].[UserId] = @user_Id_0 IdentityServer> [11:43:12 INF] Executed DbCommand (17ms) [Parameters=[@__userId_0='?' (Size = 450)], CommandType='Text', CommandTimeout='30'] IdentityServer> SELECT [role].[Name] IdentityServer> FROM [AspNetUserRoles] AS [userRole] IdentityServer> INNER JOIN [AspNetRoles] AS [role] ON [userRole].[RoleId] = [role].[Id] IdentityServer> WHERE [userRole].[UserId] = @__userId_0 IdentityServer> [11:43:12 INF] Profile service returned to the following claim types: sub preferred_username email email_verified IdentityServer> [11:43:12 DBG] End userinfo request IdentityServer> [11:43:12 INF] Request finished in 72.2538ms 200 application/json

leastprivilege commented 7 years ago

It def doesn't help throwing a wall of code at us.

Check the logs. Look at the raw JWT tokens. Put breakpoints in relevant places - e.g. your profile service.

nukec commented 7 years ago

My profile service is ok. GetProfileDataAsync is assigning 4 claims on line context.IssuedClaims = claims; and one of them is sub.

raw JWT tokens consists of all claims. I didn't find anything suspicious in logs, ill have a look again.

Update: I've tried to get previous version for identity server for net 1.1 and used net 2.0 api version of idsrv4, didn't work, same error. Did vice versa, net2.0 on idsrv and net 1.1 on api, didn't work, reversed all on 1.1 works.

Logs don't show anything suspicious..

leastprivilege commented 7 years ago

If the JWT contains all the claims you want - it must be a problem at the API. Do more research in that area.

nukec commented 7 years ago

@leastprivilege problem is Api> info: Microsoft.AspNetCore.Authorization.DefaultAuthorizationService[2] Api> Authorization failed for user: (null).

The issue is similar like here: https://stackoverflow.com/questions/45807822/asp-net-core-2-0-jwt-validation-fails-with-authorization-failed-for-user-null

With new configuration there are no more "AutomaticAuthenticate" and "AutomaticChallenge" so for a reason cookie or token is not reaching?

Full log:

Api> Now listening on: http://localhost:6452 Api> Application started. Press Ctrl+C to shut down. Api> info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] Api> Request starting HTTP/1.1 GET http://localhost:5002/
Api> info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] Api> Request starting HTTP/1.1 GET http://localhost:5002/
Api> info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] Api> Executing action method Api.Controllers.DefaultController.Index (Api) with arguments ((null)) - ModelState is Valid Api> info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] Api> Executing action method Api.Controllers.DefaultController.Index (Api) with arguments ((null)) - ModelState is Valid Api> info: Microsoft.AspNetCore.Mvc.ViewFeatures.Internal.ViewResultExecutor[1] Api> Executing ViewResult, running view at path /Views/Default/Index.cshtml. Api> info: Microsoft.AspNetCore.Mvc.ViewFeatures.Internal.ViewResultExecutor[1] Api> Executing ViewResult, running view at path /Views/Default/Index.cshtml. Api> info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] Api> Executed action Api.Controllers.DefaultController.Index (Api) in 2002.8964ms Api> info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] Api> Executed action Api.Controllers.DefaultController.Index (Api) in 2002.8964ms Api> info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] Api> Request finished in 2297.5692ms 200 text/html; charset=utf-8 Api> info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] Api> Request finished in 2297.5692ms 200 text/html; charset=utf-8 Api> info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] Api> Request starting HTTP/1.1 GET http://localhost:5002/favicon.ico
Api> info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] Api> Request starting HTTP/1.1 GET http://localhost:5002/favicon.ico
Api> info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] Api> Request finished in 16.0804ms 404 Api> info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] Api> Request finished in 16.0804ms 404 Api> info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] Api> Request starting HTTP/1.1 OPTIONS http://localhost:5002/api/program/getall
Api> info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] Api> Request starting HTTP/1.1 OPTIONS http://localhost:5002/api/program/getall
Api> info: Microsoft.AspNetCore.Cors.Infrastructure.CorsService[4] Api> Policy execution successful. Api> info: Microsoft.AspNetCore.Cors.Infrastructure.CorsService[4] Api> Policy execution successful. Api> info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] Api> Request finished in 21.4956ms 204 Api> info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] Api> Request finished in 21.4956ms 204 Api> info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] Api> Request starting HTTP/1.1 GET http://localhost:5002/api/program/getall application/json Api> info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] Api> Request starting HTTP/1.1 GET http://localhost:5002/api/program/getall application/json Api> info: Microsoft.AspNetCore.Cors.Infrastructure.CorsService[4] Api> Policy execution successful. Api> info: Microsoft.AspNetCore.Cors.Infrastructure.CorsService[4] Api> Policy execution successful. Api> info: Microsoft.AspNetCore.Authorization.DefaultAuthorizationService[2] Api> Authorization failed for user: (null). Api> info: Microsoft.AspNetCore.Authorization.DefaultAuthorizationService[2] Api> Authorization failed for user: (null). Api> info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[3] Api> Authorization failed for the request at filter 'Microsoft.AspNetCore.Mvc.Authorization.AuthorizeFilter'. Api> info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[3] Api> Authorization failed for the request at filter 'Microsoft.AspNetCore.Mvc.Authorization.AuthorizeFilter'. Api> info: Microsoft.AspNetCore.Mvc.ChallengeResult[1] Api> Executing ChallengeResult with authentication schemes (). Api> info: Microsoft.AspNetCore.Mvc.ChallengeResult[1] Api> Executing ChallengeResult with authentication schemes (). Api> info: Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationHandler[12] Api> AuthenticationScheme: Identity.Application was challenged. Api> info: Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationHandler[12] Api> AuthenticationScheme: Identity.Application was challenged. Api> info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] Api> Executed action Api.Controllers.ProgramController.GetAll (Api) in 44.5326ms Api> info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] Api> Executed action Api.Controllers.ProgramController.GetAll (Api) in 44.5326ms Api> info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] Api> Request finished in 66.5712ms 302 Api> info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] Api> Request finished in 66.5712ms 302 Api> info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] Api> Request starting HTTP/1.1 OPTIONS http://localhost:5002/Account/Login?ReturnUrl=%2Fapi%2Fprogram%2Fgetall
Api> info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] Api> Request starting HTTP/1.1 OPTIONS http://localhost:5002/Account/Login?ReturnUrl=%2Fapi%2Fprogram%2Fgetall
Api> info: Microsoft.AspNetCore.Cors.Infrastructure.CorsService[4] Api> Policy execution successful. Api> info: Microsoft.AspNetCore.Cors.Infrastructure.CorsService[4] Api> Policy execution successful. Api> info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] Api> Request finished in 14.7905ms 204 Api> info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] Api> Request finished in 14.7905ms 204 Api> info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] Api> Request starting HTTP/1.1 GET http://localhost:5002/Account/Login?ReturnUrl=%2Fapi%2Fprogram%2Fgetall application/json Api> info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] Api> Request starting HTTP/1.1 GET http://localhost:5002/Account/Login?ReturnUrl=%2Fapi%2Fprogram%2Fgetall application/json Api> info: Microsoft.AspNetCore.Cors.Infrastructure.CorsService[4] Api> Policy execution successful. Api> info: Microsoft.AspNetCore.Cors.Infrastructure.CorsService[4] Api> Policy execution successful. Api> info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] Api> Request finished in 15.666ms 404 Api> info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] Api> Request finished in 15.666ms 404

brockallen commented 7 years ago

services.AddAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme)

This should be "Bearer", not IdentityServerAuthenticationDefaults.AuthenticationScheme for APIs.

nukec commented 7 years ago

@brockallen i'm not sure what are you trying to explain here, can you give an example?

public const string AuthenticationScheme = "Bearer";

namespace IdentityServer4.AccessTokenValidation
{
    public class IdentityServerAuthenticationDefaults
    {
        public const string AuthenticationScheme = "Bearer";
        public const string IntrospectionAuthenticationScheme = "IdentityServerAuthenticationIntrospection";
        public const string JwtAuthenticationScheme = "IdentityServerAuthenticationJwt";
        public const string TokenItemsKey = "idsrv4:tokenvalidation:token";

        public IdentityServerAuthenticationDefaults();
    }
}

I am using Asp.Net Identity, and I found he has same issues here https://github.com/IdentityServer/IdentityServer4/issues/1525

Problem is when I add additional schemas aka overwwide in AddAuthentication, it still doesn't work.

brockallen commented 7 years ago

In this section:

And API resource set up:

I saw this code:

 services.AddAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme)
                .AddIdentityServerAuthentication(x =>

So if this is an API project,. then you should be using the bearer token, not the IdentityServer cookie (especially if the API is not hosting IdentityServer). Perhaps I'm missing something about your desired setup -- it;'s just something that looked fishy to me.

nukec commented 7 years ago

@brockallen yes it is API project. Can you please give an example how it should've been set up, because I thought this is correct set up for Bearer token?

brockallen commented 7 years ago

We have a whole repo: https://github.com/IdentityServer/IdentityServer4.Samples

nukec commented 7 years ago

@brockallen those examples are for older versions, come on.. I checked that already, I wouldn't ask if I found something that works there.

brockallen commented 7 years ago

No -- on the dev branch is the latest.

Anyway, looks like your constant is using the value "Bearer", since it was from those exact samples.

yaneyba commented 7 years ago

@nukec, @brockallen I am experiencing the same issue. Have you come to solution, yet? My Setup is as follow:

        services.AddAuthentication("Bearer")
            .AddIdentityServerAuthentication(o =>
            {
                o.Authority = _configuration["IdentityServerConfig:IdentityServerBaseUri"];
                o.AllowedScopes = new List<string> { "fincapi", "apiaccess" };
                o.ApiSecret = "secret";
                o.ApiName = "myservice";
                o.SupportedTokens = SupportedTokens.Jwt;
                o.RequireHttpsMetadata = false;
            });

I get a 500 error with the message below when i make a GET request to an "authorize" endpoint of my aspnet core2 api with a valid Bearer token:

    <h1>An unhandled exception occurred while processing the request.</h1>
    <div class="titleerror">WinHttpException: A security error occurred</div>
    <p class="location">System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()</p>
    <div class="titleerror">HttpRequestException: An error occurred while sending the request.</div>
    <p class="location">System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()</p>
    <div class="titleerror">IOException: IDX10804: Unable to retrieve document from: &#x27;https://devwebsvc/company.identity/core/.well-known/openid-configuration&#x27;.</div>
    <p class="location">Microsoft.IdentityModel.Protocols.HttpDocumentRetriever&#x2B;&lt;GetDocumentAsync&gt;d__8.MoveNext()</p>
    <div class="titleerror">InvalidOperationException: IDX10803: Unable to obtain configuration from: &#x27;https://devwebsvc/company.identity/core/.well-known/openid-configuration&#x27;.</div>
    <p class="location">Microsoft.IdentityModel.Protocols.ConfigurationManager&#x2B;&lt;GetConfigurationAsync&gt;d__24.MoveNext()</p>

I know the IdentityServer instance is up and running well and I can access and see the "openid-configuration" meta-data either using postman or any browser without problem.

nukec commented 7 years ago

@yaneyba this is not the same issue. I had this error on some other app. If it is localhost I resolved it with re-importing localhost certificate.

brockallen commented 7 years ago

Any update? If you feel like there's a bug somewhere, it would be best to look at our samples or quickstarts and try to reproduce the bug there so it's easier for us all to identity it and track it.

Also, there was a bug found in the IdentityServer4.AspNetIdentity.2.0.0-rc1-update1, and so there's now a 2.0.0-rc1-update2.

nukec commented 7 years ago

@brockallen I am currently back on 1.1 so I will see when I have time to try 2.0 again. What was the bug on IdentityServer4.AspNetIdentity.2.0.0-rc1-update1?

brockallen commented 7 years ago

It was this: https://github.com/IdentityServer/IdentityServer4.AspNetIdentity/issues/41

brockallen commented 7 years ago

All set on this issue -- can we close?

brockallen commented 7 years ago

Oh looks like I already asked a while ago. I'll close for now. Reopen if/when this becomes an issue again.

VictorioBerra commented 6 years ago

@brockallen

Why do you do this two different ways in your two different API samples?

IdentityServer4.Samples/Clients (AddAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme))

https://github.com/IdentityServer/IdentityServer4.Samples/blob/release/Clients/src/SampleApi/Startup.cs#L20

IdentityServer4.Samples/Quickstarts (AddAuthentication("Bearer"))

https://github.com/IdentityServer/IdentityServer4.Samples/blob/release/Quickstarts/5_HybridFlowAuthenticationWithApiAccess/src/Api/Startup.cs

lock[bot] commented 4 years ago

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.