Store password in Keycloak - passwords

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;
}

Related

IdentityServer4 Revoke all reference tokens for a client, except the current one

I have a single page application which is protected by IdentityServer4 reference tokens.
I expect users to login to from multiple computers/devices.
In the settings area of the app, the user can change their password. To do so, they must enter their current password, as well as the new password.
I also wish to give the user the option to "Logout all other devices and computers".
If the user ticks this option, I want to invalidate any other reference tokens that exist for this client and this user, but I do NOT want to invalidate the reference token the user is currently using.
I only want it to logout other devices and computers. The user should stay logged in on the computer they are using.
For the life of me, I cannot see a way to do this with IdentityServer4. I was thinking I could simply run a delete on the PersistedGrants table, however I have no way of knowing which of the persisted grants in this table is the one the user is currently using.
Please help!
I was finally able to solve this. Make sure you're using the latest version of IdentityServer, as it includes a session_id column on the PersistedGrants table. With that, the solution is clear.
When user changes password:
if (model.EndSessions)
{
var currentSessionId = User.FindFirst(JwtClaimTypes.SessionId).Value;
foreach (var grant in db.PersistedGrants.Where(pg => pg.ClientId == "the-client-name" && pg.SubjectId == user.Id.ToString() && pg.SessionId != currentSessionId).ToList())
{
db.PersistedGrants.Remove(grant);
}
db.SaveChanges();
await userManager.UpdateSecurityStampAsync(user);
}
The user's other tokens are now revoked.
However, the user (on their other computer/devices) will likely still have an authentication cookie, so if they were to go to the authorization endpoint they would be granted a new token without having to login again.
To prevent that, we intercept the request for a new token with a CustomProfileService, like so -
public override async Task IsActiveAsync(IsActiveContext context)
{
//only run check for cookie authentication
if (context.Subject.Identity.AuthenticationType == IdentityConstants.ApplicationScheme)
{
var validationResponse = await signInManager.ValidateSecurityStampAsync(context.Subject);
if (validationResponse == null)
{
context.IsActive = false;
return;
}
var user = await userManager.GetUserAsync(context.Subject);
context.IsActive = user.IsActive;
}
}

How to invalidate a JWT token of a user after password is changed by someone

I created an MVC page used JWT authentication. Once a user logged in successfully, the app returns a JWT token to the user that's stored in the request header. But then a problem occurred. Another person also signs in with the same user account and changes the password. So the first logged in session should be terminated because of security issues. The solution I thought is invalidating the JWT token of that user. But I have to define when was the user's password changed. The JWT token doesn't contain the password information so I couldn't request to the backend server to determinate the password was changed every time the user (with old password) request to the server, either. I need some ideas, suggestions.
For this feature you should add new property like SerialNumber as string on Users table
public class User { public string SerialNumber { get; set; } }
when you want to create new token for user add user SerialNumber to Claims like this
new Claim(ClaimTypes.SerialNumber, user.SerialNumber, ClaimValueTypes.String, issuer),
and when changed user password or username or status or every important property you should update serial number. when serial changed on token validator method after first http request will raise error code 401 (that means Unauthorized)
public async Task ValidateAsync(TokenValidatedContext context)
{
var claimsIdentity = context.Principal.Identity as ClaimsIdentity;
if (claimsIdentity?.Claims == null || !claimsIdentity.Claims.Any())
{
context.Fail("This is not our issued token. It has no claims.");
return;
}
var serialNumberClaim = claimsIdentity.FindFirst(ClaimTypes.SerialNumber);
if (serialNumberClaim == null)
{
context.Fail("This is not our issued token. It has no serial.");
return;
}
var userIdString = claimsIdentity.FindFirst(ClaimTypes.UserData).Value;
if (!int.TryParse(userIdString, out int userId))
{
context.Fail("This is not our issued token. It has no user-id.");
return;
}
var user = await _signInService.GetUserAsync(userId);
if (user == null)
{
context.Fail("User deleted!");
return;
}
if (user.SerialNumber != serialNumberClaim.Value || !user.Status)
{
context.Fail("This token is expired. Please login again.");
return;
}
}
on JWT Token configuration
OnTokenValidated = context =>
{
var tokenValidatorService = context.HttpContext.RequestServices.GetRequiredService<ITokenFactoryService>();
return tokenValidatorService.ValidateAsync(context);
},
Using the password directly in the token is a security risk, since an attacker could retrieve it from the user's computer. Better to either:
include a unique token ID in the token, and maintain a list of revoked tokens (or of allowed tokens; whichever is a better fit). or
include a "version number" in the user list, change it whenever the password is changed, and include the version number when the token is issued. That way, all old tokens can be rejected. #Mohammad's answer has an example of something similar.
None of those pieces of information means anything by themselves.
Use password as claim in jwt?
It will be checked in every request, so after password is changed it will return 401

How to Create login and logout using Vapor (Basic Authentication)

