MP JWT issues with IBM and Payara

605 views
Skip to first unread message

Ivan St. Ivanov

unread,
Mar 15, 2018, 6:01:02 PM3/15/18
to MicroProfile, Митя
Hello MicroProfilers!

As some of you already know, we together with my Bulgarian JUG friend Dmitry are working on the MicroProfile hands-on-lab. At the moment it is targeted at MP 1.2, but we'll easily add 1.3 features once the specs are implemented.

The overview

The lab contains a sample microservice application as well as a step-by-step guide (which we just started writing). The app represents a magazine manager with articles, authors, subscribers, user management and HTML + jQuery UI. There are 5 microservices each of which running on a different runtime:

- user microservice runs on WildFly Swarm
- content microservice runs on IBM Liberty Profile
- subscribers microservice runs on Payara Micro
- authors microservice runs on Hammock
- GUI microservice runs on TomEE

The idea is that when you start with the lab you will have a fully functional app and you will be guided to add the different MP aspects (config, health, metrics, fault tolerance, JWT, etc.)

The JWT usecase

We wanted to showcase JWT in the following way:

- When a user logs in the web page, the GUI microservice performs a request to the user microservice
- The latter checks whether this user/password combination exists and if it does, it generates a JWT token and returns it
- The subsequent requests from the GUI set the Authorization header to the JWT token
- We use that to make sure that adding new articles in the content microservice is only possible if the caller belongs to the authors group. And adding a subscriber in the subscriber microservice is only available to users in the admin group

Unfortunately neither IBM (content) nor Payara (subscribers) seem to work.

The issues

In both microservices (in the JAX-RS resource more precisely) I inject the groups claim like that:
@Inject
@Claim(standard = Claims.groups)
private Set<String> roles;
However, IBM fails with a NullPointerException upon instantiating the resource class:

java.lang.NullPointerException
at com.ibm.ws.security.mp.jwt.cdi.JsonWebTokenBean.create(JsonWebTokenBean.java:72)
at [internal classes]
at org.jboss.weld.context.AbstractContext.get(AbstractContext.java:96)
at org.jboss.weld.bean.ContextualInstanceStrategy$DefaultContextualInstanceStrategy.get(ContextualInstanceStrategy.java:100)
at [internal classes]
at org.eclipse.microprofile.jwt.JsonWebToken$1561974719$Proxy$_$$_WeldClientProxy.getClaim(Unknown Source)
at com.ibm.ws.security.mp.jwt.cdi.ClaimProducer.getSetString(ClaimProducer.java:196)
at [internal classes]
at org.jboss.weld.context.AbstractContext.get(AbstractContext.java:96)
at org.jboss.weld.bean.ContextualInstanceStrategy$DefaultContextualInstanceStrategy.get(ContextualInstanceStrategy.java:100)
at [internal classes]
at bg.jug.microprofile.hol.content.ArticleResource$Proxy$_$$_WeldClientProxy.getAllArticles(Unknown Source)

Payara is not even able to deploy my app. It fails with:

org.jboss.weld.exceptions.DeploymentException: WELD-001408: Unsatisfied dependencies for type Set<String> with qualifiers @Claim
  at injection point [BackedAnnotatedField] @Inject @Claim private bg.jug.microprofile.hol.subscribers.SubscribersResource.roles
  at bg.jug.microprofile.hol.subscribers.SubscribersResource.roles(SubscribersResource.java:0)

at org.jboss.weld.bootstrap.Validator.validateInjectionPointForDeploymentProblems(Validator.java:362)
at org.jboss.weld.bootstrap.Validator.validateInjectionPoint(Validator.java:284)
at org.jboss.weld.bootstrap.Validator.validateGeneralBean(Validator.java:137)
at org.jboss.weld.bootstrap.Validator.validateRIBean(Validator.java:158)
at org.jboss.weld.bootstrap.Validator.validateBean(Validator.java:501)
at org.jboss.weld.bootstrap.Validator.validateBeans(Validator.java:487)
at org.jboss.weld.bootstrap.Validator.validateDeployment(Validator.java:462)

