I have a Quarkus microservice doing authentication and authorization with Keycloak using quarkus-oidc and quarkus-keycloak-authorization extensions.
I need to additionally implement following two request interceptors/filters:
A filter before any auth logic kicks in. This is to copy token from query param to header (required for web sockets). What should be the priority of this filter?
A filter to have custom authorization logic. This should be executed after all authentication and keycloak authorization logic but just before API execution? What should be priority of this filter?
I tried putting #Priority(Interceptor.Priority.PLATFORM_BEFORE) and #Prematching also to my filter, but even this is being called after OIDC kicks in.
Also, is there any way to support extending quarkus oidc logic to include custom code?
I am unable to get the priority of oidc and keycloak-auth interceptors (knowing these could help me deciding the priority of my filters). Please help.
Got the answer from quarkus google groups.
For #2, ContainerRequestFilter with any priority (should not be #Prematching) will serve the purpose.
Other option is to have custom HttpSecurityPolicy. This would be called after authentication.
package org.acme.security.keycloak.authorization;
import javax.enterprise.context.ApplicationScoped;
import org.jboss.logging.Logger;
import io.quarkus.security.identity.SecurityIdentity;
import io.smallrye.mutiny.Uni;
import io.vertx.ext.web.RoutingContext;
#ApplicationScoped
public class SecurityHandler implements io.quarkus.vertx.http.runtime.security.HttpSecurityPolicy
{
#Override
public Uni<CheckResult> checkPermission(RoutingContext request, Uni<SecurityIdentity> identity, AuthorizationRequestContext requestContext)
{
Logger.getLogger(LoggingFilter.class).infof("================ custom permission");
return Uni.createFrom().item(CheckResult.PERMIT);
}
}
For #1, we can have Vertx #RouteFilter
Sample filter:
Just note the priority. Higher the priority, first it will get called.
package org.acme.security.keycloak.authorization;
import io.quarkus.vertx.web.RouteFilter;
import io.vertx.ext.web.RoutingContext;
public class WSAuthFilter
{
#RouteFilter(1000)
void myFilter(RoutingContext rc) {
rc.request().headers().add("Authorization", rc.request().query());
rc.next();
}
}
Related
In Quarkus (resteasy reactive), is there a way to get hold of the "ResourceInfo" in an HTTP Authentication Mechanism?
What I'm trying to do is read an annotation that is defined on the resource class or method, in order to choose an authentication mechanism based on it.
Injecting the ResourceInfo directly in the mechanism class does not work (and also, it is application scoped and not request scoped, so not sure it could work). I also couldn't find the info I need in the RoutingContext parameter.
I have also tried adding a ContainerRequestFilter, in which injecting the ResourceInfo with #Context works well, but I think perhaps the filters are called after the httpAuthenticationMechanism.authenticate(), because it's not called in my test when the endpoint requires authentication.
Is there another way to do this?
----> To clarify with code what I would like to do:
have different JAX-RS resources with a custom #Authorization annotations with different "api names" like this:
#Path("/jwttest")
#ApplicationScoped
#Authorization("jwttest")
public class JWTTestController {
...
}
#Path("/oidctest")
#ApplicationScoped
#Authorization("myoidc")
public class OIDCTestController {
...
}
and then different configs like this:
myframework.auth.jwttest.type=jwt
myframework.auth.jwttest.issuer=123
myframework.auth.jwttest.audience=456
myframework.auth.myoidc.type=oidc
myframework.auth.myoidc.auth-server-url=myurl
And in the HttpAuthenticationMechanism, find the value of #Authorization, and based on it, call another provider like suggested in https://quarkus.io/guides/security-customization#dealing-with-more-than-one-httpauthenticationmechanism with the right api name so that it can load the config.
I'm looking to implement simple user authentication with my dart gRPC server + client and am struggling to find samples on how to achieve this properly.
So my problems are the following:
How do I add the user authentication data (JWT) to API calls that require authentication on the client?
How to I handle this data on the server?
I assume that on the client, metadata is the way to go, but is there a way to add the authentication data automatically for each call?
For the server, I assume that interceptors are the way to go, but how do I specify interceptors for specific services only (since not all API calls require authentication)?
is there a way to add the authentication data automatically for each call?
You can supply the default CallOptions with the options parameter in the generated client constructor.
You can use that to add authorization info to all your calls. If you need to perform async work for each call (for instance, to check if the token is still valid and optionally refresh it), you could add a MetadataProvider which gets invoked for each call.
how do I specify interceptors for specific services only (since not all API calls require authentication)?
The interceptor gets access to a ServiceMethod, which contains a name. So you could check that to only invoke an interceptor on some methods:
extension OnlyInterceptSome on Interceptor {
Interceptor limitTo(Set<String> endpoints) {
return (call, method) {
// Skip the check if we don't care about the method.
if (!endpoints.contains(method.name)) return null;
// Invoke the regular interceptor otherwise
return this(call, method);
};
}
}
Can someone tell me or give a ready-made CRUD example using WebFlux, RScoket and Spring (or SpringBoot)?
I studied the RSocket documentation, WebFlux, also wrote my simple examples, but I would like to see a real CRUD application using basic methods using RSocket.
I'll be very grateful.
Thanks.
I have maintained a Spring/RSocket sample project with 4 basic interaction modes of RSocket.
If you only require request/reply case for simple CRUD operations, check the request and response mode, and select a transport protocol, TCP or WebSocket.
To implement CRUD operations, just define 4 different routes for them, like define the RESTful APIs using URI, you have to have a good plan for the naming, but in RSocket there are no HTTP methods to help you to differentiate the same routes.
For example, in the server side, we can declare a #Controller to handling messages like this.
#Controller
class ProfileController {
#MessageMapping("fetch.profile.{name}")
public Mono<Profile> greet(#DestinationVariable String name) {
}
#MessageMapping("create.profile")
public Mono<Message> greet(#Payload CreateProfileRequest p) {
}
#MessageMapping("update.profile.{name}")
public Mono<Message> greet(#DestinationVariable String name, #Payload UpdateProfileRequest p) {
}
#MessageMapping("delete.profile.{name}")
public Mono<Message> greet(#DestinationVariable String name) {
}
}
In the client side, if it is a Spring Boot application, you can use RSocket RSocketRequester to interact with the server side like this.
//fetch a profile by name
requester.route("fetch.profile.hantsy").retrieveMono()
//create a new profile
requester.data(new CreateProfileRequest(...)).route("create.profile").retrieveMono()
//update the existing profile
requester.data(new UpdateProfileRequest(...)).route("update.profile.hantsy").retrieveMono()
//delete a profile
requester.route("delete.profile.hantsy").retrieveMono()
Of course, if you just build a service exposed by rsocket protocol, the client can be a rsocket-js project or other languages and frameworks, such as Angular, React or Android etc.
Update: I've added a crud sample in my rsocket sample codes, and I have published a post on Medium.
I would like to implement authentication for all the nodes of the cluster (client and server should provide username and password to join the cluster). I tried to use the latest version, which the documentation claims to provide authentication, but it doesn't implement it for all the nodes; it's just for the new thin java client.
First question: is my understanding correct, or am I missing something?
I also tried to implement the authentication of all the nodes using the GridSecurityProcessor interface, as part of a custom plugin (by following this guide http://smartkey.co.uk/development/securing-an-apache-ignite-cluster/ and other discussions on the public mailing list).
I got the plugin to be recognized by the server node, but I can't wire my implementation of the SecurityCredentialsProvider to Ignite; it seems Ignite doesn't use it. The question is similar to this one: http://apache-ignite-users.70518.x6.nabble.com/Custom-SecurityCredentialsProvider-and-SecurityCredentials-td16360.html.
As a consequence, when GridSecurityProcessor.authenticateNode(ClusterNode node, SecurityCredentials cred) is called, cred is null.
Second question: How to hook SecurityCredentialsProvider and SecurityCredentials to Ignite, so that it will call the authorizeNode method from my plugin, with these credentials?
I managed to implement authentication with using the plugin system recommended by the documentation. I followed these steps:
Created a class extending the TcpDiscoverySpi and implements DiscoverySpiNodeAuthenticator. In it I pass the SecurityCredentials object as a constructor parameter.
`
public CustomTcpDiscoverySpi(final SecurityCredentials securityCredentials) {
this.securityCredentials = securityCredentials;
this.setAuthenticator(this);
}
In this class, I override the setNodeAttributes from TcpDiscoverySpi to add the securityCredentials object like this:
`
#Override
public void setNodeAttributes(final Map<String, Object> attrs, final IgniteProductVersion ver) {
attrs.put(IgniteNodeAttributes.ATTR_SECURITY_CREDENTIALS, this.securityCredentials);
super.setNodeAttributes(attrs, ver);
}
I also implement the authenticateNode method where I check the SecurityCredentials object of this class with the one received as a parameter on the authenticateNode method. This method returns an implementation of SecurityContext if authentication succeeded or null if not; so you need to implement it as you like. For example to give full access with no authorization, just return true for all the ****operationAllowed methods and return null in subject() method.
In IgniteConfiguration, use this class as discoverySpi: cfg.setDiscoverySpi(customTcpDiscoverySpi);
I'm trying to integrate Spring Security in my web application. It seems pretty easy to do as long as you integrate the whole process of authentication and authorization.
However, both authentication and authorization seem so coupled that it's being very time-consuming for me to understand how I could split these processes, and get authentication independently of authorization.
The authentication process is external to our system (based on single sign-on) and this cannot be modified. Nevertheless, once the user succeeds this process, it's loaded in the session, including roles.
What we are trying to achieve is to make use of this information for the authorization process of Spring Security, that's to say, to force it to get the roles from the user session instead of picking it up through the authentication-provider.
Is there any way to achieve this?
If your authentication is already done using an SSO service, then you should use one of spring security's pre-authentication filters. Then you can specify a UserDetails service (possibly custom) that will use the pre-authenticated user principle to populate the GrantedAuthority's
SpringSecurity includes several pre-authentication filters including J2eePreAuthenticatedProcessingFilter and RequestHeaderPreAuthenticatedProcessingFilter. If you can't find one that works for you, its also possible, and not that hard to write your own, provided you know where in the request your SSO implementation stuffs the data. (That depends on the implementation of course.)
Just implement the Filter interface and do something like this in the doFilter method:
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
// principal is set in here as a header or parameter. you need to find out
// what it's named to extract it
HttpServletRequest req = (HttpServletRequest) request;
if (SecurityContextHolder.getContext().getAuthentication() == null) {
// in here, get your principal, and populate the auth object with
// the right authorities
Authentication auth = doAuthentication(req);
SecurityContextHolder.getContext().setAuthentication(auth);
}
chain.doFilter(request, response);
}
Yes, it's possible. Spring Security (like most of the rest of Spring) is interface-driven so that you can plug in your own implementations selectively for different parts of the framework.
Update: Spring's authorisation and authentication mechanisms work together - the authentication mechanism will authenticate the user and insert various GrantedAuthority instances in the security context. These will then be checked by the authorisation machinery to allow/disallow certain operations.
Use nont's answer for the details on how to use pre-existing authentication. The details of how you get the details from your session (e.g. roles ) will of course depend on your specific setup. But if you put in the GrantedAuthority instances derived from the roles pre-populated in your session by your SSO system, you will be able to use them in your authorisation logic.
From the reference documentation (slightly edited, with my emphasis):
You can (and many users do) write
their own filters or MVC controllers
to provide interoperability with
authentication systems that are not
based on Spring Security. For example,
you might be using Container Managed
Authentication which makes the current
user available from a ThreadLocal or
JNDI location. Or you might work for a
company that has a legacy proprietary
authentication system, which is a
corporate "standard" over which you
have little control. In such
situations it's quite easy to get
Spring Security to work, and still
provide authorization capabilities.
All you need to do is write a filter
(or equivalent) that reads the
third-party user information from a
location, build an Spring
Security-specific Authentication
object, and put it onto the
SecurityContextHolder. It's quite easy
to do this, and it is a
fully-supported integration approach.
The server that handles the authentication should redirect the user to the application passing to it some kind of key (a token in CAS SSO). Then the application use the key to ask to the authentication server the username and roles associated. With this info create a security context that is passed to the authorization manager. This is a very simplified version of a SSO login workflow.
Take a look to CAS SSO and CAS 2 Architecture.
Tell me if you need more information.
we have had the same requirement where we had to use spring security for authorization purpose only. We were using Siteminder for authentication. You can find more details on how to use authorization part of spring security not authentication here at http://codersatwork.wordpress.com/2010/02/13/use-spring-security-for-authorization-only-not-for-authentication/
I have also added source code and test cases at http://code.google.com/p/spring-security-with-authorization-only/source/browse/
I am trying to understand CAS authentication with our own Authorization and was getting confused since the User object in Spring Security always expects the password to be filled in and we don't care about that in our scenario. After reading Surabh's post, it seems that the trick is to return a custom User object without the password filled in. I will try that out and see if it works in my case. Hopefully no other code in the chain will be expecting the Password in the User object.
I use the authorization by this:
Inject the authorization related bean into my own bean:
#Autowired
private AccessDecisionManager accessDecisionManager;
#Autowired
FilterSecurityInterceptor filterSecurityInterceptor;
Use this bean by this:
FilterInvocation fi = new FilterInvocation(rundata.getRequest(), rundata.getResponse(), new FilterChain() {
public void doFilter(ServletRequest arg0, ServletResponse arg1) throws IOException, ServletException {
// TODO Auto-generated method stub
}
});
FilterInvocationDefinitionSource objectDefinitionSource = filterSecurityInterceptor.getObjectDefinitionSource();
ConfigAttributeDefinition attr = objectDefinitionSource.getAttributes(fi);
Authentication authenticated = new Authentication() {
...........
public GrantedAuthority[] getAuthorities() {
GrantedAuthority[] result = new GrantedAuthority[1];
result[0] = new GrantedAuthorityImpl("ROLE_USER");
return result;
}
};
accessDecisionManager.decide(authenticated, fi, attr);
I too did spent lot of hours investigating on how to implement custom authorization without authentication.
The authentication process is external to our system (based on single sign-on).
I have done it, as mentioned below and it Works!!! (I am sure there are many other ways to it better, but this way just suits my scenario well enough)
Scenario : User is already authenticated by external system and all information needed for authorization is present in the request
1.
Security config need to be created, enabling global method security as below.
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
class SpringWebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(final HttpSecurity http) throws Exception {
}
}
2.) Implement Spring PermissionEvaluator to authorize whether the request should be allowed or rejected
#Component
public class CustomPermissionEvaluator implements PermissionEvaluator {
public boolean authorize(final String groups, final String role) {
boolean allowed = false;
System.out.println("Authorizing: " + groups + "...");
if (groups.contains(role)) {
allowed = true;
System.out.println(" authorized!");
}
return allowed;
};
#Override
public boolean hasPermission(final Authentication authentication, final Object groups, final Object role) {
return authorize((String) groups, (String) role);
};
#Override
public boolean hasPermission(final Authentication authentication, final Serializable targetId, final String targetType, final Object permission) {
return authorize((String) targetId, (String) permission);
};
}
3.) Add MethodSecurityConfig
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {
#Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
DefaultMethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();
expressionHandler.setPermissionEvaluator(new CustomPermissionEvaluator());
return expressionHandler;
}
}
4.) Add #PreAuthorize in your controller as shown below. In this example, all the groups of the user are present in request header with key 'availableUserGroups'.
This is then passed on to the CustomPermissionEvaluator to verify authorization. Please note that spring automatically passes Authentication object to the method 'hasPermission'.
So in case if you want to load user and check using spring 'hasRole' method, then this can be used.
#PreAuthorize("hasPermission(#userGroups, 'ADMIN')")
#RequestMapping(value = "/getSomething")
public String getSomething(#RequestHeader(name = "availableUserGroups") final String userGroups) {
return "resource allowed to access";
}
Handling Other Scenarios :
1.) In scenario where you want to load the user before you can perform the authorization. You can use spring pre-authentication filters, and do it in a similar way.
Example link : http://www.learningthegoodstuff.com/2014/12/spring-security-pre-authentication-and.html