I am relatively new to Java programming so please bear with me. I am developing a microservice to be deployed on Payara Micro. For security, I am using JWTs. Because Payara Micro is an implementation of MicroProfile, I am trying to leverage the MicroProfile JWT auth spec (Payara Micro implements spec 1.1). In accordance with the spec, I am annotating the JAX-RS configuration class with @LoginConfig
:
@ApplicationScoped
@LoginConfig(authMethod = "MP-JWT")
@DeclareRoles({"admin", "standard", "premium"})
@ApplicationPath("/*")
public class IdentityService extends Application {}
Payara Micro implements a filter that activates @RolesAllowed
annotations on JAX-RS resources (normally exclusively available to EJBs). I am leveraging this annotation to identify the claims of the JWT using the JsonWebToken
interface from the MP JWT auth spec to determine if the user is allowed to make a GET request to access a user's information based on the id in their claim:
@RolesAllowed({"admin", "standard", "premium"})
@RequestScoped
@Path("/users")
@Produces(MediaType.APPLICATION_JSON)
public class UserEndpoint {
@Inject
private JsonWebToken jwtPrincipal;
@Inject
private UserRepository userRepo;
@Path("/{id}")
@GET
public Response get(@PathParam("id") String id) {
if (!jwtPrincipal.getSubject().equalsIgnoreCase(id))
return Response.status(Response.Status.FORBIDDEN).build();
User user;
user = userRepo.get(UUID.fromString(id));
return Response.status(Response.Status.OK).entity(user).build();
}
Additionally, according to Payara's MP JWT auth activation guide, I am providing the following config properties in my META-INF/microprofile-config.properties
file for Payara to verify:
mp.jwt.verify.issuer = com.someDomain
mp.jwt.verify.publickey.location = jwtPublic.pem
It should be noted that I am vending my own JWTs through a separarate @PermitAll
endpoint based on user login information. However, these seem to be correctly constructed as they can easily be debugged and signture is verified in jwt.io's debugger (not providing keys for verification because question is already long enough):
ew0KICAidHlwIiA6ICJKV1QiLA0KICAiYWxnIiA6ICJSUzI1NiINCn0=.ew0KICAiaXNzIiA6ICJjb20uc29tZURvbWFpbiIsDQogICJzdWIiIDogIjA5M2EzYzRlLWRiZTItNGI2YS05NzU4LTI5YjM2NjU1MTljOSIsDQogICJleHAiIDogMTU2NTM1Mjc2OCwNCiAgImlhdCIgOiAxNTY1MzA5NTY4LA0KICAidXBuIiA6ICJzb21lRW1haWxAZW1haWwuY29tIiwNCiAgImp0aSIgOiAiYmU0OTNhMTgtOGZiOS00NmM3LTgwMmEtNDY1YjFhYmM2YmZlIiwNCiAgImdyb3VwcyIgOiBbICJzdGFuZGFyZCIgXSwNCiAgInByZWZlcnJlZF91c2VybmFtZSIgOiAiY2FtZXJvbjIiDQp9.F97KMZbkPNuMATlYm0pLKalvadf_aAoxgPiNR-V-Y8toeYNqAm-w1WwnttQCeBuWvXjvPCm70y-HDGGv2tB-KuzHFxKxreyDue11db1fQ6o7QtYWSYvu_1y8bhOnB-MUz3MWnhRSHj8GpCQkrYXNACW9VSv8poqgyhyctEhi98LHGCFLyg1JNrzZw2J7fCdOYeoVZlgo-I9F4XA7FIXSQxgUii1T5RcGkky3tBKVUZ8jIigW68LXtYlq12lfo3PDATxou7c5ybMijLYuwPi7A6rb5bvsqAdsO-mSP6M6t42mUYGJuJ9dx_GmhWOpYzfgFaynMHElpuV58q7pXP5_JRvMx37yHMuA0Z_dj9ruSBqqH-lN0gV3CDheuHICbxAwqFvCEgVG7ZC4S3JNkSTqofifw_iXfTJX-v8cfWI3kfH7PrZmSmrMApGQLt5bQw_HIcIWfbHuA9_r-YksdIzJRsW-2JpSEHxRPeGvq5BlXlMSWu4BoefGcTbj6OKj6Gz_Zb5O8l8mOxQAT3wElN3DRxj3M_jxRM6kg-C-DlEFAxFivNQGbpE4CSKnLaY8XnTeL3j3Pq7tnYm1kG4PWeHCppr9CKgITjfrzzY3UNQX56WwDuRFTfgmY2Tnji4xqmLxxVCGE8ahM8mnxouIiwlPjlixkRXJfkxYb8YaQSMjIGw=
However, the behavior of the @RolesAllowed
annotation seems to be unexpected. When I send a curl to the endpoint while passing the JWT bearer token as an authorization HTTP header:
curl -i --header "Authorization: Bearer ew0KICAidHlwIiA6ICJKV1QiLA0KICAiYWxnIiA6ICJSUzI1NiINCn0=.ew0KICAiaXNzIiA6ICJjb20uc29tZURvbWFpbiIsDQogICJzdWIiIDogIjA5M2EzYzRlLWRiZTItNGI2YS05NzU4LTI5YjM2NjU1MTljOSIsDQogICJleHAiIDogMTU2NTM1Mjc2OCwNCiAgImlhdCIgOiAxNTY1MzA5NTY4LA0KICAidXBuIiA6ICJzb21lRW1haWxAZW1haWwuY29tIiwNCiAgImp0aSIgOiAiYmU0OTNhMTgtOGZiOS00NmM3LTgwMmEtNDY1YjFhYmM2YmZlIiwNCiAgImdyb3VwcyIgOiBbICJzdGFuZGFyZCIgXSwNCiAgInByZWZlcnJlZF91c2VybmFtZSIgOiAiY2FtZXJvbjIiDQp9.F97KMZbkPNuMATlYm0pLKalvadf_aAoxgPiNR-V-Y8toeYNqAm-w1WwnttQCeBuWvXjvPCm70y-HDGGv2tB-KuzHFxKxreyDue11db1fQ6o7QtYWSYvu_1y8bhOnB-MUz3MWnhRSHj8GpCQkrYXNACW9VSv8poqgyhyctEhi98LHGCFLyg1JNrzZw2J7fCdOYeoVZlgo-I9F4XA7FIXSQxgUii1T5RcGkky3tBKVUZ8jIigW68LXtYlq12lfo3PDATxou7c5ybMijLYuwPi7A6rb5bvsqAdsO-mSP6M6t42mUYGJuJ9dx_GmhWOpYzfgFaynMHElpuV58q7pXP5_JRvMx37yHMuA0Z_dj9ruSBqqH-lN0gV3CDheuHICbxAwqFvCEgVG7ZC4S3JNkSTqofifw_iXfTJX-v8cfWI3kfH7PrZmSmrMApGQLt5bQw_HIcIWfbHuA9_r-YksdIzJRsW-2JpSEHxRPeGvq5BlXlMSWu4BoefGcTbj6OKj6Gz_Zb5O8l8mOxQAT3wElN3DRxj3M_jxRM6kg-C-DlEFAxFivNQGbpE4CSKnLaY8XnTeL3j3Pq7tnYm1kG4PWeHCppr9CKgITjfrzzY3UNQX56WwDuRFTfgmY2Tnji4xqmLxxVCGE8ahM8mnxouIiwlPjlixkRXJfkxYb8YaQSMjIGw=" "localhost:8080/users/093a3c4e-dbe2-4b6a-9758-29b3665519c9"
I receive the following response:
HTTP/1.1 401 Unauthorized
Server: Payara Micro #badassfish
WWW-Authenticate: Authentication resulted in SEND_FAILURE
After digging around in Payara's source code, I discovered that this response is received here.
Apparently, this occurs when the Principal
received from SecurityContext
is null and when a @RolesAllowed
annotation is present in a JAX-RS resource. The container-provided authentication module is then apparently called, which in Payara's case is BASIC authentication: https://github.com/payara/Payara/issues/2326#issuecomment-367855133. However, I don't understand why BASIC authentication is being used when I specify @LoginConfig(authMethod="MP-JWT")
and I am providing a JWT as an authorization header. There are issues related to this on github but I haven't been able to find a definitive answer on what the fix is.
So, my questions are, am I incorrectly interpreting how to get Payara to produce an injectable JsonWebToken
of the caller? How can I fix this SEND_FAILURE response?