Spring Security / Keycloak: Securing the same request path with multiple realms - java

I'd like users in two different realms (eg human users and S2S users) to access the same rest endpoint. All of multi-tenancy examples I can find (eg keycloak multi-tenancy docs) suggest implementing a KeycloakConfigResolver to pick a single realm based on the request path. Eg:
public class PathBasedKeycloakConfigResolver implements KeycloakConfigResolver {
private final KeycloakDeployment realm1Deployment;
private final KeycloakDeployment realm2Deployment;
public PathBasedKeycloakConfigResolver() throws IOException {
realm1Deployment = buildDeployment("realm1.json");
realm2Deployment = buildDeployment("realm2.json");
}
#Override
public KeycloakDeployment resolve(HttpFacade.Request request) {
String path = request.getRelativePath();
return path.startsWith("clients/") ? realm1Deployment : realm2Deployment;
}
private static KeycloakDeployment buildDeployment(String path) throws IOException {
return KeycloakDeploymentBuilder.build(new ClasspathResource(path).getInputStream());
}
}
But this requires me to pick a single realm per request path.
I want different functionality, I'd like to try authenticating the request against multiple realms and pick the first the succeeds. I feel this would be the logical way to support multiple realms for a single URI but I'm open to suggestions for achieving this.

Since Keycloak provides OAuth2 functionality, you do not necessarily need to use the keycloak adapters (a lot of them are being deprecated because of this, even, see here). Instead you can just rely on the built in functionality of Spring Security.
An example of how to configure JWT Authentication for Spring Security with multiple issuers looks like this:
JwtIssuerAuthenticationManagerResolver authenticationManagerResolver = new JwtIssuerAuthenticationManagerResolver
("https://idp.example.org/issuerOne", "https://idp.example.org/issuerTwo");
http
.authorizeHttpRequests(authorize -> authorize
.anyRequest().authenticated()
)
.oauth2ResourceServer(oauth2 -> oauth2
.authenticationManagerResolver(authenticationManagerResolver)
);
The separate issuer URLs in your case would be the issuer URLs of your respective realms. This example is taken directly from the Spring Security documentation, it also contains samples on how to achieve the same with XML configuration, should you prefer to use that.
Of course, migrating away from the adapter, if you're already using it might not be easy, but since the adapter is going away in the long term anyways, it might be worth evaluating doing so as early as possible

The Keycloak adapters deprecation is announced there.
You should have a look at this OpenID adapter I wrote. It works out of the box with as many issuers as you need and solves quite a few of keycloak spring-boot adapter limitations:
compatible with webmvc (servlets) and webflux (reactive) apps
spring boot 3 ready (does not extend WebSecurityConfigurerAdapter)
no adherence to Keycloak (works with any OpenID authorization-server)
tooling for security unit testing
Basic tutorial here.

Related

Provide security in Spring

I am developing a basic crud web app in react and spring.
I am Testing with postman, as frontend is not ready yet.
I have this method, but i just discovered that anybody knows the id can send a HTTP request and get all data.
#PostMapping("/utente")
public ResponseEntity<Object> getDatiProfiloUtente(#RequestBody final Long idUtente){
HashMap<String, Object> map = new HashMap<>();
Paziente paziente = service.findPazienteById(idUtente);
map.put("nome", paziente.getNome());
map.put("cognome", paziente.getCognome());
map.put("email", paziente.getEmail());
map.put("nTelefono", paziente.getNumeroTelefono());
map.put("emailCaregiver", paziente.getEmailCaregiver());
map.put("nomeCaregiver", paziente.getNomeCaregiver());
map.put("cognomeCaregiver", paziente.getCognomeCaregiver());
return new ResponseEntity<>(map, HttpStatus.OK);
}
How can I provide security? I want that only the logged user can see his data.
You want to use the #Secured annotation provided by spring security, this article by baeldung is a great resource and explains exactly how to set up the method security you need.
You must use #EnableWebSecurity annotation. Spring boot provides great support for security. Spring can integrate with various third-party security applications as well as provide simple in-memory security.
Here in the original documentation there is a security implementation for a simple memory. I highly recommend you review it.
https://docs.spring.io/spring-security/site/docs/4.0.x/apidocs/org/springframework/security/config/annotation/web/configuration/EnableWebSecurity.html
However, in-memory management is often not useful. Instead, you may want to keep your user information in a database or in a different application.
Other than that, the best practice for Rest is to use jwt tokens. You might want to take a look at this example of how you can use it with Spring Boot.
https://www.javainuse.com/spring/boot-jwt

