I am new to LDAP authentication. I want to authenticate the users with their LDAP credentials.
For that I got the application credential from server so that I can authenticate other users.
My credential as application user are as:
Test LDAP Server IP: xx.xx.xx.xx
Port: 389
Bind DN:uid=uidxx,ou=applications,dc=dcxx
password: passxx
For authenticating the this user, I have written the code as
public String ldap()
{
String value=null;
SearchControls constraints= new SearchControls();
Hashtable<String, String> env= new Hashtable<String, String>();
env.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL,"ldap://xx.xx.xx.xx:389");
env.put(Context.SECURITY_AUTHENTICATION, "simple");
env.put(Context.SECURITY_PRINCIPAL,"uid=uidxx, ou=applications, dc=dcxx");
env.put(Context.SECURITY_CREDENTIALS,"passxx");
try {
DirContext ctx = new InitialDirContext(env);
value = "Done with it";
}
catch(AuthenticationException e)
{
value = "Invalid User name or password";
}
catch (NamingException e) {
e.printStackTrace();
value = "Exception occurred";
}
return value;
}
I got return value as "Done with it"
Now i want to authenticate other user whose mail id and password is known to me.
Its like i want authenticate other users using their mailid, password, and for that i got uidxx and passxx to authenticate them.
How should i do that?
Not getting much help from the other sources.
Thank you
When authenticating against LDAP common work flow is
Bind to LDAP using credentials you have. This user have to have at least read-only access to subtree with users. Often it is ou=People, ou=Domain, dc=com or similar.
Query LDAP server for user's DN (here is where ANR might be useful)
Try to bind to LDAP using user's DN and password supplied to your application
This works because it is very common to give every user RW rights to his object in database. Very useful if you want user to be able to change their own password.
Related
We have been using Amplify and Cognito to register our users for an Angular6 application deployed to Lambda. The client wanted to transition from email to username as primary user identification. So we created a new user pool / client. I don't have visibility into the configuration settings, I was simply given new user pool, identity pool, and client id's. Then I changed the code for application signup to look like this:
return from(Auth.signUp({
'username': username, // was email
'password': password,
attributes: { // added these
'email': email,
'phone_number': phone_number,
'family_name': name,
'birthdate': DOB,
'custom:last_4_ssn': SSN // custom attribute
}}));
The response I'm getting with no other changes made is: Unable to verify secret hash for client. Google claims the problem is that secretAccess is currently an unsupported configuration, but the guy who has access to these services swears to me that nowhere is secretAccess configured in our setup.
I apologize for not having access to the configuration, but is there any other possible reason to receive this error?
That error is probably originating from the fact that the app client you are connected to has an associated secret key. When you create a user pool app client, it generates a secret by default:
Right now, with React-Native Amplify you have to use an app client that does not have a secret key generated. So when you create a new app client with your desired attributes, make sure the "Generate client secret" box is unchecked.
The solution is to pass secret_hash along with the adminAuthInitiate Request. And to calculate the secret hash you can use the following method:
public static String calculateSecretHash(String userPoolClientId, String userPoolClientSecret, String userName) {
final String HMAC_SHA256_ALGORITHM = "HmacSHA256";
SecretKeySpec signingKey = new SecretKeySpec(
userPoolClientSecret.getBytes(StandardCharsets.UTF_8),
HMAC_SHA256_ALGORITHM);
try {
Mac mac = Mac.getInstance(HMAC_SHA256_ALGORITHM);
mac.init(signingKey);
mac.update(userName.getBytes(StandardCharsets.UTF_8));
byte[] rawHmac = mac.doFinal(userPoolClientId.getBytes(StandardCharsets.UTF_8));
return Base64.getEncoder().encodeToString(rawHmac);
} catch (Exception e) {
throw new RuntimeException("Error while calculating ");
}
}
How to Pass Secret_Hash
Map<String, String> authParams = new HashMap<>(2);
authParams.put("USERNAME", <username>);
authParams.put("PASSWORD", <password>);
authParams.put("SECRET_HASH", calculateSecretHash(cognitoClientId, cognitoClientSecret, <username>));
AdminInitiateAuthRequest authRequest = new AdminInitiateAuthRequest()
.withClientId(userPool.getClientId()).withUserPoolId(userPool.getUserPoolId())
.withAuthFlow(AuthFlowType.ADMIN_NO_SRP_AUTH).withAuthParameters(authParams);
AdminInitiateAuthResult result = cognito.adminInitiateAuth(authRequest);
auth = result.getAuthenticationResult();
I have created a custom user storage provider which will migrate users from legacy system to keycloak's local storage on demand basis.
All the details of the migrated user is being stored in Keycloak except password.
userModel.setEmail(email);
userModel.setEnabled(isEnabled);
userModel.setEmailVerified(isEmailVerified);
userModel.setFirstName(firstName);
userModel.setLastName(lastName);
I am using the above code to store all the information of the user, but I didn't find any method/class in which stores the password.
Can anyone please help me with it?
P.S. I am using Keycloak-3.3.0-Final version.
You can use
session.userCredentialManager().updateCredential(realm, user, UserCredentialModel.password(passwordNew, false));
where session is the current KeycloakSession which you have access to in your custom user storage provider.
Thanks to Boomer's answer I managed to make it work in my implementation where the isValid function - which sends the POST request to validate the password - needed to trigger the update of password in Keycloak database.
#Override
public boolean isValid(RealmModel realm, UserModel user, CredentialInput input) {
if (!supportsCredentialType(input.getType()) || !(input instanceof UserCredentialModel)) return false;
UserCredentialModel cred = (UserCredentialModel)input;
// sending a POST request
Response response = userService.validateLogin(user.getUsername(), new EventivalUserCredentialsDto(cred.getValue()));
boolean isValid = HttpStatus.SC_OK == response.getStatus();
if (isValid) {
// save the password to local (keycloak's native) database
session.userCredentialManager().updateCredential(realm, user, cred);
// unset the federation link to never ask again - Import Implementation Strategy
user.setFederationLink(null);
}
return isValid;
}
I'm developing a simple JAX - RS server and I need to implement Authentication and Authorization. I have found in other posts that the standard way is to use the standard Java EE web servlet container security with role-mapping in conjunction with OAuth, Form-based Authentication or Basic Authentication.
My problem is my company has a custom Authentication Provider accessible by an EJB client. The Authentication provider takes username and password in input and return a UserObject with a series of rights associated with it. If the rights list contain the one associated with my REST server, then the user is also authorized to ask for resources, otherwise a CustomAuthorizationException is thrown. In fact this is a 'one-shot Authorization', so i could enforce it with additional servlet container standard authorization.
This is an example:
String username = //acquire username;
String password = // acquire password;
UserObject user = null;
try {
user = authenticationClient.login(username, password);
if(user == null){
//erro message and forward to login page
}
verifyAuthorization(user);
} catch (CustomAuthorizationException e1) {
//manage exception
} catch (CustomAuthenticationException e2) {
//manage exception
}
How can I integrate this with JAX - RS standards?
I am doing a simple website with Razor. Currently, I have database-based authentication that works, as follows:
In _AppStart.chtml:
WebSecurity.InitializeDatabaseConnection("db_connection",
"users", "id", "username", true);
In login.cshtml page:
username = Request["username"];
password = Request["password"];
if (WebSecurity.Login(username, password, true))
{
Response.Redirect("/admin");
}
else
{
errorMessage = "Login was not successful.";
}
In protected CSHTML pages, I have the following at the top of a page:
if (!WebSecurity.IsAuthenticated)
{
Response.Redirect("/login.cshtml");
}
Everything is pretty simple and works well. Now I would like to add authentication with AD. I don't know how to do it.
I came from the Java world with many years of experience. For this simple website, I do not need MVC architecture. I need simple things similar to the above (if possible). I need to do authentication just within the login.cshtml file. I googled a lot and am unable to find a tutorial (so that I can copy and paste) for what I need.
Any pointers or help is really appreciated!
Thanks and Regards
Update: This application sits on the internal network.
Update 2: Here is the code I have after successfully implemented X3074861X's code
if (IsPost)
{
username = Request["username"];
password = Request["password"];
var domain = "domain";
var host = "host";
var port = "389";
LdapConnection ldapConnection = new LdapConnection(host + ":" + port);
try
{
// authenticate the username and password
using (ldapConnection)
{
// pass in the network creds, and the domain.
var networkCredential = new NetworkCredential(username, password, domain);
// if we're using unsecured port 389, set to false. If using port 636, set this to true.
ldapConnection.SessionOptions.SecureSocketLayer = false;
// since this is an internal application, just accept the certificate either way
ldapConnection.SessionOptions.VerifyServerCertificate += delegate { return true; };
// to force NTLM\Kerberos use AuthType.Negotiate, for non-TLS and unsecured, just use AuthType.Basic
ldapConnection.AuthType = AuthType.Basic;
// this is where the authentication occurs
ldapConnection.Bind(networkCredential);
//check local database to make sure the user is one of we allowed
if (WebSecurity.Login(username, "fixed-password, just to check whether someone is on the list of allowed people", true))
{
Response.Redirect("/admin");
}
else
{
errorMessage = "Login was not successful.";
}
}
}
catch (LdapException exception)
{
//Authentication failed, exception will dictate why
errorMessage = "Login was not successful.";
}
Some explanation. I dont have control over the AD and so I can only authenticate users against it. I still have a little local database that indicates who can access the app. Everyone with access to the app has the same rights.
Thanks and credit goes to X3074861X.
Since this is an internal application, and you're looking for something simple, I would consider writing a single class to do the Active Directory authentication. You're going to need a couple things though, in order for this to work :
A reference to System.DirectoryServices.Protocols in your project.
The IP or DNS name of your Active Directory server. We'll call it host in the code below.
The port it's running on (LDAPS will be port 636, basic LDAP will be port 389). We'll call it port in the code below.
The Domain to which your users belong. We'll call it domain in the code below.
Now that you have that, you can wire this up to check the credentials from the request against your AD instance. I would try something like this :
// the username and password to authenticate
username = Request["username"];
password = Request["password"];
// define your connection
LdapConnection ldapConnection = new LdapConnection("host:port");
try
{
// authenticate the username and password
using (ldapConnection)
{
// pass in the network creds, and the domain.
var networkCredential = new NetworkCredential(username, password, domain);
// if we're using unsecured port 389, set to false. If using port 636, set this to true.
ldapConnection.SessionOptions.SecureSocketLayer = false;
// since this is an internal application, just accept the certificate either way
ldapConnection.SessionOptions.VerifyServerCertificate += delegate { return true; };
// to force NTLM\Kerberos use AuthType.Negotiate, for non-TLS and unsecured, just use AuthType.Basic
ldapConnection.AuthType = AuthType.Basic;
// authenticate the user
ldapConnection.Bind(networkCredential);
}
catch (LdapException ldapException)
{
//Authentication failed, exception will dictate why
}
}
Also, in the same way you'd communicate an authorization issue before, the ldapException can tell you why the call failed. If you want to display custom messaging, I would check the LdapException.ErrorCode property, and maybe create a case statement of return messages based on the error codes.
Or, you could just output LdapException.Message directly to the page - either way, that will at least dictate to the user why their login didn't work.
I am developing a mobile application with IBM Worklight and I have some issues with the authentication. I am using a custom authenticator and a custom login module for validating the user credentials against a Tivoli directory server.
This is the code I am using, it works when I run it like a java application (class with a main method) in Worklight Studio, but when I run it like a Worklight application (in the login function of my custom login module) it returns a naming exception and prints jndi.20
public boolean login(Map<String, Object> authenticationData) {
logger.info("SmaciLoginModule :: login");
try{
USERNAME = (String) authenticationData.get("username");
PASSWORD = (String) authenticationData.get("password");
String solicuser="uid="+USERNAME+",cn=users,dc=smaci,dc=ibm";
Hashtable<String, String> env = new Hashtable<String, String>();
env.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, "ldap://127.0.0.1:1389/");
env.put(Context.SECURITY_AUTHENTICATION, "simple");
env.put(Context.SECURITY_PRINCIPAL, solicuser);
env.put(Context.SECURITY_CREDENTIALS, PASSWORD);
DirContext ctx = new InitialDirContext(env);
boolean result = ctx != null;
if(ctx != null)
ctx.close();
return result;
}catch (Exception e) {
throw new RuntimeException("Invalid credentials"+e.getMessage());
}
}
I hope that you can help me with my problem. I don't have experience working with LDAP, I appreciate any suggestion.
Thank you!
Are you trying to get the password from the user, with this line?
String pass=(String) entry.get("password").get().toString();
If so, that is unlikely to work. Passwords are almost never retrievable via LDAP. (The exceptions are slightly complex, so consider it impossible).
What you want to do instead is try to bind with the solicuser built DN, and the PASSWORD value. Then on success (and password is not empty, since that always succeeds, but as an anonymous bind, so you have to watch for it) you know you authenticated. Else you fail it. You might wish to examine the possible error messages. Various LDAP servers give different errors. Some will report a bad password, or a bad DN (i.e. No such user). Others not so much.