With Implicit Flow on IdentityServer4 I am not receiving User data as Name and Claims in IIdentity when making requests with Authentication Bearer

asp.net-core asp.net-identity-3 c# entity-framework-6 identityserver4

Question

With EntityFramework 6 and ASP.NET Identity 2, I'm utilising IdentityServer4 in ASP.NET Core on Framework 4.6.2. (not Core).

I implemented the IProfileService for this.
And I'm using the oidc-client.js library with Angular 4.x in my client.

To get it and mount the header with the Authentication Bearer, I write the access token in the localStorage.

The Name and Claim fields in IPrincipal are blank, which is a concern. IsAuthenticated has the value false.

Configuration of My Client:

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 API configuration:

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 in my Angular 4.x apps, I have the following settings:

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 useJwtClaimTypes.Name and ClaimTypes.Name . I experimented both with and withoutJwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear() .

profile service

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;
    }
}

Following that, 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));
}

Neither works!
What may be a problem?

1
1
5/10/2017 1:02:27 PM

Popular Answer

ZZZ_tmp
0
5/10/2017 6:11:48 AM


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