What's the Spring Security 5.2 / WebClient way of using username & password to connect to another service?

We currently have several Spring Boot applications connecting to other services using a service account. Till now we used the AccessTokenRequest of the OAuth2ClientContext on a RestTemplate to put the user and password of the service account in and used the returned OAuth token to connect to the other services.
Right now we're building a new application using Spring Boot 5.2 and as the new Spring Security OAuth should be used now the separate OAuth library has become deprecated, we would also like to replace the RestTemplate solution with a WebClient one as the RestTemplate will become deprecated in the near future as well. I've tried several approaches of retrieving the token, but couldn't find a solution that works.
I found set-ups like the one mentioned on Spring Security 5 Replacement for OAuth2RestTemplate, but no way of putting a username and password inside the WebClient.
I found other approaches using a ClientRegistrationRepository instead of a ReactiveClientRegistrationRepository and some of those approaches actually have options (like How to re-initialize password grant in Spring security 5.2 OAuth) of putting a username and password in an AuthorizedClientManager that gets to be a parameter when instantiating the filter, but somehow I always end up with the message that no Bean of the ClientRegistrationRepository could be found, no matter what properties I put in the application.yaml (maybe this doesn't work because the application is an MVC application instead of a WebFlux one?)
I know that I need to set the authorization-grant-type to be 'password', but there's already someone else asking how to get that working (Spring Security 5.2.1 + spring-security-oauth2 + WebClient: how to use password grant-type) and no answers to that question, yet.
So ... did they 'deprecate' this easy way of using a username and password to retrieve a token and use that token to connect to another service in Spring Security 5.2? And if yes, what should be used now?
Yes, RestTemplate and OAuthRestTemplate are all deprecated. WebClient supports OAuth out of the box. You don't need to do anything special or add any headers yourself. It's actually extremely trivial.
Create a configuration class that exposes a WebClient bean, make sure you take the client repository as a param. In the method, you pass the repo into a filter function:
#Bean
public WebClient webClient(ReactiveClientRegistrationRepository clientRegistrations) {
return WebClient.builder().filter(filterFunction(clientRegistrations))
.baseUrl(String.format("http://%s:8080", getHostName()))
.build();
}
private ServerOAuth2AuthorizedClientExchangeFilterFunction filterFunction(ReactiveClientRegistrationRepository clientRegistrations) {
ServerOAuth2AuthorizedClientExchangeFilterFunction filterFunction = new ServerOAuth2AuthorizedClientExchangeFilterFunction(clientRegistrations, new UnAuthenticatedServerOAuth2AuthorizedClientRepository());
filterFunction.setDefaultClientRegistrationId("myKey");
return filterFunction;
}
NOTE: In the filter function, replace the "myKey" with something that matches the following property structure in application.properties (replace myKey in the property paths with your name):
spring.security.oauth2.client.registration.myKey.authorization-grant-type=password
spring.security.oauth2.client.registration.myKey.client-id=xxx
spring.security.oauth2.client.registration.myKey.client-secret=xxx
spring.security.oauth2.client.provider.myKey.token-uri=http://localhost:8080/oauth/token
Aaaannddd.... you're done! OAuth token refresh is also built in.
Well, it turned out to be a little bit different. A comment on the last SO question I linked requested the author to use debugging in the PasswordOAuth2AuthorizedClientProvider to see what went on / wrong. So I started debugging as well and with the setup you provided there are 4 Providers for 4 login types that are supplied, but out of the 4 it's not the PasswordReactiveOAuth2AuthorizedProvider that's used, and also not the ClientCredentialsReactiveOAuth2AuthorizedProvider or RefreshTokenReactiveOAuth2AuthorizedProvider one, but the AuthorizationCodeReactiveOAuth2AuthorizedProvider is called and that's quite weird when authorization-grant-type is set to password. That seems like a bug to me ...
Anyway, I found another SO question from rigon with a problem a bit different than mine, but close enough: Create route in Spring Cloud Gateway with OAuth2 Resource Owner Password grant type and he provided code I could get working as the Provider used with that codebase is in fact the PasswordReactiveOAuth2AuthorizedClientProvider
In the end I only needed to enter 3 items in the yaml-file: the client-id (with the client_id that used to be put as attribute on the AccessTokenRequest), the authorization-grant-type and the token-uri
Besides that I copied the WebClient and ReactiveOAuth2AuthorizedClientManager setup from the code provided by rigon and put the username and password from the configuration file into the settings (I left the seperate contextAttributesMapper out as I only needed to provide 2 context parameters directly into a map).

Securing REST services in Jersey

I am very much new to web services. I have exposed some REST services using Jersey 2 in integration with Spring. Now I need to secure those rest services using authentication with username/password. I am told not to use Spring Security.
I have no idea of how to do this. I did search on the net but various links show various implementation and I am unable to decide how to proceed with it.
A common way for authenticating with username and password is to use Basic Authentication. Basically the client needs to send a request header Authorization, with the the header value as Basic Base64Encoded(username:password). So is my username is peeskillet and my password is pass, I, as a client, should set the header as
Authorization: Basic cGVlc2tpbGxldDpwYXNz
In a servlet environment, the container should have support for Basic authentication. You would configure this support on the web.xml. You can see an example in 48.2 Securing Web Applications of the Java EE tutorial. You will also notice in an example
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
That is for SSL support. This is recommended for Basic Authentication.
If you don't want to deal with the hassle of working with security domains and login modules, realm, and such, that would be required to customize the servlet support, or if you're just not in a servlet environment, implementing Basic Auth in a ContainerRequestFilter is really not too difficult.
You can see a complete example of how this could be done at jersey/examples/https-clientserver-grizzly. You should focus on the SecurityFilter
The basic flow in the filter goes something like this
Get the Authorization header. If it doesn't exist, throw an AuthenticationException. In which case the AuthenticationExceptionMapper will send out the header "WWW-Authenticate", "Basic realm=\"" + e.getRealm() + "\", which is part of the Basic Auth protocol
Once we have the header, we parse it just to get the Base64 encoded username:password. Then we decode it, then split it, then separate the user name and password. If any of this process fails, again throw the WebApplicationException that maps to a 400 Bad Request.
Check the username and password. The example source code just checks if the username is user and the password is password, but you will want to use some service in the filter to verify this information. If either of these fail, throw an AuthenticationException
If all goes well, a User is created from the authenticate method, and is injected into an Authorizer (which is a SecurityContext). In JAX-RS, the SecurityContext is normally used for authorization`.
For the authorization, if you want to secure certain areas for certain resources, you can use the #RolesAllowed annotation for your classes or methods. Jersey has support for this annotation, by registering the RolesAllowedDynamicFeature.
What happens under the hood is that the SecurityContext will be obtained from the request. With the example I linked to, you can see the Authorizer, it has an overridden method isUserInRole. This method will be called to check against the value(s) in #RolesAllowed({"ADMIN"}). So when you create the SecurityContext, you should make sure to include on the overridden method, the roles of the user.
For testing, you can simply use a browser. If everything is set up correctly, when you try and access the resource, you should see (in Firefox) a dialog as seen in this post. If you use cURL, you could do
C:/>curl -v -u username:password http://localhost:8080/blah/resource
This will send out a Basic Authenticated request. Because of the -v switch, you should see all the headers involved. If you just want to test with the client API, you can see here how to set it up. In any of the three cases mentioned, the Base64 encoding will be done for you, so you don't have to worry about it.
As for the SSL, you should look into the documentation of your container for information about how to set it up.
So this is really a matter what you would like to achieve. My case was to get this thing running with mobile and a One-Page-App JavaScript.
Basically all you need to do is generate some kind of header that value that will be needed in every consecutive request you client will make.
So you do a endpoint in which you wait for a post with user/password:
#Path("/login")
public class AuthenticationResource {
#POST
#Consumes("application/json")
public Response authenticate(Credentials credential) {
boolean canBeLoggedIn = (...check in your DB or anywher you need to)
if (canBeLoggedIn) {
UUID uuid = UUID.randomUUID();
Token token = new Token();
token.setToken(uuid.toString());
//save your token with associated with user
(...)
return Response.ok(token).type(MediaType.APPLICATION_JSON_TYPE).build();
} else {
return Response.status(Response.Status.UNAUTHORIZED).build();
}
}
}
Now you need to secure resource with need for that token:
#Path("/payment")
#AuthorizedWithToken
public class Payments {
#GET
#Produces("application/json")
public Response sync() {
(...)
}
}
Notice the #AuthorizedWithToken annotation. This annotaation you can create on your own using special meta annotation #NameBinding
#NameBinding
#Target({ElementType.METHOD, ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
public #interface AuthorizedWithToken {}
And now for the filter that implements checking of the header:
#AuthorizedWithToken
#Provider
public class XAuthTokenFilter implements ContainerRequestFilter {
private static String X_Auth_Token = "X-Auth-Token";
#Override
public void filter(ContainerRequestContext crc) throws IOException {
String headerValue = crc.getHeaderString(X_Auth_Token);
if (headerValue == null) {
crc.abortWith(Response.status(Response.Status.FORBIDDEN).entity("Missing " + X_Auth_Token + " value").build());
return;
}
if(! TOKEN_FOUND_IN_DB) {
crc.abortWith(Response.status(Response.Status.UNAUTHORIZED).entity("Wrong " + X_Auth_Token + " value").build());
return;
}
}
}
You can create any number of your own annotations checking for various things in the http request and mix them. However you need to pay attention to Priorities but that actually easy thing to find. This method needs using https but that is obvious.
Security comes in two main flavours :
Container Based
application based
the standard way to secure spring applications is to use Spring Security (formerly Acegi).
It would be interesting to know why you're not being allowed to use that.
You could use container based security, but I'm guessing that your use of spring precludes that option too.
Since the choice of Spring is usually to obviate the need for the use of a full J2EE container (Edit : though as pointed out below by others, most ordinary servlet containers do allow you to implement various container based security methods)
This really only leaves you with one option which is to roll your own security.
Your use of Jersey suggests that this might be a REST application.
In which case you really ought to stick with standard HTTP Authentication methods that
comes in the following flavours in reverse order of strength :
BASIC
Digest
Form
Certificate
REST applications are usually supposed to be 'stateless', which essentially rules out form based authentication (because you'd require the use of Session)
leaving you with BASIC, Digest and Certificate.
Your next question is, who am I authenticating. If you can expect to know the username AND the password of the user based on what URL they requested (say if it's one set of credentials for all users) then Digest is the best bet since the password is never sent, only a hash.
If you cannot know the Password (because you ask a third party system to validate it etc.) then you are stuck with BASIC.
But you can enhance the security of BASIC by using SSL, or better yet, combining BASIC with client certificate authentication.
In fact BASIC authentication over HTTPS is the standard technique for securing most REST applications.
You can easily implement a Servlet Filter that looks for the Authentication Header and validates the credentials yourself.
There are many examples of such filters, it's a single self contained class file.
If no credentials are found the filter returns 401 passing a prompt for basic auth in the response headers.
If the credentials are invalid you return 403.
App security is almost an entire career in itself, but I hope this helps.
As the former posts say, you could go with different options, with a varying overhead for implementation. From a practical view, if you're going to start with this and are looking for a comfortable way for a simple implementation, I'd recommend container-based option using BASIC authentication.
If you use tomcat, you can setup a realm, which is relatively simple to implement. You could use JDBCRealm, which gets you a user and password from specified columns in your database, and configure it via server.xml and web.xml.
This will prompt you for credentials automatically, everytime you are trying to access your application. You don't have any application-side implementation to do for that.
What I can tell you now is that you already did most of the 'dirty' job integrating Jersey with Spring. I recommend to you to go an Application-based solution, is it does not tie you to a particular container. Spring Security can be intimidating at first, but then when you tame the beast, you see it was actually a friendly puppy.
The fact is that Spring Security is hugely customizable, just by implementing their interfaces. And there is a lot of documentation and support. Plus, you already have a Spring based application.
As all you seek is guidance, I can provide you with some tutorials. You can take advantage from this blog.
http://www.baeldung.com/rest-with-spring-series/
http://www.baeldung.com/2011/10/31/securing-a-restful-web-service-with-spring-security-3-1-part-3/

User authorization in spring security using OAuth2

I'm currently investigating OAuth2 with spring-security and spring boot. Although the concept of this protocol is quite clear to me the implementation details are not (spring does not provide many examples and tutorials to work with). Because of that I would be very grateful for answering my questions below:
What is purpose of "password" grant type? OAuth2 specs doesn't mention about it. Is it just Spring implementation of some kind "authorization shortcut"?
How does resource server handle access tokens (check if it is still valid, scope etc.)? Does I need to implement something or it is provided by Spring Security and ResourceServerConfig?
How RS will know which user (not client) has requested resources? Specific user resources are bound with ids, unique usernames and so on, does token have this kind of information? How to I retrieve it in order to use in resources controllers?
Authorization server (correct me if I'm wrong) is able to check if Client application has permission to requested resources or not. As I understand it shouldn't be aware of Users (Resources Owners). In situation where I authenticate via Google, Facebook or some other service user authentication is their case. What about application where OAuth2 and Resources Server are one application (I'm currently developing such one for test purposes), where I should put UserDetailsService? And how combine it in order to authorize client (js generated from app in this case) at first, then user (Resource owner) and generate token? Currenly I have implemented ClientDetailService and injected to configuration. Where should I inject UserDetailService?
#Configuration
#EnableAuthorizationServer
public class OAuth2Config extends AuthorizationServerConfigurerAdapter {
#Autowired
private AuthenticationManager authenticationManager;
#Autowired
private MongoClientDetailsService cds;
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager);
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.withClientDetails(cds);
}
}
Are there some JS libraries that helps with authorization flow for custom applications like hello.js which supports (as far I as know) well known services?
Any help would be appreciated:)
That's a lot of questions; let me start by answering 3:
the grant_type with the value password is the official reserved value for the Resource Owner Password Credentials grant type as shown in the spec here: https://www.rfc-editor.org/rfc/rfc6749#section-4.3.2 so it is not Spring specific or custom
the RS can validate the token itself if it is self-contained and structured (e.g. a JWT) or else it will need to make a callback to the Authorization Server to validate it
the validation result from step 2. may include information about the user (or: Resource Owner) who granted access to the client

Create an API within existing web application or separate?

I've read through a few questions related to this topic ( Jersey REST API as a separate web app, Should web service be separate from web site?), but I still struggle to understand which design practice would work best for an existing application.
I inherited a java web application built on spring and hibernate JPA. It's currently in production with new features being developed.
Concurrently, I will need to design a REST API that will have some form of authentication/user tracking. The web application has its own user authentication system that is likely going to be different than the API implementation (i.e. username/pw vs api keys (?)).
Given this situation, I think it'd be best to develop them separately, but I feel like in the beginning, there will be a lot of code duplication (as a result of all the jpa implementation in the web application). Ideally, once I have the rest api in place, I would switch the web application to use the API as well and remove most of the backend code that currently handles the data retrieval.
Before I start down this route though, I was wondering, is there a better way to do this?
you can use Spring Security and create a custom AuthenticationProvider . You can use a JNDI lookup to get the authservice directly from the Container it self.
Example:
#Component
public class CustomAuthenticationProvider implements AuthenticationProvider {
#Autowired
private AuthService authService;
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
String name = authentication.getName();
String password = authentication.getCredentials().toString();
// use the credentials to try to authenticate against the third party system
if (authService(name, password)) {
List<GrantedAuthority> grantedAuths = new ArrayList<>();
return new UsernamePasswordAuthenticationToken(name, password, grantedAuths);
} else {
throw new AuthenticationException("Unable to auth against third party systems");
}
}
#Override
public boolean supports(Class<?> authentication) {
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
Also, read more about RESTful apis using Spring here:
http://www.baeldung.com/rest-with-spring-series/

Categories

Resources