Skip to content
This repository has been archived by the owner on Dec 13, 2022. It is now read-only.

[Question] How is ProfileDataRequestContext.RequestedClaimTypes populated? #1067

Closed
deastr opened this issue Apr 17, 2017 · 6 comments
Closed
Labels

Comments

@deastr
Copy link

deastr commented Apr 17, 2017

I've red the documentation, checked the samples but I couldn't find any detailed info about ProfileDataRequestContext.RequestedClaimTypes in IProfileService implementation. I've tried different client/scope/resource combinations but context.ProfileDataRequestContext is always empty. When does this property contains data?

@leastprivilege
Copy link
Member

Whenever you request a scope that has associated claims.

@mathilde-c
Copy link

mathilde-c commented Jan 15, 2018

Hi,

I know my post is quite late, but I m running in the same issue and all the info on the internet are not helping me much. I am probably missing out something, here is my current code:
Identity & resources

public static IEnumerable<IdentityResource> GetIdentityResources()
{
	return new List<IdentityResource>
	{
		new IdentityResources.OpenId(),
		new IdentityResources.Profile(),
	};

}
public static IEnumerable<ApiResource> GetApiResources()
{
	return new List<ApiResource>
	{
		new ApiResource("api1", "My API"),

		new ApiResource
		{
			Name = "organisation",
			UserClaims =
			{
				"workspace"
			},
			Scopes =
			{
				new Scope
				{
					Name = "orga"
				}
			}
		}
	};
}

Then I have my client configuration as follow:

// MVC client
new Client
{
	ClientId = "mvc",
	ClientName = "MVC Client",
	AllowedGrantTypes = GrantTypes.HybridAndClientCredentials,

	ClientSecrets = 
	{
		new Secret("secret".Sha256())
	},

	RedirectUris = { "http://localhost:5002/signin-oidc" },
	PostLogoutRedirectUris = { "http://localhost:5002/signout-callback-oidc" },

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

	AllowOfflineAccess = true,


	AlwaysIncludeUserClaimsInIdToken = true,
	UpdateAccessTokenClaimsOnRefresh = true
},
// SPA Client
new Client
{
	ClientId = "js",
	ClientName = "JavaScript Client",
	AllowedGrantTypes = GrantTypes.Implicit,
	AllowAccessTokensViaBrowser = true,

	AlwaysIncludeUserClaimsInIdToken = true,
	UpdateAccessTokenClaimsOnRefresh = true,

	RedirectUris = { "http://localhost:5003/callback.html" },
	PostLogoutRedirectUris = { "http://localhost:5003/index.html" },
	AllowedCorsOrigins = { "http://localhost:5003" },

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

and in my Startup.cs I have:

	
 public void ConfigureServices(IServiceCollection services)
{
	services.AddTransient<IResourceOwnerPasswordValidator, ResourceOwnerPasswordValidator>();
	services.AddTransient<IProfileService, ProfileService>();

	services.AddMvc();

	// configure identity server with in-memory stores, keys, clients and scopes
	services.AddIdentityServer()
		.AddDeveloperSigningCredential()
		.AddInMemoryIdentityResources(Config.GetIdentityResources())
		.AddInMemoryApiResources(Config.GetApiResources())
		.AddInMemoryClients(Config.GetClients())
		//.AddTestUsers(Config.GetUsers());
		.AddProfileService<ProfileService>();

	services.AddAuthentication()	
		.AddOpenIdConnect("oidc", "OpenID Connect", options =>
		{
			options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme;
			options.SignOutScheme = IdentityServerConstants.SignoutScheme;

			options.Authority = "https://demo.identityserver.io/";
			options.ClientId = "implicit";

			options.TokenValidationParameters = new TokenValidationParameters
			{
				NameClaimType = "name",
				RoleClaimType = "role"
			};
		});
}

Finally, I have IProfileService implemented as follow

public class ProfileService : IProfileService
{
	private IUserLogic _userLogic;

	public ProfileService(IUserLogic userLogic)
	{
		_userLogic = userLogic;
	}

	//Get user profile date in terms of claims when calling /connect/userinfo
	public async Task GetProfileDataAsync(ProfileDataRequestContext context)
	{
		try
		{
			//depending on the scope accessing the user data.
			if (!string.IsNullOrEmpty(context.Subject.Identity.Name))
			{
				//get user from db (in my case this is by email)
				var user = await _userLogic.GetByEmail(context.Subject.Identity.Name);

				if (user != null)
				{
					var claims = GetUserClaims(user);

					//set issued claims to return
					context.IssuedClaims = claims.Where(x => context.RequestedClaimTypes.Contains(x.Type)).ToList();
				}
			}
			else
			{
				//where and subject was set to my user id.
				var userEmail = context.Subject.Claims.FirstOrDefault(x => x.Type == "sub");

				if (!string.IsNullOrEmpty(userEmail?.Value))
				{
					//get user from db (find user by user id)
					var user = await _userLogic.GetByEmail(userEmail.Value);

					// issue the claims for the user
					if (user != null)
					{
						var claims = GetUserClaims(user);

						context.IssuedClaims = claims.Where(x => context.RequestedClaimTypes.Contains(x.Type)).ToList();
					}
				}
			}
		}
		catch (Exception ex)
		{
			//log your error
		}
	}

	//check if user account is active.
	public async Task IsActiveAsync(IsActiveContext context)
	{
		//[...]
	}
	
	public static Claim[] GetUserClaims(User user)
	{
		return new Claim[]
		{
		new Claim("user_id", user.Id.ToString()),//("user_id", user.UserId.ToString() ?? ""),
		new Claim(JwtClaimTypes.Name, $"{user.FirstName} {user.LastName}"),//(!string.IsNullOrEmpty(user.Firstname) && !string.IsNullOrEmpty(user.Lastname)) ? (user.Firstname + " " + user.Lastname) : ""),
		new Claim(JwtClaimTypes.Email, user.Email),
		new Claim("workspace", "wx|wz")
		};
	}
}

So now, according to my understanding of the doc, the line context.IssuedClaims = claims.Where(x => context.RequestedClaimTypes.Contains(x.Type)).ToList(); should not be empty, and more specifically, context.RequestedClaimTypes should contains "workspace"

I think I'm missing a pretty important point, but I cannot gasp which one.

@StanislavChankov
Copy link

StanislavChankov commented Oct 30, 2018

I have the same issue with empty RequestedClaimTypes, can you please reopen the ticket?

Is it expected that RequestedClaimTypes is filled with the ApiResource.claimTypes, instead of the associated claims to the requested scopes.

@redplane
Copy link

Any updates ?
I have the same issue. The RequestedClaimTypes is always empty.

@bookwood1977
Copy link

You could try:

  • context.AddRequestedClaims( principal.Claims );
    insteadof
  • context.IssuedClaims

Check the properies of the context . There can be different content for "caller"

  • e.g. ClaimsProviderIdentityToken (filling then the Id-Token )

@lock
Copy link

lock bot commented Jan 11, 2020

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

@lock lock bot locked as resolved and limited conversation to collaborators Jan 11, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

No branches or pull requests

6 participants