unable to add claim to id or access token based on attributes column of accounts table - curity

After many tests I can't figure out how to add claim to userinfo / id token / access token based on the SCIM 2.0 format for the attributes column inside accounts table.
Curity 6.6.0
Mysql configured as the default datasource also for user management
Mysql attributes query configured in the datasource : SELECT * FROM accounts WHERE username = :subject
User added through the User SCIM endpoint have their data inside the 'attributes' column of the accounts table (seems to be by design when the onyl one DS exists in Curity)
family_name, given_name appears correctly in the id token as the connected user
I'm using BFF Token handler SPA code (nodejs) with the overall flow validated
Step by step :
Create a claim named 'user_type'
select the default-account-manager associates with a claim provider
in the 'select value' combo search and add userType (all attributes of the SCIM 2.0 schema are referenced in the combo)
Add the claim to a custom scope & profile scope
Configure the claim to be in the userInfo & Id Token & access token
Commit
--> No user_type field in any token or userInfo
I have tried with (return attributes.userType) and without any mapping, but no way...
The only working test is the one with a mock : return {userType: 'test'} or return 'test'.
It seems that the attributes query SELECT * FROM accounts WHERE username = :subject does'nt allow to use children fields of the attributes column.
Despite the fact that it seems to work correctly in https://curity.io/resources/learn/claims-from-authenticated-subject/ subject attributes in authentication with code
function transform(attributes) {
//Transform the attributes and return the appropriate value for the claim
if(attributes.emails !== undefined
&& attributes.emails.length > 0
&& attributes.emails[0].value !== null) {
//return {"email" : attributes.email};
return attributes.emails[0].value;
}
return null;
}
Any help will be greatly appreciate to point out what I'm doing wrong

Sounds like you got most of it right, but some details to tweak.
First, to use the attribute query, you can't use an Account Manager claims provider, that one uses the account query for the lookup and is not configurable. Instead, use the Data Source Claims Provider.
Second, the claim config can't query in multiple steps, so you'll need to transform your result.
In the picture below, I'm using a data source provider with your query, and putting the value from my attributes.timezone in the "email" claim.
Note the logger.warn, it's useful to find out what you get back from the datasource.

Related

How to set the Sort Key(Secondary key) in AWS Cognito?

I'm using Cognito User Pool for my iOS App User Registration. In general, when Registering a user with Cognito I'm using the email as userID. And also I'm collecting other info like Phone number, Business Name and etc. In this case when I try to register with the same email id with a Different Business name it will show an alert like User already Exist.
In my new Work case, I want to save/register the same email with a different Business name. How can I achieve it?
for example, if we are using a DynamoDB table we have the Partition key and Sort key. By using those we set the email as the Partition key and the Business Name as the Sort key and we can achieve uniqueness.
can we implement the same using Cognito? Does Cognito support the Partition key and Sort key concept?
Is there any way to achieve this by using Cognito?
Please help me with this issue.
Let start from this link :
Configuring User Pool Attributes
You can have changeable standard attributes as far as they are not required. You can add custom attributes as well but they are un-changeable.
Well, let's move on another case on some projects I have a need to storing a user federated identity id (i.e ap-northeast-1:3c2f5c30-0dc8-4d74-91a8-bf5c688abcde) into a cognito user pool attribute. I should store it on a custom attribute (i.e custom:identity_id) because of it will never change in the future.
Back to your case, as it will be dynamic values where users has ability to change their organization list so you can utilize an unused standard attributes for. For example, I will use "zoneinfo" although it looks strange to use unassociated attribute because there is no one with the name "organization". However at least users can pull their organization from their token once logged-in as like :
"zoneinfo": "[org1, org2, org3, etc]"
But it can't full accomodate your case as it should be stored after user registration. While if you set the "zoneinfo" on required registration, it must be unchangeable then. To solve this problem, you can utilize the cognito user pool Post-Confirmation trigger to run some logic to init a standard attribute with empty organization list (i.e "zoneinfo": "[]") adminUpdateUserAttributes. That so users can modify this attribute then because of it is not required attribute.
Sample of adminUpdateUserAttributes :
async function updateOrgAttr() {
try {
var params = {
UserAttributes: [ /* required */
{
Name: 'zoneinfo', /* required */
Value: '[]'
}
],
UserPoolId: 'ap-northeast-1_xxxxxxxx', /* required */
Username: event.userName /* event.userName is an item of post-confirmation trigger event source */
};
let cognitoidentityserviceprovider = new AWS.CognitoIdentityServiceProvider();
await cognitoidentityserviceprovider.adminUpdateUserAttributes(params).promise()
} catch(e) {
throw e
}
}

