Question

I have been trying to Integrate Identity Server 4 with SPA application. I am able to Authorize the Application in API but after the authorization the User.Claims are always empty though i have added the Claims in Scopes.

I am using Asp.net Identity in API with entity framework core.

My project are distributed in different projects.

  1. Project.Auth (using Identity Server 4)
  2. Project.Admin
  3. Project.Data (where my Context and Migration Lies)
  4. Project.Domain(Enities)
  5. Project.Service(Repository and ViewModel)

Startup.cs For Project.Admin

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddDbContext<MyContext>(options =>
                options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

        services.AddAuthorization();

        services.AddIdentity<User, IdentityRole<Guid>>()
            .AddEntityFrameworkStores<MyContext>()
            .AddDefaultTokenProviders();

        services.AddAuthentication("Bearer")
            .AddIdentityServerAuthentication(options =>
            {
                options.Authority = "https://localhost:44305";
                options.RequireHttpsMetadata = false;
                options.ApiName = "api1";
            });

        services.AddCors(options =>
        {
            options.AddPolicy("default", policy =>
            {
                policy.WithOrigins("http://localhost:8080")
                    .AllowAnyHeader()
                    .AllowAnyMethod();
            });
        });

        services.AddScoped(typeof(IRepository<>), typeof(EfRepository<>));
        services.AddScoped<IContractService, ContractService>();
        services.AddScoped<IClientService, ClientService>();

        services.AddAutoMapper(mapperConfig => mapperConfig.AddProfiles(GetType().Assembly));


        services.AddMvcCore()
            .AddJsonFormatters();
    }

Identity Server Setup

services.AddIdentityServer()
    .AddDeveloperSigningCredential()
    .AddConfigurationStore(options =>
    {
        options.ConfigureDbContext = builder =>
            builder.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"),
                sql => sql.MigrationsAssembly(typeof(MyContext).GetTypeInfo().Assembly.GetName().Name));
    })
    .AddOperationalStore(options =>
    {
        options.ConfigureDbContext = builder =>
            builder.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"),
                sql => sql.MigrationsAssembly(typeof(MyContext).GetTypeInfo().Assembly.GetName().Name));
    }).AddAspNetIdentity<User>();

TestUser.cs

public class TestUsers
{
    public static List<TestUser> Users = new List<TestUser>
    {
        new TestUser{SubjectId = Guid.NewGuid().ToString(), Username = "alice", Password = "alice",
            Claims =
            {
                new Claim(JwtClaimTypes.Name, "Alice Smith"),
                new Claim(JwtClaimTypes.Role,"Admin"),
                new Claim(JwtClaimTypes.GivenName, "Alice"),
                new Claim(JwtClaimTypes.FamilyName, "Smith"),
                new Claim(JwtClaimTypes.Email, "AliceSmith@email.com"),
                new Claim(JwtClaimTypes.EmailVerified, "true", ClaimValueTypes.Boolean),
                new Claim(JwtClaimTypes.WebSite, "http://alice.com"),
                new Claim(JwtClaimTypes.Address, @"{ 'street_address': 'One Hacker Way', 'locality': 'Heidelberg', 'postal_code': 69118, 'country': 'Germany' }", IdentityServer4.IdentityServerConstants.ClaimValueTypes.Json)
            }
        }
    };
}

Client

new Client
{
    ClientId = "js",
    ClientName = "JavaScript Client",
    AllowedGrantTypes = GrantTypes.Implicit,
    AllowAccessTokensViaBrowser = true,
    AlwaysIncludeUserClaimsInIdToken = true,
    RedirectUris =            new List<string> {"http://localhost:8080/silent","http://localhost:8080/authredirect"},
    PostLogoutRedirectUris =   { "http://localhost:8080" },
    AllowedCorsOrigins =     { "http://localhost:8080" },

    AllowedScopes =
    {
        IdentityServerConstants.StandardScopes.OpenId,
        IdentityServerConstants.StandardScopes.Profile,
        "api1",
        "role"
    }
}

ApiResource

new ApiResource("api1", "My API")