Here is the JWT that I send in the Authorization header:

{
    "kid": "mp-hol",
    "typ":"JWT",
    "alg":"RS256"
}
{
    "sub":"bi...@example.org",
    "upn":"bi...@example.org",
    "auth_time":1520753020,
    "iss":"http:\/\/localhost:9100",
    "groups":["admin"],
    "exp":1520753320,
    "given_name":"Bilbo",
    "iat":1520753020,
    "family_name":"Baggins"
}

It is signed with a private key on the users service (the one that generates it). And is of course crypted. Eventually, it looks like this:

eyJraWQiOiJtcC1ob2wiLCJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJiaWxib0BleGFtcGxlLm9yZyIsInVwbiI6ImJpbGJvQGV4YW1wbGUub3JnIiwiYXV0aF90aW1lIjoxNTIwNzUzMzU2LCJpc3MiOiJodHRwOlwvXC9sb2NhbGhvc3Q6OTEwMCIsImdyb3VwcyI6WyJhZG1pbiJdLCJleHAiOjE1MjA3NTM2NTYsImdpdmVuX25hbWUiOiJCaWxibyIsImlhdCI6MTUyMDc1MzM1NiwiZmFtaWx5X25hbWUiOiJCYWdnaW5zIn0.WcjsvYYsURSwthvzWNHqHVIa-N0pxkEsn-mOdfYo_pOLz9zxlr1NppARxe6TlKBBq7P-fAhuV_n8bJ_tNGerlkcMpEde12R25xYhWjSPja_T_7vT9HZrLH9sG0JNn_dfYLGgoe924PwkuQAhlYzak0aE7C2p0srYNMsRIUdDHlk

Of course both microservices contain the @LoginConfig annotation on their respective JAX-RS application class:
@LoginConfig(authMethod = "MP-JWT", realmName = "MP-HOL-JWT")
public class Application extends javax.ws.rs.core.Application {
}
I guess I am missing something, because my code was not much different than that of the TCK.

Here is the sample app in github:


Thank you for your help!
Ivan and Dmitry

Chunlong Liang

unread,
Mar 16, 2018, 12:32:38 AM3/16/18
to Eclipse MicroProfile
Can you send me your Liberty server.xml to show how you configure Liberty to consume JWT?

Ivan St. Ivanov

unread,
Mar 16, 2018, 2:57:06 AM3/16/18
to MicroProfile
It is part of the content microservice: https://github.com/bgjug/microprofile-hol-1x/blob/jwt/solution/content/src/main/config/server.xml

I've attached it here as well

Thanks,
Ivan

--
You received this message because you are subscribed to the Google Groups "Eclipse MicroProfile" group.
To unsubscribe from this group and stop receiving emails from it, send an email to microprofile+unsubscribe@googlegroups.com.
To post to this group, send email to microp...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/microprofile/58432c8e-291e-45f6-a15c-96cf17d4e311%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

server.xml

Arjan Tijms

unread,
Mar 16, 2018, 10:59:24 AM3/16/18
to Eclipse MicroProfile
Hi,

I'll take a look at the code soon, but just one remark first;

@Inject
@Claim(standard = Claims.groups)
private Set<String> roles;

You can't/shouldn't use this for actual role checking. The groups as injected are *request scoped*, while authentication is not necessarily request scoped at all. Additionally, the raw groups can still be mapped or otherwise subjected to authorization constraints, which will be missed when looking at the token directly.

Personally I feel it may be better to remove all of the injection requirements from the JWT spec and only have the token itself made available from the Principal that you get by e.g. @Inject Principal principal; That would make it quite a bit more clear I think we're dealing with the raw JSON token and not with any validated or processed groups.

Kind regards,
Arjan Tijms
To unsubscribe from this group and stop receiving emails from it, send an email to microprofile...@googlegroups.com.

Ivan St. Ivanov

unread,
Mar 16, 2018, 2:43:41 PM3/16/18
to MicroProfile
Thanks Arjan!

@The groups as injected are *request scoped*, while authentication is not necessarily request scoped at all.

