I have a Quarkus application in which I implemented the ContainerRequestFilter
interface to save a header from incoming requests:
@PreMatching
public class SecurityFilter implements ContainerRequestFilter {
private static final String HEADER_EMAIL = "HD-Email";
@Override
public void filter(ContainerRequestContext requestContext) throws IOException {
String email = requestContext.getHeaders().getFirst(HEADER_EMAIL);
if (email == null) {
throw new AuthenticationFailedException("Email header is required");
}
requestContext.setSecurityContext(new SecurityContext() {
@Override
public Principal getUserPrincipal() {
return () -> email;
}
@Override
public boolean isUserInRole(String role) {
return false;
}
@Override
public boolean isSecure() {
return false;
}
@Override
public String getAuthenticationScheme() {
return null;
}
});
}
}
In a class annotated with ApplicationScoped
I injected the context as follows:
@ApplicationScoped
public class ProjectService {
@Context
SecurityContext context;
...
}
The problem is that the context
attribute is actually never injected, as it is always null
.
What am I doing wrong? What should I do to be able to retrieve the SecurityContext
throughout the application's code?
I like to abstract this problem, so that the business logic does not depend on JAX-RS-specific constructs. So, I create a class to describe my user, say User
, and another interface, the AuthenticationContext
, that holds the current user and any other authentication-related information I need, e.g.:
public interface AuthenticationContext {
User getCurrentUser();
}
I create a RequestScoped
implementation of this class, that also has the relevant setter(s):
@RequestScoped
public class AuthenticationContextImpl implements AuthenticationContext {
private User user;
@Override
public User getCurrentUser() {
return user;
}
public void setCurrentUser(User user) {
this.user = user;
}
}
Now, I inject this bean and the JAX-RS SecurityContext
in a filter, that knows how to create the User
and set it into my application-specific AuthenticationContext
:
@PreMatching
public class SecurityFilter implements ContainerRequestFilter {
@Inject AuthenticationContextImpl authCtx; // Injecting the implementation,
// not the interface!!!
@Context SecurityContext securityCtx;
@Override
public void filter(ContainerRequestContext requestContext) throws IOException {
User user = ...// translate the securityCtx into a User
authCtx.setCurrentUser(user);
}
}
And then, any business bean that needs the user data, injects the environment-neutral, application-specific AuthenticationContext
.