IdentityResources

public static IEnumerable<IdentityResource> GetIdentityResources()
{
    return new List<IdentityResource> {
        new IdentityResources.OpenId(),
        new IdentityResources.Profile(),
        new IdentityResources.Email(),
        new IdentityResource {
            Name = "role",
            UserClaims = new List<string> {"role"}
        }
    };
}

Decode Token

{
  "nbf": 1525602392,
  "exp": 1525605992,
  "iss": "https://localhost:44305",
  "aud": [
    "https://localhost:44305/resources",
    "api1"
  ],
  "client_id": "js",
  "sub": "c81ce899-77d9-4c34-ab31-b456129ee762",
  "auth_time": 1525601959,
  "idp": "local",
  "scope": [
    "openid",
    "profile",
    "role",
    "api1"
  ],
  "amr": [
    "pwd"
  ]
}

Why the API is able to authorize and authenticate the Request but no Details on User and Claims? Did i missed anything on the API startup class? or there is some misconfiguration on the precedence on the startup class.

The Claims and User used to have the value before i added the DI for Context and Services on the Startup Class.

I tried again by removing the references to Project.Service and removing every thing from the Statrup class in Project.Admin. I was able to get the Claim information. As shown below.

Claim Information when Removed Dependency from Start Up

However when i add the DI to Context and other services. My Claim info got lost. However i am still authenticated and it is passing my Authorize Filter.

Break Point on <code>User.Claims</code>

Edited: When i was checking the log on my application i found a error

"Identity.Application" was not authenticated. Failure message: "Unprotect ticket failed"

1
2
5/9/2018 12:00:54 PM

Accepted Answer

I have found my Solution for this problem. I was missing couple of things on my code:

  1. There was the Duplicate references to IdentityServer4.AccessTokenValidation.
  2. I was missing the DefaultChallengeScheme on my API ConfigureServices

    services.AddAuthentication(options =>
    {
        options.DefaultAuthenticateScheme = IdentityServerAuthenticationDefaults.AuthenticationScheme;
        options.DefaultChallengeScheme = "oidc";
    })
    .AddIdentityServerAuthentication(options =>
    {
      options.Authority = "https://localhost:44305";
      options.RequireHttpsMetadata = false;
      options.ApiName = "api1";
    });
    

So my Configure Service became like below:

 public void ConfigureServices(IServiceCollection services)
{

    services.AddMvcCore().AddAuthorization().AddJsonFormatters();

    var connectionString = Configuration.GetConnectionString("DefaultConnection");
    services.AddDbContext<MyContext>(o => o.UseSqlServer(connectionString));
    services.AddIdentity<User, IdentityRole<Guid>>().AddEntityFrameworkStores<MyContext>().AddDefaultTokenProviders();


    services.AddAuthentication(
        options =>
        {
            options.DefaultAuthenticateScheme = IdentityServerAuthenticationDefaults.AuthenticationScheme;
            options.DefaultChallengeScheme = "oidc";
        })
    .AddIdentityServerAuthentication(options =>
    {
        options.Authority = "https://localhost:44305";
        options.RequireHttpsMetadata = false;
        options.ApiName = "api1";

    });

    services.AddCors(options =>
    {
        // this defines a CORS policy called "default"
        options.AddPolicy("default", policy =>
        {
            policy.WithOrigins("http://localhost:8080")
                .AllowAnyHeader()
                .AllowAnyMethod();
        });
    });
    services.AddScoped(typeof(IRepository<>), typeof(EfRepository<>));
    services.AddScoped<IContractService, ContractService>();
    services.AddScoped<IClientService, ClientService>();

    services.AddAutoMapper(mapperConfig => mapperConfig.AddProfiles(GetType().Assembly));

}

Changing above two things solved my problem for missing claims and Authorized without the Bearer Token.

1
5/9/2018 12:00:14 PM

Popular Answer

Try this

var user = User.Claims.First(claim => claim.Type=="Name").Value(); 

I am not an expert , but I think this is how you should work with Claims instead of the older versions of Asp.Net where placing User sufficed



Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow