What is the proper way of extending Keycloak -- for example via Service Provider Interface (SPI) -- to enrich the issued JWT token with information fetched from another service but without delegating the user credential check to the other service?
You create - what Keycloak documentation refers to as - a Protocol Mapper. They are various types of them that you can find out by going to the Clients > your_client > Mappers menu and try to create one. Besides, you should see that you can choose which JWT token you want to enrich, ID token or Access token. In your case, you need to customise the mapper's logic enough to fetch info from another service. There are two types of mapper that allow that (at least as far as I know):
The Script mapper: allows you to code a custom mapper in JavaScript, so you can implement the service call and add the result to the token claims in javascript. See the example on Stackoverflow, and source code of the mapper for more info. This has some limitations, e.g. does not support multi-valued claims properly.
Implement the mapper directly in Java: full flexibiliy but more work (implement Java interface AbstractOIDCProtocolMapper). See this Custom Keycloak Protocol Mapper for group membership for instance.
Related
I'd like to add a new auth method in keycloak. To be precise - I'd like the keycloak to ask external API for some specific value. I have read about flows in keycloak but they seem to be poorly documented and I have a feeling that it is not very intuitive.
During login I would like the keycloak to send request to external API and if and only if when specific value is returned allow the user to login. For example I could override some login method and add a few lines of code doing what I want.
Which method in which class is responsible for login?
There are multiple things you need to do to achieve that. I will go over them:
Implement Authenticator and AuthenticatorFactory interfaces.
Copy an existing Authentication Flow
Bind flow
I assume you know how to write and deploy a keycloak extension.
1. Implement Authenticator and AuthenticatorFactory interfaces.
The specific interfaces are those:
org.keycloak.authentication.AuthenticatorFactory
org.keycloak.authentication.Authenticator
A sample implementation:
org.keycloak.authentication.authenticators.browser.UsernamePasswordFormFactory
org.keycloak.authentication.authenticators.browser.UsernamePasswordForm
If you want to externalize your config (So you can add username/password etc. for external api), override getConfigProperties() method in AuthenticatorFactory
2. Copy an existing Authentication Flow.
Login keycloak with admin credentials.
Create a new realm (or use if you have one)
Go to Authentication tab on left.
Copy browser login flow
Add your flows/executions (Your implementation of Authenticator/Factory will be listed under executions)
You can move them up or down. Make them required or alternative etc.
If you override config list it will be shown next to your execution
3. Bind flow.
Bind your flow in the second tab of Authentication page.
I am trying out an example to add user attributes to the claim. I am following the example here. I am trying to access the claim in a filter and am unsuccessful.
I would want to understand how the protocol mappers work behind the scenes, namely how and in which order are those claims from that protocol added into the token.
In Keycloak, the function of the protocol mappers is to add additional claims to a JWT besides those that are added by default by Keyloack.
Different Protocol Mapper will have different options, but for most of them you can chose to add the claims that those mappers will produce into the:
ID Token;
Access Token;
UserInfo;
I would want to understand how the protocol mappers work behind the
scenes.
Roughly, what will happen is the following the JWT is basically an encoded JSON Object based on a specific standard, Keycloak creates that object with the Registered claims (e.g., Issuer, Subject and so on), and then it will apply the custom claims (i.e., protocol Mappers) into that temporary object by the Priority Order that you have defined for that given protocol Mapper.
The end result (i.e., the token) will be a JWT with the default claims, and with the claims added with the protocol mappers.
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 have implemented a REST application with some complicated authorization requirements.
Here's a summary
My customers purchase a proxy device called Collector that enables their home automation control to be centralized. My customers also purchase multiple home automation devices (let's call them HADevices) that report their metrics through the collector to my REST application.
An admin(who is my customer service rep), with role ROLE_ADMIN, should be able to look at any data from any Collector or HADevice. A customer, with role ROLE_USER role, should only be able to view data about the Collector or an HADevice that s/he owns.
The Collector, with role ROLE_COLLECTOR is the only role authorized to insert data i.e. create or update a resource in my REST service. Let's call this url /deviceMetrics (POST). A Collector can insert metrics for any HADevice associated with the customer. HADevices have no role and do not interact with my REST application. A Collector can only insert records to HADevices that have the same customer as the Collector.
I am using spring security 4.0 for authentication and #Secured annotation for authorization. However, I find that my code is cluttered with repetitive permission validations which take up a majority of my logic. The basic insertions and retrievals are pretty straightforward.
I want to use a PermissionEvaluator to centralize Access Control. I have to secure the following urls
GET /collectors/{id}/deviceMetrics - I retrieve the user from the
Principal and verify that the Collector with id={id} in my spring
data repository belongs to the Principal and if not I send a 403
GET/hadevices/{id}/deviceMetrics - I retrieve the user from the
Principal and verify that the HADevice with id={id} in my spring data
repository belongs to the Principal and if not I send a 403.
POST /collectors/{id}/deviceMetrics - I retrieve the Collector uniqueId
from the Principal and make sure that the Collector's id matches the
{id} in the URL
POST /hadevice/{id}/deviceMetrics - I retrieve the
Collector uniqueId from the Principal and the associated Customer. I
also pull the Customer associated with HADevice with id={id} and
compare the two. If they are unequal, I send a 403.
My application is littered with such complex authorization requirements for each REST resource, and I want to use a custom org.springframework.security.access.PermissionEvaluator, specifically by implementing the following method.
boolean hasPermission(Authentication authentication, Serializable targetId, String targetType, Object permission)
I'm planning to use a combination of targetType and request.getUrl() to get a specialized Evaluator for each url and resource.
Is there a better way to do this?
Your question is quite broad, but I think you can get away with quite simple logic for most cases.
GET /collectors/{id}/deviceMetrics
Given that you have a DeviceMetrics class with suitable properties, you can annotate your data repository with something like:
#PostAuthorize("hasRole('ROLE_ADMIN') or (hasRole('ROLE_USER') and returnObject.collector.owner = authentication.name)")
public DeviceMetrics getDeviceMetrics(long deviceId);
(This assumes that DeviceMetrics class has a property collector which has a property owner which is the username.)
That doesn't need a PermissionEvaluator at all. Maybe you need one for more complex cases:
POST /collectors/{id}/deviceMetrics
#PreAuthorize("hasRole('ROLE_COLLECTOR') and hasPermission(#collectorId, 'com.example.Collector', 'WRITE')")
public void saveDeviceMetrics(long collectorId, DeviceMetrics deviceMetrics);
You only need one PermissionEvaluator since you get all the information you need as method arguments.
For those who are wondering what my solution looks like, I borrowed from this example.
It's old and it's based on xml configuration which I am not particularly fond of. But the idea is to create a Map and initialize the custom PermissionValidator and to store the authorization logic in the Permission interface implementations.
The biggest pain point was injecting an autowired HashMap of tuples, but that's an implementation detail that reasonably experienced spring users can figure out.
We'd like to secure our rest api using an api key. Here are the requirements:
Public-facing services require an api key.
"Private" services can only accept a call from within the cluster,
not the outside world.
Each api identifies a user, and the User object must be available to
the rest service.
Is there some standard way to do this in a JAX-RS app? (We're using Resteasy.)
I've read all about filters, interceptors and basic auth, but it isn't clear to me what's the best approach.
In an earlier version of the app we had a roll-your-own solution in which public services ran on a public port and private ones on a private port. There was a custom api key lookup that set the User object as a variable into the rest service object.
I can't figure out how to do either of these things using standard JAX-RS.
Using a filter to intercept the request
This kind of authentication could be achieved with a ContainerRequestFilter, intercepting the requests to your resource methods.
The filter will be used to extract the API key from the request and validate it. If the API key is not valid, the request will be refused. Otherwise, the request will proceed to the resource methods.
Have a look at the following piece of code. The ContainerRequestContext API can be used to extract information from the HTTP request:
#Provider
#Priority(Priorities.AUTHENTICATION)
public class AuthenticationFilter implements ContainerRequestFilter {
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
// Extract and validate the API key from the request
String apiKey = requestContext.getHeaderString("API-Key");
...
}
}
Also have a look at this answer I wrote a while ago about authentication with tokens in JAX-RS. There you will find plenty of details that can be useful to address the situation you described in your question.
Identifying the user
During the authentication process, you must be able to identify the user who is performing the request. To propagate this information to your resource classes/methods you could:
Override the SecurityContext and inject it into your resource classes/methods.
Use a CDI Event and a producer method to create an object that contains the user identifier that can be injected in your resource classes/methods.
For more details on the these approaches, refer to the answer I mentioned above.
Binding the filter to some resource classes/methods
By default, the filters are global (it means they are executed for all the resource methods of your application). To bind the filter to a subset of resource methods or classes, you could use name binding annotations.
Not giving a detailed answer, but just a suggestion. Check for CustomInvokers and register the invoker for the services. Validate the api-key and throw an error if it's not valid. If there is an error then your client gets an error. The Service code won't be called.
For the actual security framework, please check netflix zuul.