How do I access the user's password from LDAP in a LdapUserDetailsMapper using spring security? - passwords

We are using spring security in our web application based on spring MVC.
We are doing authentication using LDAP module of spring security which is working properly. Now I need to get the user password from LDAP for saving in the database.
For this I am using this in my code.
public class PersonContextMapper implements UserDetailsContextMapper {
public UserDetails mapUserFromContext(DirContextOperations ctx, String username, Collection<GrantedAuthority> authorities) {
Person.Essence p = new Person.Essence(ctx);
p.setUsername(username);
p.setAuthorities(authorities);
Object passwordValue = ctx.getObjectAttribute("userPassword");
return p.createUserDetails();
}
public void mapUserToContext(UserDetails user, DirContextAdapter ctx) {
Assert.isInstanceOf(Person.class, user, "UserDetails must be a Person instance");
Person p = (Person) user;
p.populateContext(ctx);
}
}
But I am not getting the any value for the password. Its always null.
Please help.
PS. My authentication is successful. It means password entered in the login form is matches properly with the password stored in the LDAP.

It might be that the authentication state of the connection does not have permission to read the value of the userPassword attribute. Most often, applications issue a BIND request to the directory server, including appropriate controls as necessary. The password is included in the BIND request and the directory server changes the authentication state of the connection upon successful completion of the BIND request. In any case, the value of the userPassword attribute is encrypted or hashed more often than not, and applications have no need to read the value.

Related

Automatically choose Auth0 DB Connection according to user's email address

I already read the multi-tenancy guide published, and I believe the solution I require for my app would be to create a separate DB Connection for each organization that I sign up.
My issue is that, since I'm going to be setting the connection parameter to a different name per client, I would have liked Universal Login to automatically determine the DB Connection name according to the user's email address. So, instead of the user manually providing some kind of a hint to which DB Connection I should authenticate them against, I would like to automatically determine that somehow.
Is there any way to do this?
I am assuming that you are using hosted login page. The easiest way to determine the connection based on client would be to pass the connection parameter when redirecting to /authorize endpoint. Thus, Lock will use the connection parameter passed in the URL as the connection to validate the user. For example:
https://[tenant]/authorize?
client_id=K8B5DJdStcZtUzbhaxAOzCrXNbo2kmXG&
response_type=token%20id_token&
redirect_uri=http://application_url&
scope=openid%20profile%20email%20&
connection=connection_name&state=123&nonce=345
Both auth0.js and auth0-spa-js can be used to pass the extra parameter(connection).
Second approach would be to use connectionResolver options if you are using Hosted Login Page+ Lock .
connectionResolver {Function}: When in use, provides an extensibility point to make it possible to choose which connection to use based on the username information. Has username, context, and callback as parameters. The callback expects an object like: {type: 'database', name: 'connection name'}.
var options = {
connectionResolver: function (username, context, cb) {
var domain = username.includes('#') && username.split('#')[1];
if (domain) {
// If the username is test#auth0.com, the connection used will be the `auth0.com` connection.
// Make sure you have a database connection with the name `auth0.com`.
cb({ type: 'database', name: domain });
} else {
// Use the default approach to figure it out the connection
cb(null);
}
}
}
Instead of username, you can take advantage of context object to identify the client (context.clientID) and choose the connection.

How to connect LDAP With username and password?

I have my Ldap working the only issue i'm facing was when I try to login with email that is when I land in the else part in the below code. If my username is different from email then it throws error. i.e if my email is 'skumar#gmail.com' and my username is 'saurakumar' then it will through invalid username password error.
As internally I'm using username to make email i.e if the user login with name 'karan' then i'm expecting the email to be karan #gmail.com which is not true in many scenario and the Authentication fails. I'm looking for some solution wherein I can login either via email or via username I'll be able to authenticate user. Below is the snippet of my code. Please suggest?
ldapEnv.put(Context.INITIAL_CONTEXT_FACTORY, initialContextFactory);
ldapEnv.put(Context.PROVIDER_URL, url);
ldapEnv.remove(Context.SECURITY_PROTOCOL);
if (email == null) {
lContext = new InitialLdapContext(ldapEnv, null);
entryResult = searchUserEntry(lContext, user, searchCtrls);
final String usrDN = ((Context) entryResult.getObject()).getNameInNamespace();
lContext.addToEnvironment(Context.SECURITY_AUTHENTICATION, "simple");
lContext.addToEnvironment(Context.SECURITY_PRINCIPAL, usrDN);
lContext.addToEnvironment(Context.SECURITY_CREDENTIALS, pass);
lContext.reconnect(null);
} else {
ldapEnv.put(Context.SECURITY_PRINCIPAL, email);
ldapEnv.put(Context.SECURITY_CREDENTIALS, credentials);
lContext = new InitialLdapContext(ldapEnv, null);
return lContext;
searchUserEntry(lContext, user, searchCtrls);
}
Normally this is a 3-step process:
Bind to LDAP as an administrative user. Note that this should not be the master user defined in the configuration file: that's for OpenLDAP's use itself. Instead it should be a user mentioned in the DIT that has the appropriate search access for the next step.
Search for the user via some unique attribute, e.g. in your case email.
Using the found DN of the user and the password he specified, attempt to bind as that user (with the reconnect() method, after changing the environment of the context appropriately).
If all that succeeds, you have a login success. If (2) or (3) fail, you have a failure, and note that you should not tell the user which it was: otherwise you are leaking information to attackers. You should not mention whether it was the username (email) or the password that was wrong.

Integrating User Pools with Amazon Cognito Identity with authentication provider

I follow the steps described in the link: http://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-integrating-user-pools-with-identity-pools.html to integrate my user pool with cognito identity. But every time I am trying to access amazone S3 using the Authentication providers I get the following error:
E/CognitoCachingCredentialsProvider: Failure to get credentials
com.amazonaws.services.cognitoidentity.model.NotAuthorizedException:
Logins don't match. Please include at least one valid login for this
identity or identity pool. (Service: AmazonCognitoIdentity; Status
Code: 400; Error Code: NotAuthorizedException; Request ID:
ff4da8ad-9a96-11e6-9c64-67a5c841c727)
at
com.amazonaws.http.AmazonHttpClient.handleErrorResponse(AmazonHttpClient.java:712)
at
com.amazonaws.http.AmazonHttpClient.executeHelper(AmazonHttpClient.java:388)
at
com.amazonaws.http.AmazonHttpClient.execute(AmazonHttpClient.java:199)
at
com.amazonaws.services.cognitoidentity.AmazonCognitoIdentityClient.invoke(AmazonCognitoIdentityClient.java:558)
at
com.amazonaws.services.cognitoidentity.AmazonCognitoIdentityClient.getId(AmazonCognitoIdentityClient.java:444)
at
com.amazonaws.auth.AWSAbstractCognitoIdentityProvider.getIdentityId(AWSAbstractCognitoIdentityProvider.java:172)
at
com.amazonaws.auth.AWSEnhancedCognitoIdentityProvider.refresh(AWSEnhancedCognitoIdentityProvider.java:76)
at
com.amazonaws.auth.CognitoCredentialsProvider.startSession(CognitoCredentialsProvider.java:561)
at
com.amazonaws.auth.CognitoCredentialsProvider.getCredentials(CognitoCredentialsProvider.java:371)
at
com.amazonaws.auth.CognitoCachingCredentialsProvider.getCredentials(CognitoCachingCredentialsProvider.java:441)
at
com.amazonaws.auth.CognitoCachingCredentialsProvider.getCredentials(CognitoCachingCredentialsProvider.java:76)
at
com.amazonaws.services.s3.AmazonS3Client.invoke(AmazonS3Client.java:4369)
at
com.amazonaws.services.s3.AmazonS3Client.putObject(AmazonS3Client.java:1704)
at
com.amazonaws.mobileconnectors.s3.transferutility.UploadTask.uploadSinglePartAndWaitForCompletion(UploadTask.java:203)
at
com.amazonaws.mobileconnectors.s3.transferutility.UploadTask.call(UploadTask.java:85)
at
com.amazonaws.mobileconnectors.s3.transferutility.UploadTask.call(UploadTask.java:44)
at java.util.concurrent.FutureTask.run(FutureTask.java:234)
at
java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1080)
at
java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:573)
at java.lang.Thread.run(Thread.java:864).
Here is the code:
public static TransferUtility getTransferUtility(Context context) {
if (sTransferUtility == null) {
sTransferUtility = new TransferUtility(getS3Client(context.getApplicationContext()),
context.getApplicationContext());
}
return sTransferUtility;
}
public static AmazonS3Client getS3Client(Context context) {
if (sS3Client == null) {
sS3Client = new AmazonS3Client(getCredProvider(context.getApplicationContext()));
}
return sS3Client;
}
private static CognitoCachingCredentialsProvider getCredProvider(Context context) {
if (sCredProvider == null) {
sCredProvider = new CognitoCachingCredentialsProvider(
context.getApplicationContext(),
Constants.COGNITO_POOL_ID,
Regions.EU_WEST_1);
Map<String, String> logins = new HashMap<>();
logins.put("cognito-idp.eu-west-1.amazonaws.com/eu-west-1_xxxxxxxxx", idToken);
sCredProvider.setLogins(logins);
}
return sCredProvider;
}
Here how I get the token
AuthenticationHandler authenticationHandler = new AuthenticationHandler() {
#Override
public void onSuccess(CognitoUserSession cognitoUserSession, CognitoDevice device) {
Log.e(TAG, "***Auth Success***");
idToken = cognitoUserSession.getIdToken().getJWTToken();
AppHelper.setCurrSession(cognitoUserSession);
AppHelper.newDevice(device);
closeWaitDialog();
launchUser();
}
The transferutility is part of com.amazonaws.mobileconnectors.s3.transferutility package.
Thank you for your help.
felini
The problem may be configuration or the way you have created your token (the provider name part looks right). But most likely you just need to GetIdResult.
"Logins don't match. Please include at least one valid login for this identity or identity pool." is coming from the "AWSCognitoIdentityService.GetCredentialsForIdentity" api request (in java i think it is GetCredentialsForIdentityResult)
This can happen when you have an identityId for one identity, then you provide logins for another. If you change identities you need to do a "AWSCognitoIdentityService.GetId" (in java i think this is GetIdResult)
The error is telling you that either it could not find the identity provider associated with the identity pool, or the pool does not have that identity provider configured (I think this generates a different error but not sure) , or it could not associate the identityId with the logins entry token (if the username claim in the ID token did not match the identity it had for instance).
I think you need to either fix your logins dictionary, or do a GetIdResult call to make sure you have the right identityId for the logins hash you are providing.
Or... if it is configuration, make sure that you have the user pool and client id properly configured in the Authentication Providers list as a Cognito user pool authentication provider. Note that if you also configure it in IAM as an identity provider you must have the audience match that same client id. (which also works).
Note: This problem might happen if you configured "unauthenticated" access, then tried to get credentials with a logins hash, but using the unauthenticated identity. I think you would need to do a getIdResult to switch.
It was a configuration problem. I added to an existing identity pool, Cognito user pool as Authentication Providers. I provided the user pool Id and client Id.
Then I click save changes. It was shown in green on the dashboard that my changes was saved. But in reality it was not! That was the reason of the error.
As solution I created a new identity pool from scratch and added during the creation, cognito user pool as authentication provider. Only then it was properly saved after the pool creation.
In case anyone else has the same warning, it is possible to login to a Cognito Identity Pool with an unverified email, but that same user will not be able to authorise against a Cognito Identity Provider, instead this error will be thrown:
Error: Logins don't match. Please include at least one valid login for
this identity or identity pool.
A user can be set to 'Confirmed' via the Cognito console. This does not equate to 'email verified'. The user CAN login to the pool though.
A fully unconfirmed user cannot login at all.

Is there a 'passive' way to implement a new hashing method for users passwords?

I have an old site which I have reworked using Symfony3 and FOSUserBundle and am trying to work out the logistics of how to map the database over without having to cause problems for all the users.
The 'old' site hashes the passwords before storing with:
sha1("$salt1$pass$salt2")
The 'new' site (using FOSUserBundle) uses it's default method of Bcrypt somewhere within the bundle.
Is the below the correct way to "Bcrypt" members plaintext passwords in the database as they log in?
Temporarily extend the Member entity with an additional parameter Bcrypted:
use FOS\UserBundle\Model\User as BaseUser;
class Member extends BaseUser
{
/* ... */
protected $Bcrypted=false;
}
Then once Members log in with their usual (sha1()) password, they are logged in as normal, but have a function such as below run - unfortunately I can't see where/how/if this would need to be done with FOSUserBundle:
public function updatePassword($member) {
if Bcrypted==false then {
/* ... Use whatever FOSUserBundle's "Change Password" function is to rehash the password in the database */
$member->setBcrypted('true');
}
}
Then I could keep an eye on the database and if/when all users have eventually logged in and had their password hash in the database converted then I could remove the function and $Bcrypted parameter

Authentication against VDS LDAP

I want to authenticate user against VDS(virtual directory server) using Java.
How VDS is different from LDAP? Or VDS is also working on LDAP
protocol?
Please help with any sample Java code for authentication against VDS
A sample code to authenticate against LDAP is as below
String userName = "John P R-Asst General Manager";
String passWord = "asdfgh123";
String base ="OU=SOU,DC=example,DC=com";
String dn = "cn=" + userName + "," + base;
String ldapURL = "ldap://mdsdc3.example.com:389";
authEnv.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.ldap.LdapCtxFactory");
authEnv.put(Context.PROVIDER_URL, ldapURL);
authEnv.put(Context.SECURITY_AUTHENTICATION, "simple");
authEnv.put(Context.SECURITY_PRINCIPAL, dn);
authEnv.put(Context.SECURITY_CREDENTIALS, password);
try {
DirContext authContext = new InitialDirContext(authEnv);
return true;
} catch (NamingException namEx) {
return false;
}
To authenticate against VDS, is a complete dn required. Because as per experts only username and password needs to be send to VDS. It will automatically find its DN and do the authentication.
Will be thankful if anyone provide nice reference material regarding ldap and vds
A virtual directory server is a type of server that provides a unified view of identities regardless of how they are stored. (Or you may prefer Wikipedia's definition: "a software layer that delivers a single access point for identity management applications and service platforms"
LDAP is a protocol (hence the "P") for communicating with directory servers.
There isn't a necessary link between LDAP and a VDS, but it is likely that a VDS provides and LDAP interface and, potentially, other programmatic interfaces (Kerberos in particular comes to mind). The details of how you communicate with the VDS are going to be dependent on the configuration you are trying to talk to, but LDAP is a good bet.
Regarding needing a full DN, you don't even need a full DN to authenticate against plain Active Directory. The more usual mode would be to supply something like DOMAIN\username (using the sAMAccountName) or username#dc.example.com (that is, the user principal name) as the SECURITY_PRINCIPAL. In your example, the user would need to type John P R-Asst General Manager rather than anything they are likely to regard as their "user name."
You do, however, need to work out what the VDS you are trying to communicate with requires as the user name. Does it need DOMAIN\username, something else? These are details that whoever runs the VDS you are communicating with should be able to provide you.
In code, you should wind up with something like this (assuming you can use LDAP):
String userName = "DOMAIN\johnp";
String passWord = "asdfgh123";
String ldapURL = "ldaps://vds.example.com";
authEnv.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.ldap.LdapCtxFactory");
authEnv.put(Context.PROVIDER_URL, ldapURL);
authEnv.put(Context.SECURITY_AUTHENTICATION, "simple");
authEnv.put(Context.SECURITY_PRINCIPAL, username);
authEnv.put(Context.SECURITY_CREDENTIALS, password);
try {
DirContext authContext = new InitialDirContext(authEnv);
return true;
} catch (NamingException namEx) {
return false;
}