asp.net-corekeycloakopenid-connectrazor-pagesclaims-based-identity

How to map a user attribute to a standard OIDC claim in keycloak


I have a docker container with Keycloak. I am trying to understand how the authentication works, especially with regards to claims mapping.

I have created a realm and in Realm Settings > User Profile I have created a new custom attribute

Keycloak Realm Settings

With this simple configuration, after login from my AspNet Core Razor application, I am able to receive the claim phone_number in token (access_token, id_token, depending on my configuration). The phoneNumber attribute also appear in registration form as well, which is good.

{
  "exp": 1726218720,
  "iat": 1726218420,
  "auth_time": 1726218420,
  "jti": "86a79a1a-ccd2-478b-9b98-6d67e94207a6",
  "iss": "http://localhost:9001/realms/web-portal",
  "aud": "account",
  "sub": "d7af94a3-a675-4dd1-b58f-8d2baa29c1f1",
  "typ": "Bearer",
  "azp": "web-portal-client",
  "sid": "8391d1be-584f-4f3c-91c4-55979a57e635",
  "acr": "1",
  "allowed-origins": [
    "http://localhost:9999"
  ],
  "realm_access": {
    "roles": [
      "offline_access",
      "default-roles-web portal",
      "uma_authorization"
    ]
  },
  "resource_access": {
    "account": {
      "roles": [
        "manage-account",
        "manage-account-links",
        "view-profile"
      ]
    }
  },
  "scope": "openid profile offline_access phone email",
  "email_verified": true,
  "name": "Tenant Admin",
  "phone_number": "123456789",
  "preferred_username": "tenantadmin@test.com",
  "given_name": "Tenant",
  "family_name": "Admin",
  "email": "tenantadmin@test.com"
}

However, because my client is requesting the standard phone scope

  builder.Services
    .AddAuthentication(options => {
        options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
        options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
    })
    .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options => {
        // omitted for brevity
    })
    .AddOpenIdConnect(options => {
        options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
        options.Authority = $"{tokenConfiguration.Authority}/realms/{tokenConfiguration.Realm}";
        options.RequireHttpsMetadata = tokenConfiguration.RequireHttpsMetadata;
        options.ClientId = tokenConfiguration.ClientId;
        options.ClientSecret = tokenConfiguration.ClientSecret;
        options.ResponseType = OpenIdConnectResponseType.Code;
        options.UsePkce = true;

        options.Scope.Clear();
        options.Scope.Add("openid");
        options.Scope.Add("profile");
        options.Scope.Add("email");
        options.Scope.Add("phone");
        options.Scope.Add("offline_access");
        options.MapInboundClaims = false;

        options.SaveTokens = true;

        options.ClaimActions.MapJsonKey("email_verified", "email_verified");
        options.ClaimActions.MapJsonKey("phone_verified", "phone_verified");
        options.GetClaimsFromUserInfoEndpoint = true;
        options.TokenValidationParameters = new TokenValidationParameters {
            NameClaimType = "name",
            RoleClaimType = "roles"
        };
  });

I expected to automatically receive two standard claims:

Is this correct? I am new to keycloak and mainly trying to match the knowledge I have with other IdPs. I have also checked this question which is very similar but has no answer.


Solution

  • Maybe I missunderstood, but you could try following steps to get a "phone_number_verified" claim.

    1. Add both phoneNumber and phoneNumberVerified attribute to userprofile. enter image description here

    2. Go to Client scopes, click "phone" scope, switch to Mappers tab then click phone number verified. Make sure this is set to the phoneNumberVerified attribute.
      enter image description hereenter image description here

    3. Add the user with this attribute value to true or false.Then after login. The accessToken will contains this "phone_number_verified" claim.
      enter image description here