1

New to the whole Identity concept but I've had a couple of Google searches and haven't found a reply I felt fitting.

I'm using .NET Core 1.0.0 with EF Core and IdentityServer 4 (ID4). The ID4 is on a separate server and the only information I get in the client is the claims. I'd like to have access to the full (extended) user profile, preferrably from User.Identity.

So how to I set up so that the User.Identity is populated with all the properties on the ApplicationUser model without sending a DB request every time? I'd like the information to be stored in cache on authentication until the session ends.

What I don't want to do is that in each controller set up a query to get the additional information. All controllers on the client will be inheriting from a base controller, meaning I could DI some service if that's necessary.

Thanks in advance.

Client

app.UseCookieAuthentication(new CookieAuthenticationOptions
        {
            AuthenticationScheme = "Cookies"
        });

        app.UseOpenIdConnectAuthentication(new OpenIdConnectOptions
        {
            AuthenticationScheme = "oidc",
            SignInScheme = "Cookies",

            Authority = Configuration.GetSection("IdentityServer").GetValue<string>("Authority"),
            RequireHttpsMetadata = false,
            ClientId = "RateAdminApp"
        });

ID4

app.UseIdentity();

app.UseIdentityServer();

services.AddDeveloperIdentityServer()
            .AddOperationalStore(builder => builder.UseSqlServer("Server=localhost;Database=Identities;MultipleActiveResultSets=true;Integrated Security=true", options => options.MigrationsAssembly(typeof(Startup).GetTypeInfo().Assembly.GetName().Name)))
            .AddConfigurationStore(builder => builder.UseSqlServer("Server=localhost;Database=Identities;MultipleActiveResultSets=true;Integrated Security=true", options => options.MigrationsAssembly(typeof(Startup).GetTypeInfo().Assembly.GetName().Name)))
            .AddAspNetIdentity<ApplicationUser>();

ApplicationUser Model

public class ApplicationUser : IdentityUser
{
    [Column(TypeName = "varchar(100)")]
    public string FirstName { get; set; }
    [Column(TypeName = "varchar(100)")]
    public string LastName { get; set; }
    [Column(TypeName = "nvarchar(max)")]
    public string ProfilePictureBase64 { get; set; }
}
adem caglin
  • 22,700
  • 10
  • 58
  • 78
Max
  • 137
  • 2
  • 10

1 Answers1

0

If you want to transform claims on the identity server, for your case(you use aspnet identity) overriding UserClaimsPrincipalFactory is a solution(see Store data in cookie with asp.net core identity).

public class AppClaimsPrincipalFactory : UserClaimsPrincipalFactory<ApplicationUser, IdentityRole>
{
    public AppClaimsPrincipalFactory(
        UserManager<ApplicationUser> userManager,
        RoleManager<IdentityRole> roleManager,
        IOptions<IdentityOptions> optionsAccessor) : base(userManager, roleManager, optionsAccessor)
    {
    }

    public async override Task<ClaimsPrincipal> CreateAsync(ApplicationUser user)
    {
        var principal = await base.CreateAsync(user);

        ((ClaimsIdentity)principal.Identity).AddClaims(new[] {
             new Claim("FirstName", user.FirstName)
        });

        return principal;
    }
}

// register it
services.AddScoped<IUserClaimsPrincipalFactory<ApplicationUser>, AppClaimsPrincipalFactory>();

Also you can use events(on the client application) to add extra claims into cookie, it provides claims until the user log out.

There are two(maybe more than) options:

First using OnTicketReceived of openidconnect authentication:

    app.UseOpenIdConnectAuthentication(new OpenIdConnectOptions
    {
        AuthenticationScheme = "oidc",
        SignInScheme = "Cookies",

        Authority = Configuration.GetSection("IdentityServer").GetValue<string>("Authority"),
        RequireHttpsMetadata = false,
        ClientId = "RateAdminApp",
        Events = new OpenIdConnectEvents
        {
           OnTicketReceived = e =>
           {
               // get claims from user profile
               // add claims into e.Ticket.Principal
               e.Ticket = new AuthenticationTicket(e.Ticket.Principal, e.Ticket.Properties, e.Ticket.AuthenticationScheme);

               return Task.CompletedTask;
            }
        }
    });

Or using OnSigningIn event of cookie authentication

app.UseCookieAuthentication(new CookieAuthenticationOptions
{
     AuthenticationScheme = "Cookies",
     Events = new CookieAuthenticationEvents()
     {
         OnSigningIn = async (context) =>
         {
             ClaimsIdentity identity = (ClaimsIdentity)context.Principal.Identity;
             // get claims from user profile
             // add these claims into identity
         }
     }
});

See similar question for solution on the client application: Transforming Open Id Connect claims in ASP.Net Core

Community
  • 1
  • 1
adem caglin
  • 22,700
  • 10
  • 58
  • 78
  • So any additional properties I'd like the user profile to have would need to be added as claims, predefined or custom? E.g. having FirstName on the ApplicationUser model wouldn't really provide any value then? – Max Oct 07 '16 at 08:07
  • Dou you want to add claims in identity server ? See my update. – adem caglin Oct 07 '16 at 08:13
  • Definitely looks like getting closer to what I'm after! My only concern about having properties duplicated is that I'd need to ensure that both the user's claims and the AspNetUsers properties would need to sync. But could I then use OnSigningIn to instead just select all properties into context.Principal.Identity and that way avoid ensuring claims and AspNetUsers properties get out of sync? @adem-caglin – Max Oct 07 '16 at 08:47