We are using the JWT for authorization only. Which I guess should be request scope (as JWTs "expire").

Cheers,
Ivan

To unsubscribe from this group and stop receiving emails from it, send an email to microprofile+unsubscribe@googlegroups.com.

To post to this group, send email to microp...@googlegroups.com.

arjan tijms

unread,
Mar 16, 2018, 2:51:24 PM3/16/18
to MicroProfile
Hi,

On Fri, Mar 16, 2018 at 7:43 PM, Ivan St. Ivanov <ivan.st...@gmail.com> wrote:
We are using the JWT for authorization only. Which I guess should be request scope (as JWTs "expire").

The problem is that authentication is smaller than request scope. If a caller logs out mid-request, then for the remainder of the request the caller is obviously not authenticated, but the groups injected from JWT are still the same.

Even worse, if you'd mid request re-authenticate for whatever reason, possibly using a different user, the groups would still be the same, while the actual caller's groups and roles can have changed.

All together, depending on the injected groups for authorization decisions is not really safe. Even if it would happen to work for your case it gives the wrong example unfortunately, and in real life applications people could seriously trip over this.

I'll look at the rest of the code soon.

Kind regards,
Arjan Tijms





 

--
You received this message because you are subscribed to a topic in the Google Groups "Eclipse MicroProfile" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/microprofile/G0Mf-gKxNWI/unsubscribe.
To unsubscribe from this group and all its topics, send an email to microprofile+unsubscribe@googlegroups.com.

To post to this group, send email to microp...@googlegroups.com.

Arjan Tijms

unread,
Mar 16, 2018, 3:30:30 PM3/16/18
to Eclipse MicroProfile
Hi again,

I looked at the code for "subscribers" and noticed a few things:

1. @LoginConfig(authMethod = "MP-JWT") was missing
2. No accepted issuer was configured
3. No public key was configured

Note that configuration of those last two mandatory things for some reason were specified as needing to be done in a server specific way. As a result it must be realised that an application using MP-JWT *is never portable*. That's a big caveat.

I did a PR with the changes. See https://github.com/bgjug/microprofile-hol-1x/pull/2



That one shows how Liberty and WildFly need to be configured for MP-JWT.

Hope this helps!

Kind regards,
Arjan Tijms

Andy Guibert

unread,
Mar 16, 2018, 4:23:55 PM3/16/18
to Eclipse MicroProfile
Hi Ivan,

I ran into the same NPE as well when trying out JWT on Liberty and created an issue [1] for it.  Looks like it's caused by incomplete config, so the OpenLiberty team is changing the NPE to a properly translated error message [2].

Here is the error message that we're adding in place of this NPE:
CWWKS5604E: A JsonWebToken Principal can't be injected because one is not available. Protect the requesting resource so authentication occurs before the resource is accessed.
Do you think that error message would have been sufficient for you to figure out what's wrong if it had been thrown instead of the NPE?

I haven't had time to test out the fix myself, but based on what I've seen it seems that you're missing:
 1) A <keystore> configuration in the server.xml
 2) Some <security-constraint> in the app's web.xml to indicate what URL pattern and HTTP methods need to be secured.

Another thing that you may find helpful is the MicroProfile JWT guide on openliberty.iohttps://openliberty.io/guides/microprofile-jwt.html

HTH, Andy

[1] https://github.com/OpenLiberty/open-liberty/issues/2712


On Thursday, March 15, 2018 at 5:01:02 PM UTC-5, Ivan St. Ivanov wrote:

Alasdair Nottingham

unread,
Mar 16, 2018, 4:36:09 PM3/16/18
to microp...@googlegroups.com
By extension aren’t you arguing no app using SSL can be portable since SSL config is also app server specific?

Alasdair Nottingham
--
You received this message because you are subscribed to the Google Groups "Eclipse MicroProfile" group.
To unsubscribe from this group and stop receiving emails from it, send an email to microprofile...@googlegroups.com.
To post to this group, send email to microp...@googlegroups.com.

arjan tijms

