Vertx and validating a JWT

762 views
Skip to first unread message

Jerry Thome

unread,
Feb 6, 2019, 12:21:04 PM2/6/19
to vert.x
We are starting to use the oauth2 libraries to execute an AUTH Code flow, retrieve the access_token, and validate the JWT signature.  So far, the auth code flow works great.  We have a simple POC setup to 'protect' a resource like seen in many examples.  I am able to retrieve the JSON from the principal.

principal = {
  "access_token" : "eyJhbGciOiJSUzUxMiJ9.eyJpcsIn0RemovedCharactersToShorten.iuBODHxV0o2nBCvTsJSwkkkJYxxi2X0qFOCtznBJqSRO6uxU0J3Yjy_INw8xVHelxrWKurE5D_Cy23bnKRl-adSuoCJfGRq5SFF7B2BHvmJ4fxhy1scpA0CCKEzknCBWDcYzKJDzmrv2ZeA17DGhAAXsFFuY33Rlzj7duyu9AfK-1i65oZeBDw1e72a0Y7eifcZCueRI2NHR-gvwNYr4shhdEzgyifgcCThmb_HjaBAi5ydKgqngfuHzQ1twwD87Lc9le_7i_e1yt3oc4SXuhiWLbqLfjB0E2_j_RxhYp448DiDfbXM7KJDDzsxjJOW2X2qrIV1ksF-mDEsJq7AuO20M-eTuRvSDXdrra_W5S6VG-yJsKl1z77rVKZWaf9DwFjgE1RNuPDkHnBpRmMyKPlhF124WkYqOf4H1Jif_2v2U8bY41x6pjhWNGUGpbd2xMAsZMVra8iXseN3fKKHqaD57eWX8XGjFxkadvGAo5V-cIBzPgf4ZFSXpX90fGgxl3MC2_PXgV4Fzf4GD6RfJ96zhfaqK-tNsgIlE4w8qzlkgf70ueAn3wkq4qz4PERuixhLnkuXBLjkQgOUGVo_-gbBuoSah_ctE-5SvGFI4e2h4VcAv3dyuzscWMTyAMoz1k_twLPVBHahBqkflEEcQ2Y6AxrR-4Mfwlnr_mn7FCCM",
  "expires_in" : 300,
  "refresh_token" : "RB10724B10CDA423192C39038086AB20D",
  "token_type" : "Bearer",
  "expires_at" : 1549467771152
}

How do we now convert the access_token into a validated JWT?  Is that done through OAuth2Auth and decodeToken (which seems to be deprecated) or should we use JWTAuth?  Ideally, I'd like to leverage the "jwks_uri" defined in our /.well-known/openid-configuration to avoid dealing with keystores and whatnot.

Any tips, suggestions, or references to Java examples using well-known configuration to validate a JWT would be greatly appreciated.  I've spent hours looking at examples and trying different things with keys, urls, etc. but I'm coming up short.

Thanks,
Jerry

Paulo Lopes

unread,
Feb 7, 2019, 6:34:20 AM2/7/19
to vert.x
Hi Jerry,

I think you should be looking at:


This will setup a working oauth2 auth provider with JWT support (if your openid discovery has the keys listed as you wrote).

The "decodeToken" method as been deprecated as it caused confusion and was doing the same work as the generic interface method:

"authenticate(json, handler)"

is doing.

The format for the json is:

{
  access_token: "token"
  type: "Bearer"
}

For vert.x 4 we I've the plan to make this json a data object so type safety and documentation is clear about which fields are allowed or not.

Jerry Thome

unread,
Feb 7, 2019, 8:51:46 PM2/7/19
to vert.x
Today we tried OpenIDConnectAuth.rxDiscover.  We hit issues just loading the configuration.  The Vert.x code was easy to debug through and helped us determine our `well-known` was not 100% spec compliant.  We then compared the Vert.x code (like acceptable kty values) against the specifications to double check possible values.  The code was much easier to follow and understand than the specs.  Wow...

Off to a good start.  We should be closer to end-to-end testing tomorrow.  

