OpenIDConnectAuth for a server application

121 views
Skip to first unread message

Mohammad Naghavi

unread,
Mar 10, 2020, 8:27:00 AM3/10/20
to vert.x
As I had the problem of JWKS fetch and rotation with Auth0, I decided to use OIDC for simpler setup but I'm not getting how things should be configured so that the resulting OAuth2AuthHandler will be functionality similar to JWTAuthHandler.

I have a web application which has own client Id from Auth0 and fetches a JWT encoded access token successfully. In that token the audience is set to the vertx server application's id (and future URL).

While configuring JWTAuth, I provide following information: algorithm, audience, issuer and leeway. The authorities are then set on the created JWTAuthHandler. All these are correctly checked for when a request comes in with JWT token.

Now trying to migrate to OIDC, I configure OpenIDConnectAuth with site (which is the same value as JWT's issuer) and pass empty string as client Id, as it seems to me that as server app, I don't need that set as the exchange of authorization code for access token is done by client.

What I can't find out is, when using OIDC whether:
- is it possible to set any leeway (to overcome time sync problems)?
- how can I set a check for audience of JWT token? (provider issues tokens for multiple audience and I have to know if token is intended for current API)
- the scopes inside JWT are not added as authorities when using OAuth2AuthHandler, so how is the authroization check possible?
- also, JWTAuthOptions allows setting permission claim key to customise where permissions are retrieved form JWT, (how) is it possible to do while using OIDC?


I'm using Vertx 4.0 milestone 4

Thanks in advance for helping me.

Mohammad Naghavi

unread,
Mar 10, 2020, 8:43:26 AM3/10/20
to vert.x
one more comment, using OAuth2ClientOptions().setJWTOptions() doesn't help as it seems anything set on that is ignored. I tried setting an audience that is not present in JWT and an invalid issuer and it didn't use these values.

Christopher Tate

unread,
Mar 10, 2020, 9:38:45 AM3/10/20
to vert.x
Hello Mohammad!

These are the configuration items I use for integrating OpenID Connect with a Keycloak or Red Hat SSO server:
JsonObject keycloakJson = new JsonObject()
   
.put("realm", siteConfig.getAuthRealm())
   
.put("resource", siteConfig.getAuthResource())
   
.put("auth-server-url", siteConfig.getAuthUrl())
   
.put("ssl-required", siteConfig.getAuthSslRequired())
   
.put("credentials", new JsonObject().put("secret", siteConfig.getAuthSecret()))
   
;

You can find this configuration here in my open source project:

* Within Red Hat SSO, which I deployed on openshift.com, I can setup a "realm" which I configured above as "COMPUTATE.ORG":

* I configure a client "computate.org" in that realm as the "resource" in the configuration:

* The "auth-server-url" is the URL to Red Hat SSO as you can see above: "https://sso.computate.org/auth".
* I would always set "ssl-required" to true and use valid certificates, for info on generating those TLS certificates for free, you can see my website here: https://www.computate.org/enUS/course/001/008-how-to-obtain-free-tls-certificates
* The "credentials" "secret" is defined here in Red Hat SSO:

I integrate with Keycloak and Red Hat SSO similarly and perform a successful logout without a call to AccessTokenImpl.logout. The user needs to visit a link to the keycloak server's openid-connect/logout request for the realm, with the redirect_url that will log them out of keycloak, as described here:

I make the logout URL on the web page link be this:

String o = siteConfig.getAuthUrl()
        + "/realms/"
        + siteConfig.getAuthRealm()
        + "/protocol/openid-connect/logout?redirect_uri="
        + URLEncoder.encode(siteConfig.getSiteBaseUrl() + "/logout", "UTF-8");

You can find this here in my open source project:

Then you need to configure a logout route in your Vert.x application to get the session, destroy the session and clear the user, then reroute somewhere.

router.get("/logout").handler(rc -> {
    Session session = rc.session();
    if (session != null) {
        session.destroy();
    }
    rc.clearUser();
    rc.reroute("/school");
});

You can see my whole configureOpenApi Promise method here where Red Hat SSO integration is configured:


Then you need to configure your Red Hat SSO client to have the right redirect URLs for logout, you can figure the URLs here for both development up to production if they use the same Keycloak server, like I do here:


You would also setup a callback route as well here, that part is important too:


You setup an authProvider, setup cookies, setup sessions, create the OAUTH2AuthHandler with a valid callback URL and callback method that does nothing like below:

OAuth2Auth authProvider = KeycloakAuth.create(vertx, OAuth2FlowType.AUTH_CODE, keycloakJson);

router
.route().handler(new CookieHandlerImpl());
LocalSessionStore sessionStore = LocalSessionStore.create(vertx);
SessionHandler sessionHandler = SessionHandler.create(sessionStore);
sessionHandler
.setAuthProvider(authProvider);
router
.route().handler(sessionHandler);

String siteUrlBase = siteConfig.getSiteBaseUrl();
OAuth2AuthHandler authHandler = OAuth2AuthHandler.create(authProvider, siteUrlBase + "/callback");

authHandler
.setupCallback(router.get("/callback"));

Hopefully that gets you started :)

