Keycloak custom user federation with user attributes - keycloak

I have implemented a custom UserStorageProvider with AbstractUserAdapter for Keycloak for retrieving users from external DB and login users with credentials stored in that DB. All works ok and the data is read only via Keycloak admin panel.
I wanted to add additional attributes to users from data stored in external DB, I would like to add this data to the token via Attribute Mapper, is there a way to do it? Or do II need to implement AbstractUserAdapterFederatedStorage? The problem with the later is that it is not read only and allows to edit the user data transferred to the keycloak user store.

It really depends on your implementation. If you have access to desired data for attributes and to the user, you can simply assign new attributes to him, i.e:
KeycloakSession session = // get session somewhere
RealmModel currentRealm = session.getContext().getRealm();
UserModel user = getUserSomehow(session, currentRealm);
user.setSingleAttribute("attributeName", "attributeValue");
...
private UserModel getUserSomehow(KeycloakSession session, RealmModel realm) {
return KeycloakModelUtils.findUserByNameOrEmail(context.getSession(), context.getRealm(), "<username>");
// or (cached model):
// return session.users().getUserById("<userId>", realm);
// or (Un-cached model):
// return session.userStorageManager().getUserById("<userId>", realm);
}

Related

JWT Auth with Servicestack Ormlite - Generated Bearer token too large to use (>4096)