unread,
Mar 16, 2018, 5:08:15 PM3/16/18
to MicroProfile
Hi,

On Fri, Mar 16, 2018 at 9:36 PM, Alasdair Nottingham <alasdair....@gmail.com> wrote:
By extension aren’t you arguing no app using SSL can be portable since SSL config is also app server specific?

Partially that's indeed the case, isn't it? 

In many cases though the application is unaware of SSL being used or not, and it would work either with or without. Occasionally you'd need to be a bit careful with how you handle redirects but that's it. Additionally, SSL requirements can quite often be switched off and the application would work all the same. This is for instance what the Java EE Security / Soteria tests do.

But as soon as an application uses MP-JWT, it's not directly portable anymore. Especially for cloud deployment this can be rather unexpected. Even more so since most implementations DO allow or even require the mandatory configuration be done within the application archive. 

So while with SSL it's largely a necessity, with MP-JWT there's simply no need for this. It concerns two configuration options; an issuer, and a public key. Neither of those have to be shielded from the untrusted eyes of developers. The public key is, by it's very name, public.

The config api already existed, so I'm not sure why MP-JWT didn't focus on defining portable config for that. But, alas, that happened, so for now I think we should focus on educating users that an MP-JWT application is not portable, and then fix this oversight/omission in a next spec revision.

Kind regards,
Arjan Tijms




 

Alasdair Nottingham

On Mar 16, 2018, at 3:30 PM, Arjan Tijms <arjan...@gmail.com> wrote:

Hi again,

I looked at the code for "subscribers" and noticed a few things:

1. @LoginConfig(authMethod = "MP-JWT") was missing
2. No accepted issuer was configured
3. No public key was configured

Note that configuration of those last two mandatory things for some reason were specified as needing to be done in a server specific way. As a result it must be realised that an application using MP-JWT *is never portable*. That's a big caveat.

I did a PR with the changes. See https://github.com/bgjug/microprofile-hol-1x/pull/2



That one shows how Liberty and WildFly need to be configured for MP-JWT.

Hope this helps!

Kind regards,
Arjan Tijms

--
You received this message because you are subscribed to the Google Groups "Eclipse MicroProfile" group.
To unsubscribe from this group and stop receiving emails from it, send an email to microprofile+unsubscribe@googlegroups.com.

To post to this group, send email to microp...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/microprofile/cbc2275a-ea70-4d3c-be83-163d6939326d%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

--
You received this message because you are subscribed to a topic in the Google Groups "Eclipse MicroProfile" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/microprofile/G0Mf-gKxNWI/unsubscribe.
To unsubscribe from this group and all its topics, send an email to microprofile+unsubscribe@googlegroups.com.

To post to this group, send email to microp...@googlegroups.com.

Ivan St. Ivanov

unread,
Mar 16, 2018, 5:27:42 PM3/16/18
to MicroProfile
Thank you all for the help and advises!

@Arjan: thanks for the PR! I've deliberately pushed the JWT implementation in the jwt branch, so that we could keep master running. There we had the public key and the @LoginConfig annotation. However, we were missing the properties file. So thanks for bringing that, I'll merge your fix!

Thank you also for the insight about the differences and the additional steps needed to configure JWT for the various servers. I think you are right that this needs to be specified further somehow. I mean, one and the same simple thing is done in different ways in each platform. So it shouldn't be hard to unify that. Great that Gaurav opened that issue. It makes also my life easier, as it summarizes the way to configure JWT for the runtimes that support it.

@Andy: thank you too! Obviously my server.xml lacks the configuration of the issuer (as in the Payara case). Plus, my public key is not jks, but pem. I'll fix those problems and will try again.

As for the error message in LIberty, I tried to find the source code, as the exception trace was pointing to a specific line. And it was NullPointerException, which is not that complicated to investigate. But I couldn't find the source (TBH, I didn't search really hard ;)). 

With all the documentation and Arjan's PR I will make this work hopefully.

Thanks again for your help!
Ivan

Ivan St. Ivanov

unread,
Mar 17, 2018, 9:11:34 AM3/17/18
to MicroProfile
Hi again,

