1

I'm using IdentityServer4 in ASP.NET Core on Framework 4.6.2 with EntityFramework 6 and Asp.Net Identity 2 (not Core).

For this I implemented the IProfileService.
And I am using in my client with Angular 4.x the oidc-client.js library.

I write the access_token in the localStorage to get it and mount the header with the Authentication Bearer.

The problem is that in IPrincipal the Name and Claim are empty. The IsAuthenticated property is false.

My Client configuration:

new Client
{
    ClientName  = "API Client",
    ClientId = IdentityContants.Clients.ImplicitClient.ClientId,
    RequireClientSecret = false,
    AllowedGrantTypes = GrantTypes.Implicit,
    AccessTokenType = AccessTokenType.Jwt,
    AllowAccessTokensViaBrowser = true,
    AlwaysSendClientClaims = true,
    RequireConsent = false,

    RedirectUris = { ... },
    PostLogoutRedirectUris = { ... },
    AllowedScopes =
    {
        IdentityServerConstants.StandardScopes.OpenId,
        IdentityServerConstants.StandardScopes.Profile,
        IdentityContants.Scopes.ApiScope.Name,
    }
}

The configuration in API:

JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();

app.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions
{
    Authority = IdentityContants.AuthServer,
    ApiName = IdentityContants.Clients.ImplicitClient.ClientId,
    LegacyAudienceValidation = true,
    AllowedScopes = new[]
    {
        "openid",
        "profile",
        IdentityContants.Scopes.ApiScope.Name
    },
    RequireHttpsMetadata = false
});

And I have these settings in Angular 4.x applications:

const settings: any = {
  authority: environment.urls.api.account,
  client_id: environment.clients.api.id,
  redirect_uri: environment.urls.front.apps + '/auth',
  post_logout_redirect_uri: environment.urls.front.apps,
  response_type: 'id_token token',
  scope: 'api.scope openid profile',
  loadUserInfo: true
};

I'm using JwtClaimTypes.Name and ClaimTypes.Name. I tried with and without the JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear().

The ProfileService

public class ProfileService : IProfileService
{
    private readonly AppUserManager _userManager;

    public ProfileService(AppUserManager userManager)
    {
        _userManager = userManager;
    }

    public async Task GetProfileDataAsync(ProfileDataRequestContext context)
    {
        var subject = context.Subject;
        if (subject == null) throw new ArgumentNullException(nameof(context.Subject));

        var subjectId = subject.GetSubjectId();

        var user = await _userManager.Users
            .Include( ... the includes )
            .SingleOrDefaultAsync(x => x.Id == subjectId);

        if (user == null)
            throw new ArgumentException("Invalid subject identifier");

        context.IssuedClaims = await GetClaimsFromUserAsync(user);
    }

    public async Task IsActiveAsync(IsActiveContext context)
    {
        var subject = context.Subject;
        if (subject == null) throw new ArgumentNullException(nameof(context.Subject));

        var subjectId = subject.GetSubjectId();
        var user = await _userManager.Context.Users
            .Include( ... the includes )
            .SingleOrDefaultAsync(x => x.Id == subjectId);

        context.IsActive = await ValidateSecurityStamp(user, subject);
    }

    private async Task<bool> ValidateSecurityStamp(Usuario user, ClaimsPrincipal subject)
    {
        if (user == null)
            return false;

        if (!_userManager.SupportsUserSecurityStamp)
            return true;

        var securityStamp = subject.Claims.Where(c => c.Type == "security_stamp").Select(c => c.Value).SingleOrDefault();
        if (securityStamp == null)
            return true;

        var dbSecurityStamp = await _userManager.GetSecurityStampAsync(user.Id);
        return dbSecurityStamp == securityStamp;
    }

