Is it possible to define multiple authenticationProviders in Micronaut?
Let's say I have an entity A which can be logged using authenticationProviderA: which given a user and pass checks the DB table of A.
Is it possible to add an entity B and its authenticationProviderB which given a user and pass will check the DB table of B?
If so, how do you define in your controller which authenticationProvider you want to use?
After taking a look at io.micronaut.security.authentication.Authenticator I've seen it's possible to have multiple authenticationProviders in Micronaut.
The documentation says:
An Authenticator operates on several {#link AuthenticationProvider} instances returning the first authenticated {#link AuthenticationResponse}.
From what I've seen you just have to implement AuthenticationProvider and the Authenticator will include the implementations (even if it isn't annotated!) in an internal list of AuthenticationProviders.
IMHO this isn't a good way to provide multiple ways to authenticate. In the example provided in the question, the authentication for A and B both require calls to DB which means depending on the order of the execution of the AuthenticationProviders unneeded BD calls will be executed.
I think would be better to provide a way to indicate which AuthenticationProviders has to be used by controller or endpoint.
Maybe there is a way to do that and I just don't know, so feel free to comment if so.
There is no built-in solution regarding the problem, but there are multiple ways to achieve what you want with small amount of code.
Solution №1:
Creating custom AuthenticationRequest.class and LoginController.class if you need multiple login end points:
public class AuthenticationRequestForEntityA extends UsernamePasswordCredentials { ... }
public class AuthenticationRequestForEntityB extends UsernamePasswordCredentials { ... }
in your custom LoginController replace the default UsernamePasswordCredentials with your specific AuthenticationRequestForEntityA or AuthenticationRequestForEntityB and copy paste the rest of the code from the original LoginController.class:
#Consumes({MediaType.APPLICATION_FORM_URLENCODED, MediaType.APPLICATION_JSON})
#Post
public Single<MutableHttpResponse<?>> login(#Valid #Body AuthenticationRequestForEntityA usernamePasswordCredentials, HttpRequest<?> request) {
Flowable<AuthenticationResponse> authenticationResponseFlowable = Flowable.fromPublisher(authenticator.authenticate(request, usernamePasswordCredentials));
...
and then in your authentication providers:
public class AuthenticationProviderA implements AuthenticationProvider {
#Override
public Publisher<AuthenticationResponse> authenticate(#Nullable HttpRequest<?> httpRequest, AuthenticationRequest<?, ?> authenticationRequest) {
if (authenticationRequest instanceof AuthenticationRequestForEntityA) {
return authenticate(authenticationRequest);
} else {
// return empty
}
}
}
public class AuthenticationProviderB implements AuthenticationProvider {
#Override
public Publisher<AuthenticationResponse> authenticate(#Nullable HttpRequest<?> httpRequest, AuthenticationRequest<?, ?> authenticationRequest) {
if (authenticationRequest instanceof AuthenticationRequestForEntityB) {
return authenticate(authenticationRequest);
} else {
// return empty
}
}
}
Solution №2: Creating your custom route-based AuthenticationProvider
Since HttpRequest is available in the AuthenticationProvider as an input parameter you can simply authenticate based on the httpRequest path or query parameter properties.
To make the code cleaner you can create your own RouteBasedAuthenticationProvider interface:
public interface RequestBasedAuthenticationProvider extends AuthenticationProvider {
/**
You can check the request path or request parameter or whatever
*/
boolean supports(HttpRequest<?> request);
}
then in Micronaut AuthenticationProvider:
#Context
public class AppAuthenticationProvider implements AuthenticationProvider {
private final Collection<RequestBasedAuthenticationProvider> providers;
constructor(...) {...}
#Override
public Publisher<AuthenticationResponse> authenticate(#Nullable HttpRequest<?> httpRequest, AuthenticationRequest<?, ?> authenticationRequest) {
return providers.stream()
.filter(provider -> provider.supports(httpRequest))
.findFirst()
.orElseThrow(//Throw provider not found error)
.authenticate(httpRequest, authenticationRequest);
}
}
Related
This seems to be a hot topic based on the amount of questions asked but I have not found the answer I am looking for just yet. I want to implement a simple authorization service in my Quarkus app, but I seem to be repeating code over and over again.
Basically, I take in the JWT from the Authorization Http header and check if the role supplied in it is sufficient to access my endpoint:
public void someApiCall(#Context HttpHeaders headers) {
authService.validate(ApiToken.SOME_API_CALL, headers); // Throws an exception when unauthorized
//…
}
Now, I think this looks really clunky and I do not like the additional parameter that I need for every single Http endpoint. I have done some research into AOP and know how to add an interceptor which could validate the Http headers through an annotation which would be applied to my method:
#Authorize
public void someApiCall(/*…*/) { /*…*/ }
The issue is, I do not know how to pass in arguments into this annotation to specify the required role. I want something like this:
#Authorize(UserRole.SYSADMIN)
This seems pretty simple but I cannot figure it out. Below you will find the interceptor and annotation classes (Missing the required role of course):
Authorize.java
#Retention(value=RUNTIME)
#Target(value=METHOD)
public #interface Authorize {}
AuthorizeInterceptor.java
#Interceptor
#Priority(3000)
#Authorize
public class AuthorizeInterceptor {
#Inject
AuthorizationService authService;
#AroundInvoke
public void validateRole(InvokationContext ctx) {
authService.validate(ApiToken.ALL, ((RestEndpoint)ctx.getTarget()).getHttpHeaders());
}
}
RestEndpoint.java
public class RestEndpoint {
#Context
HttpHeaders headers;
public HttpHeaders getHttpHeaders() { return headers; }
}
SomeResource.java
public class SomeResource extends RestEndpoint {
#GET
#Authorize
public Object someApiCall() {
/* do code directly */
}
}
So, in conclusion, where I write #Authorize, I want to have #Authorize(UserRole.SOME_ROLE).
Thanks in advance!
So, I managed to figure it out. It turns out that it isn't that hard, I just didn't know where to look.
Here are the modified classes:
Authorize.java
#InterceptorBinding
#Retention(RUNTIME)
#Target({TYPE, METHOD})
public #interface Authorize {
// Nonbinding is very important. It makes the interceptor fire regardless of the value
#Nonbinding ApiToken value();
}
AuthorizeInterceptor.java
#Interceptor
#Priority(3000)
#Authorize(ApiToken.NULL)
public class AuthorizeInterceptor {
/* fields */
public Object validate(InvokationContext ctx) throws Exception {
authService.validate(/* stays the same */);
return ctx.proceed();
}
}
SomeResource.java
public class SomeResource {
#GET
#Authorize(ApiToken.SOME_API_CALL)
public Object someApiCall() { /* implementation */ }
}
As Turing85 pointed out, a similar API already exists in JavaEE which implements the authorization functionality in the same way.
I would like to get the username of the user in every request to add them to log file.
This is my solution:
First, I created a LoggedUser with a static property:
public class LoggedUser {
private static final ThreadLocal<String> userHolder =
new ThreadLocal<>();
public static void logIn(String user) {
userHolder.set(user);
}
public static void logOut() {
userHolder.remove();
}
public static String get() {
return userHolder.get();
}
}
Then I created a support class to get username:
public interface AuthenticationFacade {
Authentication getAuthentication();
}
#Component
public class AuthenticationFacadeImpl implements AuthenticationFacade {
#Override
public Authentication getAuthentication() {
return SecurityContextHolder.getContext().getAuthentication();
}
}
Finally, I used them in my Controllers:
#RestController
public class ResourceController {
Logger logger = LoggerFactory.getLogger(ResourceController.class);
#Autowired
private GenericService userService;
#Autowired
private AuthenticationFacade authenticationFacade;
#RequestMapping(value ="/cities")
public List<RandomCity> getCitiesAndLogWhoIsRequesting(){
loggedUser.logIn(authenticationFacade.getAuthentication().getName());
logger.info(LoggedUser.get()); //Log username
return userService.findAllRandomCities();
}
}
The problem is I don't want to have AuthenticationFacade in every #Controller, If I have 10000 controllers, for example, it will be a lot of works.
Do you have any better solution for it?
The solution is called Fish Tagging. Every decent logging framework has this functionality. Some frameworks call it MDC(Mapped Diagnostic Context). You can read about it here and here.
The basic idea is to use ThreadLocal or InheritableThreadLocal to hold a few key-value pairs in a thread to track a request. Using logging configuration, you can configure how to print it in the log entries.
Basically, you can write a filter, where you would retrieve the username from the security context and put it into the MDC and just forget about it. In your controller you log only the business logic related stuff. The username will be printed in the log entries along with timestamp, log level etc. (as per your log configuration).
With Jhovanni's suggestion, I created an AOP annotation like this:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface LogUsername {
}
In the same package, I added new #Aop #Component class with AuthenticationFacade injection:
#Aspect
#Component
public class LogUsernameAop {
Logger logger = LoggerFactory.getLogger(LogUsernameAop.class);
#Autowired
private AuthenticationFacade authenticationFacade;
#Before("#annotation(LogUsername)")
public void logUsername() throws Throwable {
logger.info(authenticationFacade.getAuthentication().getName());
LoggedUser.logIn(authenticationFacade.getAuthentication().getName());
}
}
Then, in every #GetMapping method, If I need to log the username, I can add an annotation before the method:
#PostMapping
#LogUsername
public Course createCourse(#RequestBody Course course){
return courseService.saveCourse(course);
}
Finally, this is the result:
2018-10-21 08:29:07.206 INFO 8708 --- [nio-8080-exec-2] com.khoa.aop.LogUsername : john.doe
Well, you are already accesing authentication object directly from SecurityContextHolder, you can do it in your controller.
#RequestMapping(value ="/cities")
public List<RandomCity> getCitiesAndLogWhoIsRequesting(){
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if(authentication != null){
//log user name
logger.info(authentication.get());
}
return userService.findAllRandomCities();
}
If you do not want to put all this in every endpoint, an utility method can be created to extract authentication and return its name if found.
public class UserUtil {
public static String userName(){
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
return authentication == null ? null : authentication.getName();
}
}
and call it in your endpoint like
#RequestMapping(value ="/cities")
public List<RandomCity> getCitiesAndLogWhoIsRequesting(){
//log user name
logger.info(UserUtil.username());
return userService.findAllRandomCities();
}
However, you are still adding lines of code in every endpoint, and after a few of them it starts to feel wrong being forced to do it. Something I suggest you to do is try aspect oriented programming for this kind of stuff. It will require you to invest some time in learning how it works, create annotations or executions required. But you should have it in a day or two.
With aspect oriented your endpoint could end like this
#RequestMapping(value ="/cities")
#LogUserName
public List<RandomCity> getCitiesAndLogWhoIsRequesting(){
//LogUserName annotation will inform this request should log user name if found
return userService.findAllRandomCities();
}
of course, you are able to remove #LogUserName custom annotation and configure the new aspect with being triggered by methods inside a package, or classes extending #Controller, etc.
Definitely it is worth the time, because you can use aspect for more than just logging user name.
You can obtain the username via request or parameter in your controller method. If you add Principal principal as a parameter, Spring Ioc Container will inject the information regarding the user or it will be null for anonymous users.
#RequestMapping(value ="/cities")
public List<RandomCity> getCitiesAndLogWhoIsRequesting(Principal principal){
if(principal == null){
// anonymous user
}
}
There are various ways in Spring Security to fetch the user details from the security context. But according to your requirement, you are only interested in username, so you can try this:
#RequestMapping(value ="/cities")
public List<RandomCity> getCitiesAndLogWhoIsRequesting(Authentication authentication){
logger.info(authentication.getName()); //Log username
return userService.findAllRandomCities();
}
Hope this helps!
I want to perform authentication in a filter before my resource method is called. Within this filter I would also like to retrieve the permissions of a user and pass it on through a RequestScoped #Inject annotation.
#Authenticated
public class AuthenticationFilter implements ContainerRequestFilter {
#NameBinding
#Retention(RetentionPolicy.RUNTIME)
public #interface Authenticated {};
#Inject
private ISecurityHandler handler;
public AuthenticationFilter() {}
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
// Filter out unauthorized
// Retrieve user permissions
this.handler.setUserPermissions(...);
}
}
Resource:
#Path("my-path")
public class GetVisitorsDataResource {
#Inject private ISecurityHandler handler;
#GET
#Path("resource-method")
#Authenticated
#Produces(MediaType.APPLICATION_JSON)
public Response resource() {
System.out.println(handler.getUserPermissions());
return Response.ok().build();
}
}
I have registered the filter and a Factory for the injection.
public static class SecurityHandlerProvider implements Factory<ISecurityHandler> {
#Override
public ISecurityHandler provide() {
System.out.println("PROVIDING SECURITY CONTEXT!");
return new SecurityHandlerImpl();
}
#Override
public void dispose(ISecurityHandler instance) {
System.out.println("DISPOSING SECURITY CONTEXT!");
}
}
I have also bound it.
bindFactory(SecurityHandlerProvider.class).to(ISecurityHandler.class).in(RequestScoped.class);
It is important that the object is created when a request is received and only accessible within that request. When the request is finished, the dispose method should be called. The only way I can achieve something similar is through the #Singleton annotation. However, the object is not destroyed after the request is completed and is shared across all requests.
I have been investing too much time into this issue already, is there perhaps anybody that knows how to achieve the preferred result?
Your code doesn't really make much sense. One place you are injecting ISecurityHandler, and another place SecurityHandler, but the factory is for ISecurityContext. I will just assume those are typos or copy and paste errors.
Aside from that I'll assume that really all is ok, since you you said it works as a singleton. So I'm guessing you are facing the "Not inside a request scope" error. The easiest fix for that is to just inject using javax.inject.Provider, which allows us to lazily retrieve the object. When the object is retrieve, it will be withing a request scope.
#Inject
private javax.inject.Provider<ISecurityContext> securityContextProvider;
#Override
public void filter(ContainerRequestContext context) throws IOException {
ISecurityContext sc = securityContextProvider.get();
}
...
bindFactory(SecurityHandlerProvider.class)
.to(ISecurityContext.class)
.in(RequestScoped.class);
NB, you should also make sure to annotate you AuthenticationFilter with #Priority(Priorities.AUTHENTICATION) so that it occurs before any other filter even you my prefer it to be a #PreMatching filter. The earlier into the system the authentication happens, the better, I'd say.
As an aside, you may want to look into Jersey's RolesAllowedDynamicFeature. It allows you to use the jsr250 annotations #RolesAllowed, #DenyAll, and #PermitAll for your resource classes and methods.
It is basically a filter that occurs after your Priorites.AUTHENTICATION filter, and it looks up the javax.ws.rs.core.SecurityContext from the ContainerRequestContext to look up roles. You just need to create the SecurityContext inside your authentication filter, so the next filter can look it up.
You can see an example here. You can check the user permission in the isUserInRole. When the set the SecurityContext, Jersey's filter will be called afterwards, and it calls your isUserInRole. Doing it this way, you get access control for free.
I am currently implementing Spring Security in my application. I did manage to put #Secured annotation on my service that getAllUsers() from the database, and it is working fine as long as the user is identified (depending on his rights, he can get or not the list of users).
But I have a #Scheduled method in charge of indexing all users, and when it is launched it call the same protected getAllUsers() method, and obviously crashes as it is not logged in : I get the following exception :
org.springframework.security.authentication.AuthenticationCredentialsNotFoundException: An Authentication object was not found in the SecurityContext
I'm currently thinking of one possible solution, which would be to mark the internal methods with a custom annotation, which would be retrieved by a custom AccessDecisionVoter allowing the caller to call the protected method.
I'm looking for best practice for this kind of usecase
Because method is #Secured and spring expect security authentication object in context. Here is working example of AccessDecisionVoter Spring-security - AccessDecisionVoter-impl wont be invoked
or if u will have filters or smth which will depends on user context values this one should be ok
#Scheduled
public void method() {
try {
ScheduledAuthenticationUtil.configureAuthentication();
// do work
}
catch(Exception e) {
e.printStackTrace();
}
finally {
ScheduledAuthenticationUtil.cleanAuthentication();
}
}
private static class ScheduledAuthenticationUtil {
public static void configureAuthentication() {
// inject auth obj into SecurityContextHolder
}
public static void cleanAuthentication() {
// SecurityContextHolder clean authentication
}
}
I assume your service class looks like :
public class MyServiceImpl implements MyService {
...
#Secured
public Xxx getAllUsers() {
...
// call DAO
...
return xxx;
}
...
}
And you call myService.getAllUsers() from the #Scheduledclass.
The simplest way is to split getAllUsers and make the service class inherit from 2 interfaces, one containing the secured method, and one that would contain a publically accessible version :
public class MyServiceImpl implements MyService, MyScheduledService {
...
#Secured
public Xxx getAllUsers() {
return restrictedGetAllUsers;
}
public Xxx restrictedGetAllUsers() {
...
// call DAO
...
return xxx;
}
...
}
public interface MyService {
Xxx getAllUsers();
}
public interface MyScheduledService extends MyService {
Xxx restrictedGetAllUsers();
}
Then in your controller class :
#Autowired MyService myService => will call only getAllUsers()
and in your #Scheduled class :
#Autowired MyScheduledService myService => will call restrictedGetAllUsers()
All that may seem overcomplicated, but as your scheduled class and you controller have no reason to call the service methods the same way, it make sense to present them two different interfaces with different security requirements.
I went with kxyz answer, improved with a service that run a piece of code by setting the wanted Authorities before running the code, and putting back the previous authorities when the code is done :
public void runAs(Runnable runnable, GrantedAuthority... authorities) {
Authentication previousAuthentication = SecurityContextHolder.getContext().getAuthentication();
configureAuthentication(authorities);
try {
runnable.run();
} finally {
configureAuthentication(previousAuthentication);
}
}
protected void configureAuthentication(GrantedAuthority... authorities) {
Authentication authentication = new UsernamePasswordAuthenticationToken("system", null, Arrays.asList(authorities));
configureAuthentication(authentication);
}
protected void configureAuthentication(Authentication authentication) {
SecurityContextHolder.getContext().setAuthentication(authentication);
}
Reffers to PhilippeAuriach answer - there is a better way to run new thread with authorites - with spring security extended runnable method, context from main thread is copied into delegated runnable
public void authorizedExecute(Runnable runnable) {
new Thread(new DelegatingSecurityContextRunnable(runnable)).start();
}
I am developing a REST API using RESTEasy with Guice and at the moment I am trying to incorporate basic authentication by use of an annotation similar to the #Auth found in Dropwizard. With
#Path("hello")
public class HelloResource {
#GET
#Produces("application/json")
public String hello(#Auth final Principal principal) {
return principal.getUsername();
}
}
the hello resource invocation should be intercepted by some code performing basic authentication using the credentials passed in the Authorization HTTP request header and on success injecting the principal into the method principal parameter. I would also like to be able to pass a list of allowed roles to the annotation, e.g. #Auth("admin").
I really need some advice in what direction to go to achieve this?
I think your best bet would be using an intermediate value within request scope. Assuming that you didn't put HelloResource in singleton scope, you can inject this intermediate value in some ContainerRequestFilter implementation and in your resource, and you can fill it inside this ContainerRequestFilter implementation with all authentication and authorization info you need.
It will look something like this:
// Authentication filter contains code which performs authentication
// and possibly authorization based on the request
#Provider
public class AuthFilter implements ContainerRequestFilter {
private final AuthInfo authInfo;
#Inject
AuthFilter(AuthInfo authInfo) {
this.authInfo = authInfo;
}
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
// You can check request contents here and even abort the request completely
// Fill authInfo with the data you need
Principal principal = ...; // Ask some other service possibly
authInfo.setPrincipal(principal);
}
}
#Path("hello")
public class HelloResource {
private final AuthInfo authInfo;
#Inject
HelloResource(AuthInfo authInfo) {
this.authInfo = authInfo;
}
#GET
#Produces("application/json")
public String hello() {
// authInfo here will be pre-filled with the principal, assuming
// you didn't abort the request in the filter
return authInfo.getPrincipal().getUsername();
}
}
public class MainModule extends AbstractModule {
#Override
protected void configure() {
bind(AuthFilter.class);
bind(HelloResource.class);
bind(AuthInfo.class).in(RequestScoped.class);
}
}
And even if you did put the resource (or even the filter) in singleton scope for some reason, you can always inject Provider<AuthInfo> instead of AuthInfo.
Update
It seems that I was somewhat wrong in that the filter is by default not in singleton scope. In fact it seem to behave like singleton even though it is not bound as such. It is created upon JAX-RS container startup. Hence you will need to inject Provider<AuthInfo> into the filter. In fact, the container startup will fail if AuthInfo is injected into the filter directly while being bound to request scope. Resource (if not explicitly bound as singleton) will be OK with direct injection though.
I have uploaded working program to github.