When creating (SAML2.0) SP metadata, I can request attributes to be returned by the IdP. For example:
...
<md:AttributeConsumingService>
<md:RequestedAttribute FriendlyName="givenName"
Name="urn:oid.2.5.4.42"/>
</md:AttributeConsumingService>
...
On successful login, I will see this as part of AttributeStatement:
...
<saml2:AttributeStatement xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">
<saml2:Attribute Name="givenName">
Tom
</saml2:Attribute>
</saml2:AttributeStatement>
... I think.
Question is: Does my requested Requested[FriendlyName] value, in this case "givenName" map directly to the returned Attribute[Name] value? That is, if I simply change FriendlyName's value to "FirstName", should I expect the returned Attribute to be named "FirstName" rather than "givenName"?
And, does such a change require a change on the IdP side? That is, should I expect that the IdP is "looking" at value of FriendlyName rather than the Name="urn:oid.2.5.4.42", or should it flow transparently through (a well built IdP)?
Or, is asserted attribute's Name tied to the requested Name oid & and if I request Name="urn:oid.2.5.4.42", I'll get an asserted attribute Name="givenName", always.
The IdP will return something similar to this:
<AttributeStatement>
<Attribute Name="urn:oid:2.5.4.42" FriendlyName="givenName" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri">
<AttributeValue>Dave</AttributeValue>
</Attribute>
</AttributeStatement>
The formal name of the attribute is urn:oid:2.5.4.42 but it also has a human readable name which is 'givenName'. The FriendlyName is only for user interface things like showing the user what attributes have been released. The SP should use the formal name (urn:oid ...) to make authorisation decisions etc.
You can see what a FriendlyName is likely to be from the eduPerson spec which also gives you the SAML2 Name for the attribute.
The IdP will release what you ask it to release, subject to data laws etc and you need to show you need that attribute to provide a service to the IdP's users. The IdP will obtain the mail attribute from its store, e.g. Active Directory and send it to you in an AttributeStatement, subject to the profile in use, in this case SAML2 WBSSO which requires urn:oid Name and FriendlyName. You shouldn't rely on FriendlyName for authorisation but should use the SAML2 Name instead. FriendlyName is only to show to users if they want to know which attributes have been released by their IdP.
You can see examples of RequestedAttribute here.
Related
Our organization stores signing certificates in Active Directory. We are using anonymous bind to search for them at a base DN (e.g. OU=MY ORG,dc=mydc,dc=org). I have been trying to use the Spring LdapTemplate to look them up, but no matter what method I use, I get the cryptic InterruptedNamingException.
Assuming a cert subject of cn=mycert.myorg.com
My code looks like this
LdapContextSource contextSource = new LdapContextSource();
contextSource.setUrl(String.format(LDAP_URL_FORMAT, ldapCertStoreParameters.getServerName(),
ldapCertStoreParameters.getPort()));
contextSource.setBase(ldapCertStoreParameters.getBaseDn());
contextSource.setAnonymousReadOnly(true);
contextSource.afterPropertiesSet();
LdapTemplate ldapTemplate = new LdapTemplate(contextSource);
ldapTemplate.setIgnorePartialResultException(true);
ldapTemplate.afterPropertiesSet();
X500Principal principal = x509CertSelector.getSubject();
Object obj = ldapTemplate.lookup(new LdapName(principal.getName()));
The X500 principal's name is the whole dn. cn=mycert.myorg.com,OU=MY ORG,dc=mydc,dc=org
I have also tried the search using just the cn.
We have verified that the DN exists on the server using Apache Directory Studio.
• I would suggest you to please remove the call altogether or set the ‘userSearchBase’ either to an empty String (“”) as per the given example in the below community thread: -
Configure Spring security for Ldap connection
As in the ‘AbstractContextSource’, set the base suffix from which all operations should origin. If a base suffix is set, you will not have to (and, indeed, must not) specify the full distinguished names in any operations performed. Since you specified the full DN for the userDN/filter, you must not specify the base.
AD servers are apparently unable to handle referrals automatically, which causes a ‘PartialResultException’ to be thrown whenever a referral is encountered in a search. To avoid this, set the ‘ignorePartialResultException’ property to true. There is currently no way of manually handling these referrals in the form of ‘ReferralException’, i.e., either you get the exception (and your results are lost) or all referrals are ignored (if the server is unable to handle them properly). Neither is there any simple way to get notified that a ‘PartialResultException’ has been ignored.
For more details regarding the LDAP template search for Active Directory stored certificates, kindly refer to the link below: -
https://docs.spring.io/spring-ldap/docs/current/apidocs/org/springframework/ldap/core/LdapTemplate.html
• Also, please try to refer to the below documentation for configuration of Springboot LDAP template configuration through certificates stored in Active Directory: -
https://www.baeldung.com/x-509-authentication-in-spring-security
I need to know the client's grant type (or OAuth Flow type) in my API protected by IdentitySrever4, but not sure how to do that. I am assuming that I need to add the grant type to the access token. Can someone help me by pointing me to instructions/documentations or sample code?
UPDATE
Under standard IdentityServer4 EF model, my SQL Server data store has a ClientGrantTypes table and a ClientClaims table (see screenshot below). I am assuming that I need to create a ClientClaims record that ties into ClientGrantTypes. If it is SQL, you can just join the tables on ClientID, but how do you implement it here to get the Grant Type into access token?
It would be easier to find the answer if you explained the purpose for the requirement. From my experience one common task is to distinguish authorization_code and client_credentials flow use for the same client, but that's easy: the second one does not contain user information (sub and sid claims).
Also don't forget about restricted auth flow combinations in Identityserver (for instance you can't allow both implicit and authorization_code flow for the same client), so one client is usually bound to the only user interactive flow.
Finally, the auth flow is generally not about API. It's only about interaction among IdP and Client. API usually use scopes as general information about clients, so... when you have two clients -- one with implicit grant and the other with authorization_code, you can distinguish which one is in use by setting different scopes.
Isn't that enough?
A check for a particular grant type could be performed in Identityserver the following way:
public class ExtendedClaimsService : DefaultClaimsService{
public override async Task<IEnumerable<Claim>> GetAccessTokenClaimsAsync(
ClaimsPrincipal subject,
ResourceValidationResult resourceResult,
ValidatedRequest request)
{
var outputClaims = (await base.GetAccessTokenClaimsAsync(subject, resourceResult, request)).ToList();
//if (request.Secret.Type == "NoSecret") //this is more or less the same
if ((request as ValidatedTokenRequest)?.GrantType != "client_credentials")
{
//filter out server-side-only scopes here
//or add any custom claim you like
}
return outputClaims;
}
}
Registration: services.AddTransient<IClaimsService, ExtendedClaimsService>(); after services.AddIdentityServer() in your Startup
One option is to have different client definitions for the different flows and then use a ClientClaim to indicate what type of client it is:
To set the client claims in the client definition just set it like:
ClientClaimsPrefix="",
AlwaysSendClientClaims=true,
Claims = new List<ClientClaim>()
{
new ClientClaim("role","admin"),
new ClientClaim("name","joe"),
new ClientClaim("admin","yes"),
new ClientClaim("employmentid","employee"),
new ClientClaim("employeetype","yes"),
new ClientClaim("creditlimit","100000")
}
I am using Delphi 10.3 and IPWorks LDAP component. I can modify most attributes without any issues, such as unicodePwd, givenName, and mail. However, for the userAccountControl attribute of a user, I am unable to set ADS_UF_PASSWD_CANT_CHANGE, of course after successfully binding as an administrator with secure connection and supplying correct DN, because if the connection is not secure, it is impossible to modify the password:
const
ADS_UF_NORMAL_ACCOUNT = 512;
ADS_UF_DONT_EXPIRE_PASSWD = 65536;
ADS_UF_PASSWD_CANT_CHANGE = 64;
ADS_UF_LOCKOUT = 16;
ipaLDAP1.DN := searchResultDN;
ipaLDAP1.AttrCount := 1;
ipaLDAP1.AttrType[0] := 'userAccountControl';
ipaLDAP1.AttrValue[0] := IntToStr(ADS_UF_NORMAL_ACCOUNT + ADS_UF_DONT_EXPIRE_PASSWD + ADS_UF_LOCKOUT + ADS_UF_PASSWD_CANT_CHANGE);
ipaLDAP1.AttrModOp[0] := amoReplace;
ipaLDAP1.Modify();
It is strange that I can not modify ADS_UF_PASSWD_CANT_CHANGE. It doesn't take effect on the user. When I check the user, this attribute is still unchecked. I don't understand why.
userAccountControl is a bitmask, so you should be using the or operator to combine flags, not the + operator.
But, more importantly, according to How to use the UserAccountControl flags to manipulate user account properties:
PASSWD_CANT_CHANGE
Note: You cannot assign this permission by directly modifying the UserAccountControl attribute. For information about how to set the permission programmatically, see the "Property flag descriptions" section.
Where the "Property flag descriptions" section says:
PASSWD_CANT_CHANGE - The user cannot change the password. This is a permission on the user's object. For information about how to programmatically set this permission, visit the following Web site:
Modifying User Cannot Change Password (LDAP Provider)
That page, in turn, says:
The ability of a user to change their own password is a permission that can be grant or denied. To deny this permission, set two ACEs in the security descriptor discretionary access control list (DACL) of the user object with the ADS_ACETYPE_ACCESS_DENIED_OBJECT ace type. One ACE denies the permission to the user and another ACE denies the permission to the Everyone group. Both ACEs are object-specific deny ACEs that specify the GUID of the extended permission for changing passwords. To grant this permission, set the same ACEs with the ADS_ACETYPE_ACCESS_ALLOWED_OBJECT ace type.
The following procedure describes how to modify or add ACEs for this permission.
To modify or add the ACEs for this permission
Bind to the user object.
Obtain the IADsSecurityDescriptor object from the ntSecurityDescriptor property of the user object.
Obtain an IADsAccessControlList interface for the security descriptor from the IADsSecurityDescriptor.DiscretionaryAcl property.
Enumerate the ACEs for the object and search for the ACEs that have the change password GUID ({AB721A53-1E2F-11D0-9819-00AA0040529B}) for the IADsAccessControlEntry.ObjectType property and "Everyone" or "NT AUTHORITY\SELF" for the IADsAccessControlEntry.Trustee property.
Note: The "Everyone" and "NT AUTHORITY\SELF" strings are localized based on the language of the first domain controller in the domain. Because of this, the strings should not be used directly. The account names should be obtained at run time by calling the LookupAccountSid function with the SID for "Everyone" ("S-1-1-0") and "NT AUTHORITY\SELF" ("S-1-5-10") well-known security principals. The GetSidAccountName, GetSidAccountName_Everyone, and GetSidAccountName_Self C++ example functions shown in Reading User Cannot Change Password (LDAP Provider) demonstrate how to do this.
Modify the IADsAccessControlEntry.AceType property of the ACEs that were found to ADS_ACETYPE_ACCESS_DENIED_OBJECT if the user cannot change their password or ADS_ACETYPE_ACCESS_ALLOWED_OBJECT if the user can change their password.
If the "Everyone" ACE is not found, create a new IADsAccessControlEntry object that contains the property values shown in the table below and add the new entry to the ACL with the IADsAccessControlList.AddAce method.
If the "NT AUTHORITY\SELF" ACE is not found, create a new IADsAccessControlEntry object with the same property values shown in the table below except the Trustee property contains the account name for SID "S-1-5-10" ("NT AUTHORITY\SELF"). Add the entry to the ACL with the IADsAccessControlList.AddAce method.
To update the ntSecurityDescriptor property of the object, call the IADs.Put method with the same IADsSecurityDescriptor obtained in Step 2.
Commit the local changes to the server with the IADs.SetInfo method.
If either of the ACEs were created, you must reorder the ACL so that the ACEs are in the correct order. To do this, call the GetNamedSecurityInfo function with the LDAP ADsPath of the object and then the SetNamedSecurityInfo function with the same DACL. This reordering will occur automatically when the ACEs are added.
The following table lists the IADsAccessControlEntry object property values.
AccessMask
ADS_RIGHT_DS_CONTROL_ACCESS
AceType
ADS_ACETYPE_ACCESS_DENIED_OBJECT if the user cannot change their password or ADS_ACETYPE_ACCESS_ALLOWED_OBJECT if the user can change their password.
AceFlags
0
Flags
ADS_FLAG_OBJECT_TYPE_PRESENT
ObjectType
"{AB721A53-1E2F-11D0-9819-00AA0040529B}" which is the change password GUID in string form.
InheritedObjectType
Not used
Trustee
Account name for SID "S-1-1-0" (Everyone).
There is a fairly lengthy code example provided on the same page.
I am using MSAL to validate a user against Microsoft Azure AD and am getting a valid token back.
The MSAL library is handing me back a User object which contains a property called userIdentifier.
This is a string, not a GUID.
The documentation says to use a field called oid to uniquely identify users across the Microsoft Identity platform. I have this property available under User.idToken.oid.
This value is a GUID as the documentation says.
My question is, what is this User.userIdentifier and should I be using this? I need to know what to store on the database side to tie this local user to an Azure AD user.
For clarity's sake it is advised to use the accountIdentifier property as it will always be populated, even in the edge cases where oid might not be available:
const accountIdentifier: string = idToken.objectId || idToken.subject;
More info on GitHub.
Looking at the source code (https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-core/src/Account.ts), there is no userIdentifier, but there is an accountIdentifier.
Could be yours is an older version, in which case this might not be correct for that.
// create accountIdentifier
const accountIdentifier: string = idToken.objectId || idToken.subject;
It is either the oid or sub claim, whichever has a value.
If you want the oid, you should get it from the idTokenClaims property on Account.
I'm trying to authenticate with spotipy using Authorization Code Flow like this:
token = util.prompt_for_user_token(username, scope, client_id=client_id,
client_secret=client_secret,redirect_uri=redirect_url)
When I assign any string to "username", I'm asked to authenticate the request in a browser which is popping up; everything works fine.
When I set a different string to "username" before running my code a second time, the authentication is done against the previously authenticated username (which is still authenticated in the browser session); just as if the value of "username" is not taken into account at all.
Also, I seem to be able to set any arbitrary value to "username" like "pipapo"; when I login to my Spotify account (which isn't "pipapo", obviously) this one is authenticated and methods like current_user_saved_tracks() do get the resources of the account authenticated instead of "pipapo".
Anyways: The access_token and refresh_token are saved to the cachefile .cache-pipapo; thus saving the credentials of the "who-ever-logged-into-the-browser" to the file named after the "wrong" account.
So: What is this parameter good for then, if ultimately the user's interactive selections are responsible for what the code is doing? And why is this even a required parameter if more or less not utilized in the auth process?
I just had a look at spotipy/util.py myself, how def prompt_for_user_token(...) is designed and what parameter "username" is used for; indeed, it is used for defining the caching file name only to hand it over to oauth2.SpotifyOAuth() like so:
cache_path=".cache-" + username
So, you can use any value here; it does not need to be the correct username, necessarily.