With Payara the problem was actually that I have forgotten to add an empty beans.xml. Once I added it, my application could be successfully deployed. 

However, when I tried to request the method that accesses the injected claim, I got NullPointerException, i.e. the claim was not injected.

As for Liberty, I checked the given example (https://openliberty.io/guides/microprofile-jwt.html). It actually relies heavily on the Servlet spec. There are utilities to extract the authorization token from the request, then parse the JSON part. It also uses web.xml (as suggested in this thread by Andy as well) to protect resources, to define roles, etc. But I am afraid this is not what I want.

Generally, what I want is to let the runtimes parse the JWT for me and inject the claims in my JAX-RS resources. Everything that I implemented in the lab was based on what I found in the TCK, i.e. should be implemented by all the runtimes that state MP JWT compliance.


    @Inject
    @Claim("groups")
    private Set<String> groups;

In the same package is the test that is supposed to be run on the runtimes claiming MP JWT support:


In its deployment method it only packages the above class, the public key, empty beans.xml and the JAX-RS application class. No web.xml, no security constraints that come from the Servlet spec, no nothing.

The TCK test method verifyInjectedGroups checks exactly that the groups claim is injected and is not null.

As a conclusion, by just following the TCK classes I struggle booting up the resources that use MP JWT. I think the issuer and public key checks are only done after that?

I would appreciate any help here. I will present this HoL next Thursday at Voxxed Bucharest. We are ready with almost everything besides JWT. I am afraid that I will leave it out in the first version, if I don't find a solution soon. 

Thanks,
Ivan

Arjan Tijms

unread,
Mar 18, 2018, 5:44:56 PM3/18/18
to Eclipse MicroProfile
Hi,

I deployed the subscribers application locally to Payara here, and it deployed fine without having added beans.xml. I didn't test it though.

One other thing, you don't have to encrypt the token. Signing is enough and prevents tampering. Encryption is only needed when the token (also) contains sensitive information that shouldn't be read by third parties, but that's not the case here.

Just take a look at what the examples sent over the wire (https://github.com/javaee-samples/microprofile1.2-samples/tree/master/jwt-auth)

Kind regards,
Arjan

arjan tijms

unread,
Mar 19, 2018, 7:55:23 AM3/19/18
to MicroProfile

Chunlong Liang

unread,
Mar 19, 2018, 11:32:49 AM3/19/18
to Eclipse MicroProfile
For Liberty, it is OK not to have authorization constraint in web.xml, but you do need configure Liberty how to verify the JWT. In server.xml, you need have a configuration element like this:
<mpJwt 
     id="myMpJwt"
     jwksUri="https://example.com/api/jwk"
     issuer="https://example.com/api/v1"
     audiences="conferenceService">
</mpJwt>
Hi,

To unsubscribe from this group and stop receiving emails from it, send an email to microprofile...@googlegroups.com.

To post to this group, send email to microp...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/microprofile/cbc2275a-ea70-4d3c-be83-163d6939326d%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

--
You received this message because you are subscribed to a topic in the Google Groups "Eclipse MicroProfile" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/microprofile/G0Mf-gKxNWI/unsubscribe.
To unsubscribe from this group and all its topics, send an email to microprofile...@googlegroups.com.

To post to this group, send email to microp...@googlegroups.com.

--
You received this message because you are subscribed to the Google Groups "Eclipse MicroProfile" group.
To unsubscribe from this group and stop receiving emails from it, send an email to microprofile...@googlegroups.com.

To post to this group, send email to microp...@googlegroups.com.

Scott Stark

unread,
Mar 23, 2018, 12:53:21 AM3/23/18
to Eclipse MicroProfile
There is a TCK test for injection of the groups claim, however, the base testsuite xml configuration and documentation on running the TCK failed to include this test. This is covered by this issue:
https://github.com/eclipse/microprofile-jwt-auth/issues/61

I know there are a few holes in implementations because of this oversight. This will be addressed in a 1.1 release.
Reply all
Reply to author
Forward
0 new messages