I'm trying to implement a custom keycloack Authenticator SPI for authenticating against an external Datasource. Spring boot Rest Service is also available, I can also use that.
Use case I am trying to solve is
User is presented keycloak login screen. Onsubmission User is validated against external Datasource.
Retrieve some attributes from external datasource, map it to keycloak's id and access token.
Also put in a condition of user restriction of same user logging in multiple times at the same time.
I was thinking, it could be solved by retrieving user session information that's available in the keycloak datasource. If i use external datasource, does keycloak still maintain session information?
I followed section 8.3 of the official guide (https://www.keycloak.org/docs/latest/server_development/index.html#_auth_spi_walkthrough), which is very similar to what I need.
Now i skipped and started as per section 11(https://www.keycloak.org/docs/latest/server_development/index.html#_user-storage-spi) seems more apt as well.
What I have done is started with implementing custom authenticator SPI thought it isn't the right way and now implemented UserStorageProvider.
/***
* From UserLookupProvider
*/
public UserModel getUserById(String id, RealmModel realm) {
System.out.println("ID: " + id + ":REALM:" + realm);
StorageId storageId = new StorageId(id);
/**
* StorageId.getExternalId() method is invoked to obtain
* the username embeded in the id parameter
*/
String username = storageId.getExternalId();
System.out.println("Name:" + username);
return getUserByUsername(username, realm);
}
/***
* From UserLookupProvider
* This method is invoked by the Keycloak login page when a user logs in
*/
public UserModel getUserByUsername(String username, RealmModel realm) {
System.out.println("USERNAME: " + username + ":REALM:" + realm);
UserModel userModel = loadedUsers.get(username);
if (userModel == null) {
String password = properties.getProperty(username);
if (password != null) {
userModel = createUserModel(realm, username);
System.out.println("New UserModel:");
System.out.println(userModel.toString());
loadedUsers.put(username, userModel);
}
}
return userModel;
}
protected UserModel createUserModel(RealmModel realm, String username) {
return new AbstractUserAdapter(session, realm, model) {
#Override
public String getUsername() {
return username;
}
};
}
Followed the doc(https://www.keycloak.org/docs/latest/server_development/index.html#packaging-and-deployment-2)
The class files for our provider implementation should be placed in a jar. You also have to declare the provider factory class within the META-INF/services/org.keycloak.storage.UserStorageProviderFactory file.
Question here is: jar I created doesn't have services directory inside "META-INF" folder, do I need to create manually and add it?
org.keycloak.examples.federation.properties.FilePropertiesStorageFactory
Once you create the jar you can deploy it using regular WildFly means: copy the jar into the deploy/ directory or using the JBoss CLI.
After creating jar using maven, copied jar to "keycloak-6.0.1\standalone\deployments" folder. but i don't see my provider in the "User Federation list"
Any suggestion/help would be greatly appreciated!!
Thanks in advance for your suggestions.
Incase if anybody ran into issues like this:
UserStorage SPI wasn't displaying because of META-INF/services folder. It's provided in the documentation but it isn't clear
In src/main/resources, create a folder structure META-INF/services
Create a file called org.keycloak.storage.UserStorageProviderFactory (the whole thing is the filename) in META-INF/services directory. Its contents is the fully qualified class name of your SPI:
com.test.UserSpi
Ok you clarified you need a User Store Provider API. Great
Now as for your 2nd "Problem/Challenge":
Retrieve some attributes from external datasource, map it to keycloak's id and access token. Need to retrieve users unique id and add it as subject id in the jwt. That's the id, rest of the services can use to retrieve the id, when this token is passed to other services.
For this, the best you can do is:
Add those user's unique data as users attributes (see them on the Admin console)
Create a "Client scope" on Keycloak, with a mapper for "user property"
to map those properties you'd like to add (from your user) to your Id-token and access-token. You also need to tie your client with your just created "client scope". This may sound a little bit confusing, but this video is great material and I bilieve it'll help you alot: https://www.youtube.com/watch?v=ZxpY_zZ52kU (arround min 6:30 you'll see how to add extra user info to your tokens)
also check this page out: https://jwt.io/ (when you paste there encoded tokens, you can see their contents), it is a great tool for developers.
When you advance in your solution I'll help with the unique session, or you post that as a different question, as that's a different problem.
Hope it helps.
I'm not exactly sure what do you need. Lets start by differentiating Authentication SPI (federated identity check) vs User Provider SPI (federated users). The first one (section 8 of the doc - is more about focusing on authenticating users against an external service - similar to facebook, or google). Federated user store is more like you have your own users in a legacy system with their legacy "roles structure", and you basically want to manage them via keycloak (either by importing them, or by querying the via some API - this would be section 11 of that documentation). So please decide what is indeed what you need.
2nd, you mention following:
> User is presented keycloak login screen. Onsubmission User is
> validated against external Datasource.
>
> Retrieve some attributes from external datasource, map it to
> keycloak's id and access token.
>
> Also put in a condition of user restriction of same user logging in
> multiple times at the same time.
>
> I was thinking, it could be solved by retrieving user session
> information that's available in the keycloak datasource. If i use
> external datasource, does keycloak still maintain session information?
What do you mean by: Retrieve some attributes from external datasource, map it to keycloak's id and access token. ? Usually you only retrieve user core information, plus maybe roles and other custom attributes (not session information). Keycloak itself as an Authorization Server based on openIDConnect, will generate acces tokens which already contains information about what protected resource can by accessed by whom, so you dont really need to import any session from somewhere else, or concern yourself with the generation of said tokens.
regarding: Also put in a condition of user restriction of same user logging in
multiple times at the same time. what exactly are you trying to accomplish (or avoid?) when you log the 1st time your client receives a Bearer token valid for X amount of time, within that time you wont need to log yourself again, until the token expires or is roveked; again something your Auth server takes care of, not something you implement. Is there something more specific you want?
I was thinking, it could be solved by retrieving user session information that's available in the keycloak datasource. If i use external datasource, does keycloak still maintain session information? This doesn't sound right, what session data do you refer to? or you need access to? your userdata, scopes, roles, etc can be accessed via KEycloak Rest API (https://www.keycloak.org/docs-api/6.0/rest-api/index.html#_overview). Your external datasource is for user related core data (not external sessions), why do you think you need to import an external session?
Related
I have an application where single user can work in contexts of multiple companies. We call such a connection (user<->company) a permit. Every one of this permits can have different sets of permissions/roles. We want user to login just once and then he can simply change permits within application without need to enter password again.
Till now we had only one application and kept this whole permission model in our own DB. Unfortunately now we have to support second application which should inherit those permits. I was wondering wether is possible to move that model to keycloak so we don't have to replicate it to every single db and keep it in sync manually.
I have searched keycloak documentation regarding this topic but have found no information att all, which seems quite odd, because I don't think we are the first one working with multiple context application.
So now I'm asking is it possible to configure our model in keycloak and if so, how to do it? Eventually are there different options? I guess that I can provided that model as a claim with json structure but that doesn't feel right to me. I was thinking about custom IDP which could provide such claims based on DB so there no spelling errors and less repetition but I feel there should be a better way.
You could try to write your own Keycloak provider (SPI). There is a built in mechanism that allows you to expose REST endpoint on the Keycloak: https://github.com/keycloak/keycloak/tree/master/examples/providers/domain-extension
That REST could be called with authorized context only for example by passing Access-Token (Authorization header with Bearer value). On the provider level (through implementation of: org.keycloak.services.resource.RealmResourceProviderFactory and org.keycloak.services.resource.RealmResourceProvider) you have access to user's Keycloak session and object UserModel like in the following code:
AuthenticationManager.AuthResult authResult = new AppAuthManager().authenticateBearerToken(keycloakSession, keycloakSession.getContext().getRealm());
UserModel userModel = authResult.getUser();
UserModel class has methods for getting and setting attributes, so some information that indicates the current permit/company ID can be stored there. You can use REST methods exposed on the Keycloak to modify the model within the 'session' (represented by Access-Token).
The Github example shows also how to use another Keycloak provider (ex. built-in JPA provider) from you custom provider's level, so using that approach you could try to connect to the database with your permits/company informations. Of course the datasource representing you database should also be registered as Keycloak datasource.
I was doing some research about drop-wizard security and authentication. Here is the link that I used http://howtodoinjava.com/dropwizard/dropwizard-basic-auth-security-example/.
My question is how to actually create new users, since VALID_USERS is a static final and can't be changed. I was thinking about creating a database, and that would consist of user object that contains the username and role ex. admin. (I don't need a password) But I am confused what I would return. In their example they returned Optional.of(new User(credentials.getUsername(), VALID_USERS.get(credentials.getUsername())));
Would I return a user object?
Essentially, I want to authenticate a user by the username and give them a role of authorization ex. admin, basic. But I guess I am confused how to generate a list of users and their roles. I was thinking of making a database, but I am not sure how exactly I would implement that.
public class AppBasicAuthenticator implements Authenticator<BasicCredentials, User>
{
private static final Map<String, Set<String>> VALID_USERS = ImmutableMap.of(
"guest", ImmutableSet.of(),
"user", ImmutableSet.of("USER"),
"admin", ImmutableSet.of("ADMIN", "USER")
);
#Override
public Optional<User> authenticate(BasicCredentials credentials) throws AuthenticationException
{
if (VALID_USERS.containsKey(credentials.getUsername()) && "password".equals(credentials.getPassword()))
{
return Optional.of(new User(credentials.getUsername(), VALID_USERS.get(credentials.getUsername())));
}
return Optional.empty();
}
}
in the latest version of DropWizard you can find it possible both to do Authentication and Authorization. The former, in a nutshell, instructs DropWizard to ask a user for credentials if you use basic authentication when she tries to access a resource or provide some other identity check. The latter allows one to grant a user access to various resources based on user's roles.
The are various possibilities how you can store user data and roles. Examples include a database which you mentioned, a LDAP server and a third-party identity management system.
If you are interested in Basic Authentication, you can take a look at my example here. A database is used to store user's credentials. Also, here is my little bit dated tutorial on DropWizard authentication. The code for the latest version is in the aforementioned example application.
To implement authentication only, you can skip adding roles and registering an Authorizer. To add authorization you can add a roles collection to your User entity and use annotations such as #RolesAllowed and #PermitAll along with Authorizer implementation to grant/deny access to your resources.
A link to DropWizard authentication docs is here.
Please feel free to ask more questions in comments and good luck.
I have a REST service implemented using Spring MVC (RestControllers) with token based security (using Spring Security). How can i filter resources depending on user identity? Let's say user has some reports. How can I let authorized user by performing a call to /reports to see only his reports?
Obviously i can make userId to be a request parameter or path variable, but something tells me that this is a bad practice.
I assume i can achieve that using Spring Security features, but how exactly could i do that and, more important, where is the most appropriate place to apply such filtering? Should controllers perform calls to services passing user identity or should it be somehow retrieved at repositories level (I use Spring Data JPA)?
Thanks in advance
You have Authentication object whenever a user is successfully logged in.
It contains Object principal Object credentials and Set authorities.
All you need to do is override UserDetailsService to add new parameters for your authenticated user. Add your userId in authentication as shown in blog
Now when you do
SecurityContextHolder.getContext().getAuthentication().getPrincipal()
this will return you the User object of the spring security.
You can get the user id from here and use this in controller to do necessary actions.
first post here, hope im doing right.
In a project, we have a scenario where we have a single web application with multiple entities. Currently, the login is managed via default JDBC Spring Security provider, working fine.
For a new requirement, we need that each entity can have their own login method (currently 2 methods would be available, the JDBC one, which is the current one, and the second method would be authentication via SAML, with each entity defining their own IdP, but this is another story)
I need some guidelines on how this can be achieved, I have done some search and I have found providers for different URL's, etc... But not different login methods for the same app and url's depending on the user type or entity.
Is a good approach to have a custom single entry point where we can check the entity user and then use the suitable authentication provider?
Kind regards,
Alex
As each of your users might be using a different IDP you will in any case need to determine the username before proceeding with initialization of the authentication process - but you already know this.
One approach to take (similar to what Microsoft is using with the Office 365 for corporate users) is:
display a login page with fields for standard username + password
once user enters username and blurs the input field, you make an AJAX call (to your custom API made for this purpose) and fetch information about authentication type + IDP to use for this user
in case the type is password you simply let user continue with filling in the password field and POST to the same place as you're used to for processing with the JDBC provider
in case the type is federated authentication you initialize authentication with the correct IDP by redirecting to /saml/login?idp=xyz and continue with the SAML flow
It's possible to avoid any APIs by submitting the form once user enters the username, or let user click a "Continue" button. It would then make sense to use a custom EntryPoint which:
redirects user to the main login page in case it wasn't provided with a username
displays either login page with username/password or redirects to the correct IDP, once username was provided
I'm working on a cloud endpoints backend and want to restrict certain operations to admin users.
My current code works like this:
#ApiMethod(httpMethod = "PATCH", name = "item.update", path = "items")
public Item update(Item newObject, User user)
throws UnauthorizedException, OAuthRequestException {
OAuthService oAuthService = OAuthServiceFactory.getOAuthService();
if (!oAuthService.isUserAdmin()) {
throw new UnauthorizedException("Only admin users can modify content.");
}
...
}
I know app engine has a concept of user roles, but I'm curious if Endpoints
do. I've tried using the OAuthService.isUserAdmin() call but that doesn't
seem to be working out very well and the docs have a big old warning saying
Note: You should not confuse Endpoints auth with the auth for
non-Endpoints App Engine web apps described in the article on configuration settings
https://developers.google.com/appengine/articles/auth in the Admin
Console, where you also specify the user login requirement in your
web.xmlhttps://developers.google.com/appengine/docs/java/config/webxml#Security_and_Authentication
file. That approach is not used with Endpoints."
Do I have to create some sort of authorization myself that uses the User object that's passed into the update method? Any thoughts?
I had similar issues. Indeed OAuth user service has nothing to do with AppEngine user service. What I ended up doing was having a dedicated user type entity in my datastore where I store a specific flag (regular/admin) for each user. This flag is updated when I use AppEngine user service (i.e. so that the administrators I specified in the console get the proper admin flag).
In my endpoints API I get the current user authDomain and id, look up in my datastore to check whether it has the admin flag. The key of my user entity is composed of "authDomain:userId" and as I only support google user for now, it looks like (gmail.com:123456789)
This means that an administrator has to login once using the AppEngine UserService (i.e. a dedicated webpage in my case) so that the flag is properly updated
I needed to do the same thing and validate some endpoint to grant access only to admin members listed in the project console and used the same implementation presented above, but the oAuthService.isUserAdmin() accept one or more string parameters, this parameters are scopes that you specify and the Oauth uses to get user informations, in my case i just set this parameter and it works like the code bellow.
OAuthService authService = OAuthServiceFactory.getOAuthService();
User user;
try {
com.google.appengine.api.users.User currentUser =
authService.getCurrentUser(Constants.EMAIL_SCOPE);
if (currentUser != null && authService.isUserAdmin(Constants.EMAIL_SCOPE)) {
user = new User(currentUser.getEmail());
return user;
}
...
The EMAIL_SCOPE constant is defined by
public static final String EMAIL_SCOPE = "https://www.googleapis.com/auth/userinfo.email";
In my case i implemented an authenticator, to pass user information to endpoint only if it's admin user, you can read more about the authenticators if you want.
https://cloud.google.com/appengine/docs/java/endpoints/javadoc/com/google/api/server/spi/config/Authenticator