I am fairly new to OpenId and auth in MVC and creating an OpenIddict auth server and client mvc app as shown in the authorization code flow sample. https://github.com/openiddict/openiddict-samples
I need to add companyId
from my application user to my claims and I'm trying to figure out the steps needed.
So far I found out the following:
I add the claims and scope to ticket in AuthorizationController.CreateTicketAsync
by creating a new ClaimsPrincipal with the new claims added, then adding scope by calling ticket.SetScopes
.
// Add my specific claims: CompanyId and CompanyName
var claims = new List<Claim>();
claims.Add(new Claim(MyClaimsConstants.Claims.CompanyId, user.SelectedCompanyId.ToString()));
T_Company company = await _companyRepo.GetCompany(user.SelectedCompanyId);
claims.Add(new Claim(MyClaimsConstants.Claims.CompanyName, company.CompanyName));
// Create the new identity with the added claims
var newIdentity = new ClaimsIdentity(principal.Identity, claims);
principal = new ClaimsPrincipal(newIdentity);
....
if (!request.IsAuthorizationCodeGrantType())
{
// Set the list of scopes granted to the client application.
// Note: the offline_access scope must be granted
// to allow OpenIddict to return a refresh token.
ticket.SetScopes(new[]
{
OpenIdConnectConstants.Scopes.OpenId,
OpenIdConnectConstants.Scopes.Email,
OpenIdConnectConstants.Scopes.Profile,
OpenIdConnectConstants.Scopes.OfflineAccess,
OpenIddictConstants.Scopes.Roles,
MyClaimsConstants.Scopes.Company // <-
}.Intersect(request.GetScopes()));
}
I add the destination to my claims in AuthorizationController.CreateTicketAsync to add it to the IdentityToken.
foreach (var claim in ticket.Principal.Claims)
{
// Never include the security stamp in the access and identity tokens, as it's a secret value.
if (claim.Type == _identityOptions.Value.ClaimsIdentity.SecurityStampClaimType)
{
continue;
}
var destinations = new List<string>
{
OpenIdConnectConstants.Destinations.AccessToken
};
// Only add the iterated claim to the id_token if the corresponding scope was granted to the client application.
// The other claims will only be added to the access_token, which is encrypted when using the default format.
if (((claim.Type == OpenIdConnectConstants.Claims.Name || claim.Type == OpenIdConnectConstants.Claims.Nickname) && ticket.HasScope(OpenIdConnectConstants.Scopes.Profile)) ||
(claim.Type == OpenIdConnectConstants.Claims.Email && ticket.HasScope(OpenIdConnectConstants.Scopes.Email)) ||
(claim.Type == OpenIdConnectConstants.Claims.Role && ticket.HasScope(OpenIddictConstants.Claims.Roles)) ||
((claim.Type == MyClaimsConstants.Claims.CompanyId || claim.Type == MyClaimsConstants.Claims.CompanyName) && ticket.HasScope(MyClaimsConstants.Scopes.Company))) // <-
{
destinations.Add(OpenIdConnectConstants.Destinations.IdentityToken);
}
claim.SetDestinations(destinations);
}
I add the check for scope and conditionally add claims in UserInfoController
if (User.HasClaim(OpenIdConnectConstants.Claims.Scope, MyClaimsConstants.Scopes.Company))
{
claims[MyClaimsConstants.Claims.CompanyId] = user.SelectedCompanyId;
claims[MyClaimsConstants.Claims.CompanyName] = (await _companyRepo.GetCompany(user.SelectedCompanyId))?.CompanyName;
}
I add the scope to my options when calling AddOpenIdConnect in my client so it will be added to requests.
options.Scope.Add(MyClaimsConstants.Scopes.Company);
This seems to be working as intended.
Can someone please comment on these steps or maybe point out a sample that shows this particular kind of implementation? (It seems very basic but I have no idea if I have done something really bad in the process).
This seems to be working as intended.
Because that's indeed how you're supposed to implement custom scopes.
Note that in the most recent OpenIddict versions, you can expose the standard and custom scopes supported by your server in the discovery document (that you can retrieve via http://[host]/.well-known/openid-configuration) so that client applications can easily determine whether a scope is publicly supported or not:
services.AddOpenIddict(options =>
{
// ...
options.RegisterScopes(
OpenIdConnectConstants.Scopes.Profile,
OpenIdConnectConstants.Scopes.Email,
MyClaimsConstants.Scopes.Company);
});