I want to create login and logout methods and routes. I've done already basic authentication but now I'm stuck how to continue. How should I do that, should I use sessions?
I'm using Vapor 3, Swift 4 and PostgreSQL and followed this tutorial https://medium.com/rocket-fuel/basic-authentication-with-vapor-3-c074376256c3. I'm total newbie so I appreciate a lot if you can help me!
my User model
struct User : Content, PostgreSQLModel, Parameters {
var id : Int?
private(set) var email: String
private(set) var password: String
}
extension User: BasicAuthenticatable {
static let usernameKey: WritableKeyPath<User, String> = \.email
static let passwordKey: WritableKeyPath<User, String> = \.password
}
UserController.swift, registering user.
private extension UserController {
func registerUser(_ request: Request, newUser: User) throws -> Future<HTTPResponseStatus> {
return try User.query(on: request).filter(\.email == newUser.email).first().flatMap { existingUser in
guard existingUser == nil else {
throw Abort(.badRequest, reason: "a user with this email already exists" , identifier: nil)
}
let digest = try request.make(BCryptDigest.self)
let hashedPassword = try digest.hash(newUser.password)
let persistedUser = User(id: nil, email: newUser.email, password: hashedPassword)
return persistedUser.save(on: request).transform(to: .created)
}
}
}
So in Basic authentication there is no 'logout' per se as there's no login. With HTTP Basic Auth you transmit the user's credentials with each request and validate those credentials with each request.
You mention sessions, but first it's important to know what type of service you are providing? Are you providing an API or a website? They are different use cases and have different (usually) methods for authentication and login.
For an API you can use Basic Authentication and generally in your login function you exchange the credentials for some sort of token. Clients then provide that token with future requests to authenticate the user. To log out you simply destroy the token in the backend so it is no longer valid.
For a website, things are a little different since you can't manipulate the requests like you can with a normal client (such as setting the Authorization header in the request). HTTP Basic authentication is possible in a website, though rarely used these days. What traditionally happens is you submit the user's credentials through a web form, authenticate them and then save the authenticated user in a session and provide a session cookie back to the browser. This authenticates the user in future requests. To log a user out you just remove the user from the session.
Vapor's Auth package provides everything you need to do both of these scenarios. See https://github.com/raywenderlich/vapor-til for examples of both

Apache Shiro - LDAP for Authentication and Properties/Ini for Authorization

i´m trying to add some authentication and authorization functionality to my small web application. therefore i´m using apache shiro.
my plan: using an existing ldap server for user authentication and using a properties or ini file for authorization.
here´s a small example:
user x wants to use the application
he enters his username and his password
the ldap server is used for authentication --> user + pwd correct?
if authentication is verified and correct, a properties file or ini file is used to check if the user is permitted, to start some functions inside the application.
i hope you know what i´m trying to do.
now i´m not sure how to implement this feature. is it enough to use an ini file or is it required to implement my own realm?! is there an example implementation?
i´m grateful for every information
and sorry for my bad english :/
Yes, you have to implement a realm but this is not difficult. You just have to extend JndiLdapRealm and override the queryForAuthorizationInfo method.
This method returns an AuthorizationInfo interface type. In your case the easiest is to return an instance of SimpleAuthorizationInfo which implements this interface.
You must initialize the AuthorizationInfo with the roles and/or permissions for the authenticated user. When this method is called, the user is already authenticated but not authorized.
Inside this method you can read the authorization information from any data source that you want, it can be a properties or ini file, properties associated with the user in the LDAP server, a database or anything that pleases you.
A realm implementation could be:
package example.shiro.realm.ldap;
import javax.naming.NamingException;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.ldap.JndiLdapRealm;
import org.apache.shiro.realm.ldap.LdapContextFactory;
import org.apache.shiro.subject.PrincipalCollection;
public class JndiLdapAuthzRealm extends JndiLdapRealm {
private List<String> getRoles(String userName) {
List<String> roles = new ArrayList<>();
// TODO: get roles from data source and fill list
roles.add("user");
roles.add("admin");
return roles;
}
private List<String> getPermissions(String userName) {
List<String> perms = new ArrayList<>();
// TODO: get permissions from data source and fill list
perms.add("myapp:run");
perms.add("myapp:file:create");
return perms;
}
#Override
protected AuthorizationInfo queryForAuthorizationInfo(PrincipalCollection principals,
LdapContextFactory ldapContextFactory) throws NamingException {
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
String userName = principals.getPrimaryPrincipal().toString();
info.addRoles(getRoles(userName));
info.addStringPermissions(getPermissions(userName));
return info;
}
}
In your case, rewrite the getRoles and getPermissions to get the roles and permissions for the authenticated user from the properties or ini file.
In shiro.ini:
[main]
ldapRealm = example.shiro.realm.ldap.JndiLdapAuthzRealm
ldapRealm.userDnTemplate = uid={0},cn=users,cn=accounts,dc=example,dc=com
ldapRealm.contextFactory.url = ldap://192.168.0.10

Symfony Security / Custom User Provider : How to get the login password within the custom User Provider?

I'm using Symfony Security with a custom User Provider in my system. It provide users via a web service.
I configure the provider according to this tutorial (http://symfony.com/doc/current/cookbook/security/custom_provider.html).
Here is the function which check the user:
public function loadUserByUsername($username)
{
$userData = webServiceCheckUser($username);
// return an array whit user credentials
if ($userData) {
$password = $userData['password'];
$salt = $userData['salt'];
$roles = $userData['roles'];
$user = new WebserviceUser($username, $password, $salt, $roles);
return $user;
}
throw new UsernameNotFoundException(sprintf('Username "%s" does not exist.', $username));
}
This works fine, the function webServiceCheckUser() call a web service with the username and then it return an array with user data. But now I need to check the user through another web service that requires the username and encrypted password of the user to authenticate him.
I have a function that encrypts the plain text password like the web service is waiting, but I can't get the password that was typed by the user in the form login within the custom user provider class. It is ok too if I could get the password already encrypted. Either one solve the problem.
There is anyway to do this?
#Pazi is correct in that building your own custom authentication provider will work. However, it's not an easy task.
An alternative approach is to override DaoAuthenticationProvider and have it pass the password to the loadUser function.
security_listeners.xml:
<parameter key="security.authentication.provider.dao.class">
Symfony\Component\Security\Core\Authentication\Provider\DaoAuthenticationProvider
</parameter>
Bit of a hack perhaps but it saves a good deal of work.