    public async Task<List<Claim>> GetClaimsFromUserAsync(Usuario user)
    {
        var claims = new List<Claim>
        {
            new Claim(JwtClaimTypes.Subject, user.Id),
            new Claim(JwtClaimTypes.Name, user.UserName),
            new Claim(JwtClaimTypes.PreferredUserName, user.UserName),
            new Claim(ClaimTypes.Name, user.UserName)  // to test
        };

        if (_userManager.SupportsUserEmail)
        {
            claims.AddRange(new[]
            {
                new Claim(JwtClaimTypes.Email, user.Email),
                new Claim(JwtClaimTypes.EmailVerified, user.EmailConfirmed ? "true" : "false", ClaimValueTypes.Boolean)
            });
        }

        if (_userManager.SupportsUserClaim)
        {
            claims.Add(new Claim(CustomClaimTypes.User.Name, user.Name));
            ... others claims

            claims.AddRange(await _userManager.GetClaimsAsync(user.Id));
        }

        if (_userManager.SupportsUserRole)
        {
            var roles = await _userManager.GetRolesAsync(user.Id);

            claims.AddRange(roles.Select(role => new Claim(JwtClaimTypes.Role, role)));
            claims.AddRange(roles.Select(role => new Claim(ClaimTypes.Role, role))); // to test
        }

        return claims;
    }
}

And, then Action Login Post:

[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Login(LoginInputModel model)
{
    if (ModelState.IsValid)
    {
        var user = await _userManager.Users
            .Include( ... the includes )
            .SingleOrDefaultAsync(x => x.UserName
                .Equals(model.Username, StringComparison.CurrentCultureIgnoreCase));

        if (await _userManager.CheckPasswordAsync(user, model.Password))
        {
            AuthenticationProperties props = null;
            if (AccountOptions.AllowRememberLogin)
            {
                props = new AuthenticationProperties
                {
                    IsPersistent = true,
                    ExpiresUtc = DateTimeOffset.UtcNow.Add(AccountOptions.RememberMeLoginDuration)
                };
            }

            await _events.RaiseAsync(new UserLoginSuccessEvent(user.UserName, user.Id, user.UserName));
            await HttpContext.Authentication.SignInAsync(user.Id, user.UserName, props);

            if (!_interaction.IsValidReturnUrl(model.ReturnUrl) || !Url.IsLocalUrl(model.ReturnUrl))
                return Redirect(model.ReturnUrl);

            return Redirect(Urls.Apps);
        }

        await _events.RaiseAsync(new UserLoginFailureEvent(model.Username, "invalid credentials"));

        ModelState.AddModelError("", AccountOptions.InvalidCredentialsErrorMessage);
    }

    return View(new LoginInputModel(model.Username, model.ReturnUrl));
}

Nothing work!
What could be wrong?

JamesTK
  • 113
  • 8
  • What exactly are you trying to do? Can you please update your post with your IProfileServer implementation? What is your main goal? – moritzg May 10 '17 at 05:45
  • I have an authentication server with Ids4 using Implicit Flow and I also have the APIs that consume this authentication service. For these APIs I have clients in Angular 4.x. With oidc-client.js I am redirected to the authentication service, I can log in normally and step through my AuthService of the application with Angular, thus being able to access the application and get all the data of the access_token returned. But when you make a request, none of the user information, such as the Name, appears in the IIdentity (ClaimsIdentity). I'll post my ProfileService, yeah, a moment. – JamesTK May 10 '17 at 12:47
  • How do you log the users in? – moritzg May 10 '17 at 12:48
  • @moritzg the Auth Server uses the QuickStart UI of Ids4. However, I changed the code a bit to validate the user through the UserManager. I'll post this snippet of code as well. – JamesTK May 10 '17 at 12:52
  • @moritzg, edited, thanks! – JamesTK May 10 '17 at 13:03
  • @JamesTK Did you managed to solve this problem? – Rasik May 07 '18 at 15:10

1 Answers1

0

You need to add the following to your authentication middleware.

TokenValidationParameters = new TokenValidationParameters
{
    NameClaimType = JwtClaimTypes.Name;
}
Lutando
  • 4,909
  • 23
  • 42
  • Hi, thanks for your help! But I'm not using `UseIdentityServerAuthentication`, and I also did not find this option `UseJwtBearerAuthentication`. Is there any other way to configure this setting? – JamesTK May 10 '17 at 12:39
  • Sorry, I changed to `UseJwtBearerAuthentication` and I found this option. But, dont work. – JamesTK May 10 '17 at 13:33