I'm migrating my ServiceStack Ormite MVC application to use the JWTAuthProvider for stateless auth. I have this working by authenticating as normal, and setting the returned BearerToken and RefreshToken on a successful auth:
using (var authService = HostContext.ResolveService<AuthenticateService>(ServiceStackRequest))
{
var response = await authService.PostAsync(new Authenticate
{
provider = CredentialsAuthProvider.Name,
UserName = model.UserName,
Password = model.Password,
RememberMe = true,
});
var authResponse = response.GetDto() as AuthenticateResponse;
Response.Cookies.Append("ss-tok", authResponse.BearerToken, new CookieOptions() {...});
Response.Cookies.Append("ss-reftok", authResponse.RefreshToken, new CookieOptions() {...});
}
This appears to work for some users and not for others. The problem seems to be that user accounts with a lot of permissions end up with much larger BearerToken values, some over 4096, so these cannot be set for the ss-tok cookie.
The JWT Auth provider docs provide references to the CreatePayloadFilter and the PopulateSessionFilter, however these are only used when creating a session from a token, not the other way around. I want to filter out items (the permissions in particular) when serializing to a token.
Ideally the permissions would be excluded if there are too many (or always be excluded if that's not possible) and would be lazy-loaded when accessed. This may be possible with a custom AuthUserSession inheriting from the base AuthUserSession that Lazy-loads the Permissions, but I don't know how I could do this without the JWT Provider loading the permissions to serialise too.

Mongodb Realm custom JWT auth causes empty custom user data

I'm working on an app that currently uses email/password auth provided by MongoDB Realm. I'm thinking of switching to using the custom JWT auth for various reasons. I'm quite extensively using the custom user data to store all sorts of things. When I switch from email/password to custom JWT the login works, but the custom user data is empty...
This works fine:
const jwt = await axios.post("https://MYAPI.COM/login", {
user: email.value,
password: password.value,
});
console.log(`jwt: ${JSON.stringify(jwt.data)}`);
const credentials = Realm.Credentials.emailPassword(
email.value,
password.value
);
// const credentials = Realm.Credentials.jwt(jwt.data);
console.log("logging in");
const user = await realm.logIn(credentials);
await user.refreshCustomData();
const customUserData = await user.refreshCustomData()
console.log(`Logged in as ${JSON.stringify(user)}`);
console.log(`customUserData: ${JSON.stringify(customUserData)}`);
When I uncomment the line changing the credentials the custom user data is empty
I was running into a similar problem using Custom Function for authentication.
I was mapping the userId incorrectly -
custom user data is based on the ID of the authenticated user, please make sure that the user ID is mapped correctly in the corresponding collection.
https://www.mongodb.com/docs/realm/users/enable-custom-user-data/

Apache Shiro Multirealm Authentication + find out which realm did the authentication

I have 2 possible realms to authenticate my users in my webapplication.
here are a few lines from my shiro.ini:
securityManager.realms = $ldapRealm, $saltedJdbcRealm
strategy = org.apache.shiro.authc.pam.FirstSuccessfulStrategy
securityManager.authenticator.authenticationStrategy = $strategy
Authentication works fine for both realms and the FirstSuccessfulStrategy works fine as well.
In my custom AuthenticationFilter within the executeLogin() method I have this code to do the login:
Subject currentUser = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
currentUser.login(token);
How can I now determine which realm was resposible for the authentication after the .login() method is executed?
If the user was authenticated via the LDAP Realm I would like to get some more information about the user from the LDAP.
Does anyone know how this can be done?
Subject subj = SecurityUtils.getSubject()
SimplePrincipalCollection spc = (SimplePrincipalCollection) subj.getPrincipals();
Set<String> realmNames = spc.getRealmNames();
The realmNames variable should contain one element, the realm that authenticated the user.
If your realm implementation is a standard one then the SimpleAuthenticationInfo created when the user is authenticated will have been created with the name of the realm that successfully authenticated the user.

Storing User Credientials in the Bayeux Server

I'd like to store the clients UserName and SessionId when a client subscribes to a particular channel. When i override canHandshake() i can get the user credentials using the following:
userName = (String) authentication.get("userName");
sessionId = (String) authentication.get("sessionId");
Just wondering how i can store these credentials and later retrieve them? I've had a look at the authentication documentation here and it just mentions linking the authentication data to the session. Is this the Bayeux Server side session??
Thanks
The "linking" can be done in several ways.
You can link this information in an external map via:
#Override
public boolean canHandshake(BayeuxServer server, ServerSession session, ServerMessage message)
{
...
Map<String, Object> authentication = ...;
map.put((String)authentication.get("userName"), session);
...
}
where the map can be a java.util.ConcurrentHashMap<String, ServerSession> field in the security policy itself, or in another object such as a user service.
For simpler use cases, the userName can be linked directly to the session in this way:
session.setAttribute("userName", authentication.get("userName"));
Or you can use both techniques.
This is the updated link for the authentication how-to, and you can find the latest comprehensive CometD documentation at http://docs.cometd.org.

grails spring-security create user

Im having some problems creating new user from one of my controllers. I'm trying to add a new user to my MongoDB user collection like this. Authorities is defined as a Set of Role in the domain.
Role role = new Role(authority:"ROLE_USER")
User user = new User(username:params.username,email:params.email,password:params.password,enabled:params.enabled,
accountExpired:params.accountExpired,accountLocked:params.accountLocked,passwordExpired:params.passwordExpired,
authorities:[role])
if (user.validate()) {
user.save(flush:true)
} else {
user.errors.allErrors.each { println it }
}
The exact same code is able to create a user successfully from the bootstrap, but when i'm trying to do the same thing from a simple controller i'm getting this error:
2012-09-24 10:43:27,450 [http-8080-3] ERROR binding.GrailsDataBinder - Unable to auto-create type interface java.util.Set, class java.lang.InstantiationException thrown in constructor
a:662)
Looks like the problem is with data binding. You have to create User with authorities first and then add role using UserRole domain. Something like:
Role role = Role.findByAuthority("ROLE_USER")
User user = new User(username:params.username,email:params.email,password:params.password,enabled:params.enabled, accountExpired:params.accountExpired,accountLocked:params.accountLocked,passwordExpired:params.passwordExpired)
new UserRole(user: user, role: role).save(flush: flush, insert: true)
user.save(flush:true)
For more information how to create user with spring security, you may want to look at Spring Security UI