Setting up new default Roles in jhipster

How can I set up default roles in jhipster ? (using angularjs and spring).
I explain myself
in the registration page I want to specify the role for the registred user. let's say by a checkbox or a list. (for exemple human and animal )
How can I do that in the angular controller and in spring ?
What I can do now ?
I added the roles I need in the database and in angular and I can specify the roles for the new registred users , only through the Admin's users management page.
There is some work to do, to achieve that, so I will paste just the right parts with some small samples..
In general you must extend the API to become aware of a role selection, so this information can be provided explicitly. Then you change your angularJS frontend as you need.
for the backend
a registration happens by POSTing a ManagedUserVM to /api/account/register, so the first thing is to tell AccountResource.registerAccount(...) to pass a set of of strings (your roles) as additional parameter to userService.createUser
#Timed
public ResponseEntity registerAccount(#Valid #RequestBody ManagedUserVM managedUserVM) {
HttpHeaders textPlainHeaders = new HttpHeaders();
///...
User user = userService
.createUser(managedUserVM.getLogin(),
managedUserVM.getPassword(),
managedUserVM.getFirstName(),
managedUserVM.getLastName(),
managedUserVM.getEmail().toLowerCase(),
managedUserVM.getImageUrl(),
managedUserVM.getLangKey(),
//add authorities here
managedUserVM.getAuthorities()
);
mailService.sendActivationEmail(user);
//...
}
Then in UserService.createUser, you apply the set and add them to the user before saving it, by adding the Set<String> authorities to its parameters and
if (authorities != null) {
Set<Authority> authorities = new HashSet<>();
authorities.forEach(
authority -> authorities.add(authorityRepository.findOne(authority))
);
user.setAuthorities(authorities);
}
and this should be sufficient to pass authorities to /api/register/ and save them. You should be aware of users forbid to register themselves with ADMIN roles, but the security consideration is up to you and not part my answer.
apply to frontend
Knowing your API now can process also authorities, you could just pass them.
You just add some checkbox or selectbox with ng-model="vm.registerAccount.authorities" to src/main/webapp/app/account/register/register.html (if angularJS1) or
[(ngModel)]="registerAccount.authorities" tosrc/main/webapp/app/account/register/register.component.html` (if angular2).
AFAIK this should lead automatically to the angular services passing these authorities/roles to the API.
I hope my brief answer helps you to find the proper places! Feel free to ask in comments if you stuck

No given name or surname claim when using Azure Active Directory OAuth

We are authenticating our MVC application using Azure Active Directory but the only information we get back in our ClaimsPrincipal is the Name and Group Memberships.
We need access to the users given name and last name as well. Any pointers on how we can resolve this?
OpenID Connect introduces an id_token (This is a JWT).
Looking at the documentation, the id_token contains some claims that could match :
given_name: Provides the first or "given" name of the user, as set on the Azure AD user object.
family_name: Provides the last name, surname, or family name of the user as defined in the Azure AD user object.
unique_name: Provides a human readable value that identifies the subject of the token. This value is not guaranteed to be unique within a tenant and is designed to be used only for display purposes.
So in your controller you can access these claims like that:
using System.Security.Claims;
...
var identity = (ClaimsIdentity)User.Identity;
var lastName = identity.Claims.First(
c => c.Type == "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname").Value;
var firstName = identity.Claims.First(
c => c.Type == "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname").Value;
var displayName = identity.Claims.First(c => c.Type == "name").Value;
These claims correspond to the First Name, Last Name and Display Name in your Azure AD :

Using Claims Types properly in Owin Identity and Asp.Net MVC

I am using Owin and Identity and I am having a problem with Claims.
I have applications where users use an email to authenticate and others that use an username.
The sign in method in the business layer can accept an Email or an Username depending on the case.
To "obfuscate" the user identity I use a GUID, unique to each user, when displaying a page with user info.
I also use this because sometimes an email or an username can be a problem in the url ...
When I sign a user I have the following claims types:
new Claim(ClaimTypes.Email, user.Email),
new Claim(ClaimTypes.Name, user.FullName),
new Claim(ClaimTypes.GivenName, user.FirstName),
new Claim(ClaimTypes.Surname, user.LastName),
new Claim(ClaimTypes.NameIdentifier, user.UserUniqueIdentifier.ToString())
So my interpretation is:
Email is the user's email
Name is the user's full name
GivenName is the user's first name
Surname is the user's last name
NameIdentifier is the user's unique identifier ... It can be the email, the username or in this case I am using an Unique ID.
What is strange is there is no Claim Type for Username. Where would to place it?
Basically it seems there is a problem when a Username is not used as the Unique name identifier but it is still necessary.
Is something wrong with my logic claims types?
ClaimTypes.Name (http:// schemas.xmlsoap.org/ws/2005/05/identity/claims/name) should be used for the username.
ClaimTypes.NameIdentifier is typically used for the user's id. In some cases it could be a username.
ASP.NET Identity uses ClaimTypes.Name to store the username, and ClaimTypes.NameIdentifier to store the primary key GUID of the user.
If you examine what Facebook or Google return from oAuth you will see that ClaimTypes.Name is ClaimTypes.GivenName + ClaimTypes.Surname. LinkedIn returns then concatenated and I believe this is a bug because I have a completely different username there. Twitter returns username for ClaimTypes.Name, but Twitter is a special case and they do not even return email.
All of them are using some opaque numeric identifier for ClaimTypes.NameIdentifier. And they use their own string names, usually starting with urn:facebook:link, urn:google:profile, etc for custom data.
Asp.NET Identity model uses UserName for ClaimTypes.Name. The bottom line is that ClaimTypes.Name is used differently in practice. You could add any claim name as a string and could add the urn:... scheme to make it unambiguous.

Spring Security User Roles Per Organization

In my application I have a top level entity called Organization. The relationship between User and Organization is many-to-many.
Because of this I could have the following scenario:
UserA has role ROLE_ADMIN for OrganizationA
UserA has role ROLE_USER for OrganizationB
I need to ensure that when UserA accesses resources for OrganizationB he is not doing it as an ADMIN. So I need an additional check that the user has the correct roles at the organization level. Is there anything built into Spring Security that allows for this? If not, does anyone know what the best way would be to about solving this?
UPDATE: A bit more information...
A User logs in and chooses which org they want to work with. That is stored in the session. Beyond that, URLs are locked down with the Secured annotation. What that means is that if UserA were to log in and select OrgA, they should be able to access /admin/user/create however, if they log in and choose OrgB they should not have access to that URL.
The long way is to add additional checks in every method where this matters. So call some service method that says "ok, you're an admin for OrgA but not for OrgB and you're logged in using OrgB, so deny this request".
I'm hoping for a more grails / spring-security way of handling this.
You can probably do this by using a custom AccessDecisionVoter. The vote method will supply you with the "configuration attributes" for the resource (method or URL), which will typically be the required roles, and you can obtain the current user's roles/authorities either directly from the Authentication object, or by reading the current org and selecting the appropriate roles for the user.
I'm assuming that you have some way of differentiating the user's roles, based on the org they've selected.
Essentially, you'd be writing an extended version of the standard RoleVoter, which takes the organization into account.
I think I'm little late here but this is what worked for me:
When an organization is selected, you can set a new Authentication object with new roles in your session(The previous Authentication object gets invalidated). Something like this:
#RequestMapping(value = "/org-a")
String orgA(HttpServletRequest request) {
request.getSession().setAttribute("org", "org-a")
Organization org = new Organization("org-a")
reloadRolesForAuthenticatedUser(org)
....
}
private void reloadRolesForAuthenticatedUser(Organization org) {
Authentication auth = SecurityContextHolder.getContext().getAuthentication()
List<String> newRoles = getRoles(auth.getPrincipal().getUsername(), org)
List<GrantedAuthority> authorities = getAuthorities(newRoles)
Authentication newAuth = new UsernamePasswordAuthenticationToken(auth.getPrincipal(),auth.getCredentials(),authorities)
SecurityContextHolder.getContext().setAuthentication(newAuth)
}
private List<GrantedAuthority> getAuthorities(List<String> roles) {
List<GrantedAuthority> auths = new ArrayList<GrantedAuthority>()
if (!roles.isEmpty()) {
for (String r : roles) {
auths.add(new SimpleGrantedAuthority(r))
}
}
return auths
}

Resources