Courage!
Christopher Tate

Mohammad Naghavi

unread,
Mar 10, 2020, 9:58:14 AM3/10/20
to vert.x
Hi Christophe, 

thanks for the answer with all the screenshots and co. However I'm concretely talking about using Auth0. Setting up and maintaining any provider like Keycloak is for me out of scope.

so just for better explanation, I include the code. My goal is to make following two methods to work similarly as authentication handlers:


private static Single<AuthHandler> setupAuth0UsingJwt(Vertx vertx, String issuer, String audience) {
return WebClient
.create(vertx, new WebClientOptions().setKeepAlive(false))
.getAbs(issuer + ".well-known/jwks.json")
.rxSend()
.map(response ->
response
.bodyAsJsonObject()
.getJsonArray("keys")
.stream()
.map(o -> (JsonObject) o)
.collect(Collectors.toList())
)
.map(jwks -> {
var jwtAuth =
JWTAuth.create(
vertx,
new JWTAuthOptions()
.setJWTOptions(new JWTOptions()
.setAlgorithm("RS256")
.setAudience(List.of(audience))
.setIssuer(issuer)
.setLeeway(10)
)
.setPermissionsClaimKey("permissions")
.setJwks(jwks)
);

return JWTAuthHandler.create(jwtAuth);
});
}

private static Single<AuthHandler> setupAuth0UsingOidc(Vertx vertx, String issuer, String audience) {
return OpenIDConnectAuth
.rxDiscover(
vertx,
new OAuth2ClientOptions()
.setSite(issuer)
.setClientID("")
.setJWTOptions(new JWTOptions()
.setAlgorithm("RS256")
.setAudience(List.of(audience))
.setIssuer(issuer)
.setLeeway(10)
)
)
.map(OAuth2AuthHandler::create);
}

Where issuer is a valid domain name of Auth0 which is issuing JWT tokens and audience is the identifier of an API in terms of Auth0 for which the client which is calling my server is requesting the JWT tokens.

currently, using JWTAuth all works as expected. When a request comes in with a JWT token in header, issuer, audience are checked for correctness and the permissions list is loaded properly. but when using OIDC, neither issuer and audience are tested for in the JWT token nor permissions are loaded into user object which is injected in routing context. 

Any idea if and how they could be made to act similarly?

Paulo Lopes

unread,
Mar 10, 2020, 2:44:22 PM3/10/20
to vert.x
Hi,

Some of the things you mention are already in the 4.0 branch, for example, refresh of keys on jwt. What is still missing, is to use the exact same validation procedure for token based oidc and jwt.

Regarding Auth0, I don't have experience with it (or an account) but for sure you must have a "clientId" as it's a protocol requirement.

I've quickly created an account and during the setup I got it here:

Mohammad Naghavi

unread,
Mar 11, 2020, 3:26:59 AM3/11/20
to vert.x
Hi Paulo,

thanks for the comments and taking time to sign up on Auth0. I think this might apply not only to Auth0 but any provider with `.well-known` URLs for both OIDC and JWKS.

regarding your comment:

Refreshing JWT keys and even fetching them at first place to begin with is no where in docs to find so I rolled my own fetching process. So definitely some mention in docs are needed. Also all the deprecations in AuthHandlers that don't have any comments what should be done instead are very confusing.

So for OIDC's JWT validation, I have to wait.

As for clientId, what you see there is the application clientId, which is supposed to be my single page application or any other client to my API (=resource server). In Auth0 resource servers are named APIs. there you don't have clientId but an identifier which is supposed to be the audience in JWT tokens. In general I think in any OAuth2 process, including OIDC, the resource server which is just receiving access tokens to let clients work with resources don't need a clientId as it does not exchange auth code for access token or refresh a token. It is all done on the client, right? so basically just for sake of authN & authZ on resource server, no client ID is needed. Please correct me if i'm wrong. 


Regards,
Mohammad
Reply all
Reply to author
Forward
0 new messages