Thanks for pointing us in this direction!

Jerry


Paulo Lopes

unread,
Feb 8, 2019, 3:44:44 AM2/8/19
to vert.x
Great to hear that!

Jerry Thome

unread,
Feb 12, 2019, 11:18:52 AM2/12/19
to vert.x
Hey Paulo, hoping you can give us your thoughts....

In the well-known, we have "authorization_endpoint":"https://security.example.com/auth/service/oauth2/{tenant}/authorize" configured.  The idea is to support multi-tenancy.  The client is suppose to substitute the "{tenant}" with the respective clientId we need to authenticate with.  Microsoft supports something similar https://docs.microsoft.com/en-us/azure/active-directory/develop/v1-protocols-openid-connect-code.

Originally, we tried something like this...

OpenIDConnectAuth.rxDiscover(vertx, new OAuth2ClientOptions()
    .setClientID("myclientId")
    .setClientSecret("mysecret")
    .setSite("https://security.example.com"))
.flatMap(this::createHttpServer)
...

and saw a _bad request_ response from the auth server.  That's when we realized we need to replace the "{tenant}" before the authorize call.  So, I tried the below because the API supported it:

OpenIDConnectAuth.rxDiscover(vertx, new OAuth2ClientOptions()
    .setClientID("myclientId")
    .setClientSecret("mysecret")
.flatMap(this::createHttpServer) // Mount the config and create the HTTP server
...

but the setAuthorizationPath seems to be completely ignored.  Is it intentional that information read from the well-known should not be tweaked?  At a glance, OpenIDConnectAuth.discover codes seems to grab "authorization_endpoint" from well-known and I don't see a way to override it.

Thoughts?

Thanks.

Paulo Lopes

unread,
Feb 14, 2019, 5:40:59 AM2/14/19
to vert.x
Hi Jerry,

Out of curiosity what kind of provider is this?

It looks like you need some sort of string interpolation mechanism which is not hard to implement but it needs to reach the implementation details of the handler. The discovered value takes precedence so that is why it looks your manual override is ignored, if this is a well documented feature we might be able to do something about it.

Jerry Thome

unread,
Mar 8, 2019, 12:27:01 PM3/8/19
to vert.x
Hey Paulo,

The 'provider' is an internal service built to support 2 and 3 legged auth flows.  It was built several years ago before cloud services like auth0 and others became well-known and somewhat trusted.

Here is my best attempt at describing the feature need.

Actors in this example:
- Shared Authorization / Authentication Service (supports OAuth2)
- Educational Service Provider App 1
- Educational Service Provider App 2
- Users from Jackson Elementary School
- Users from Jefferson Middle School

Assertion(s):
- Each Service Provider App is registered with the auth service and has a client id and password

Scenario:
- The "Educational Service Provider App 2" wants to use OAuth2 Auth Code flow that the shared authentication service supports, to authenticate a user from "Jackson Elementary School"

Problem:
The Authorization / Authentication Service needs to know which school (or tenant) the request is on behalf of (different databases and routing).  It only has the clientId and password representing the application.  

We express the customer through a {tenant} in our published endpoints like in the example below:
Microsoft has better documentation expressing their support for multi-tenancy with some examples too:

I think I have this all correct.  Does the general scenario make sense?

Thanks. 

Jerry Thome

unread,
Mar 19, 2019, 2:40:26 PM3/19/19
to vert.x
Hey Paulo,

Any thoughts on the feature request described?  We have a story coming pretty quickly where we'll need the variable substitution to support multi-tenancy.  Maybe we can cobble together various pieces of the OAuth2 apis to get the flow we need, but the OpenIDConnect is exactly what we need (just with the substitution).

Thanks

Paulo Lopes

unread,
Apr 8, 2019, 5:08:16 AM4/8/19
to vert.x
Sorry for the late reply, please open an issue in vertx-auth to support {tenant} placeholder in the urls. This is an important feature and we need to store the tenant in the user object, plus allow it to be passed in the authorization call
Reply all
Reply to author
Forward
0 new messages