Google/Facebook Authentication - vertx-auth or vertx-pac4j

427 views
Skip to first unread message

Shahji

unread,
Jan 6, 2017, 1:56:23 AM1/6/17
to vert.x

What is the recommend solution to use for integrating Google and Facebook authentication in application?

I found two existing solutions - vertx-auth and vertx-pac4j - the latter is however not part of 3.3.3 yet. Is it expected to be integrated in upcoming releases?

I tried the former solution based on documentation found here http://vertx.io/docs/vertx-auth-oauth2/java/ and ran into a few problems, specifically the following exception. Look like the "state" parameter is not being handled.

java.lang.NullPointerException: value
    at io.netty.util.internal.ObjectUtil.checkNotNull(ObjectUtil.java:31) ~[server.jar:na]
    at io.netty.handler.codec.DefaultHeaders.setObject(DefaultHeaders.java:478) ~[server.jar:na]
    at io.netty.handler.codec.http.DefaultHttpHeaders.set(DefaultHttpHeaders.java:164) ~[server.jar:na]
    at io.vertx.core.http.impl.HeadersAdaptor.set(HeadersAdaptor.java:97) ~[server.jar:na]
    at io.vertx.core.http.impl.HttpServerResponseImpl.putHeader(HttpServerResponseImpl.java:157) ~[server.jar:na]
    at io.vertx.core.http.impl.HttpServerResponseImpl.putHeader(HttpServerResponseImpl.java:54) ~[server.jar:na]
    at io.vertx.ext.web.handler.impl.OAuth2AuthHandlerImpl.handle(OAuth2AuthHandlerImpl.java:60) ~[server.jar:na]
    at io.vertx.ext.web.handler.impl.OAuth2AuthHandlerImpl.handle(OAuth2AuthHandlerImpl.java:32) ~[server.jar:na]
    at io.vertx.ext.web.impl.RouteImpl.handleContext(RouteImpl.java:215) [server.jar:na]
    at io.vertx.ext.web.impl.RoutingContextImplBase.iterateNext(RoutingContextImplBase.java:78) [server.jar:na]
    at io.vertx.ext.web.impl.RoutingContextImpl.next(RoutingContextImpl.java:94) [server.jar:na]
    at io.vertx.ext.web.handler.impl.UserSessionHandlerImpl.handle(UserSessionHandlerImpl.java:63) [server.jar:na]
    at io.vertx.ext.web.handler.impl.UserSessionHandlerImpl.handle(UserSessionHandlerImpl.java:29) [server.jar:na]
    at io.vertx.ext.web.impl.RouteImpl.handleContext(RouteImpl.java:215) [server.jar:na]
    at io.vertx.ext.web.impl.RoutingContextImplBase.iterateNext(RoutingContextImplBase.java:78) [server.jar:na]
    at io.vertx.ext.web.impl.RoutingContextImpl.next(RoutingContextImpl.java:94) [server.jar:na]
    at io.vertx.ext.web.handler.impl.SessionHandlerImpl.lambda$handle$0(SessionHandlerImpl.java:115) [server.jar:na]
    at io.vertx.ext.web.handler.impl.SessionHandlerImpl.lambda$doGetSession$2(SessionHandlerImpl.java:141) [server.jar:na]
    at io.vertx.ext.web.sstore.impl.LocalSessionStoreImpl.get(LocalSessionStoreImpl.java:60) ~[server.jar:na]
    at io.vertx.ext.web.handler.impl.SessionHandlerImpl.doGetSession(SessionHandlerImpl.java:128) [server.jar:na]
    at io.vertx.ext.web.handler.impl.SessionHandlerImpl.getSession(SessionHandlerImpl.java:124) [server.jar:na]
    at io.vertx.ext.web.handler.impl.SessionHandlerImpl.handle(SessionHandlerImpl.java:99) [server.jar:na]
    at io.vertx.ext.web.handler.impl.SessionHandlerImpl.handle(SessionHandlerImpl.java:33) [server.jar:na]
    at io.vertx.ext.web.impl.RouteImpl.handleContext(RouteImpl.java:215) [server.jar:na]
    at io.vertx.ext.web.impl.RoutingContextImplBase.iterateNext(RoutingContextImplBase.java:78) [server.jar:na]
    at io.vertx.ext.web.impl.RoutingContextImpl.next(RoutingContextImpl.java:94) [server.jar:na]
    at io.vertx.ext.web.handler.impl.BodyHandlerImpl$BHandler.doEnd(BodyHandlerImpl.java:202) ~[server.jar:na]
    at io.vertx.ext.web.handler.impl.BodyHandlerImpl$BHandler.end(BodyHandlerImpl.java:180) ~[server.jar:na]
    at io.vertx.ext.web.handler.impl.BodyHandlerImpl.lambda$handle$0(BodyHandlerImpl.java:67) ~[server.jar:na]
    at io.vertx.core.http.impl.HttpServerRequestImpl.handleEnd(HttpServerRequestImpl.java:406) ~[server.jar:na]
    at io.vertx.core.http.impl.ServerConnection.handleEnd(ServerConnection.java:298) ~[server.jar:na]
    at io.vertx.core.http.impl.ServerConnection.processMessage(ServerConnection.java:424) ~[server.jar:na]
    at io.vertx.core.http.impl.ServerConnection.handleMessage(ServerConnection.java:139) ~[server.jar:na]
    at io.vertx.core.http.impl.HttpServerImpl$ServerHandler.doMessageReceived(HttpServerImpl.java:615) ~[server.jar:na]
    at io.vertx.core.http.impl.HttpServerImpl$ServerHandler.doMessageReceived(HttpServerImpl.java:522) ~[server.jar:na]
    at io.vertx.core.http.impl.VertxHttpHandler.lambda$channelRead$0(VertxHttpHandler.java:71) ~[server.jar:na]
    at io.vertx.core.impl.ContextImpl.lambda$wrapTask$2(ContextImpl.java:314) ~[server.jar:na]
    at io.vertx.core.impl.ContextImpl.executeFromIO(ContextImpl.java:190) ~[server.jar:na]
    at io.vertx.core.http.impl.VertxHttpHandler.channelRead(VertxHttpHandler.java:71) ~[server.jar:na]
    at io.vertx.core.net.impl.VertxHandler.channelRead(VertxHandler.java:122) ~[server.jar:na]
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:372) ~[server.jar:na]
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:358) ~[server.jar:na]
    at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:350) ~[server.jar:na]
    at io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:293) ~[server.jar:na]
    at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:267) ~[server.jar:na]
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:372) ~[server.jar:na]
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:358) ~[server.jar:na]
    at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:350) ~[server.jar:na]
    at io.netty.handler.logging.LoggingHandler.channelRead(LoggingHandler.java:233) ~[server.jar:na]
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:372) ~[server.jar:na]
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:358) ~[server.jar:na]
    at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:350) ~[server.jar:na]
    at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1334) ~[server.jar:na]
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:372) ~[server.jar:na]
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:358) ~[server.jar:na]
    at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:926) ~[server.jar:na]
    at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:129) ~[server.jar:na]
    at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:610) ~[server.jar:na]
    at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:551) ~[server.jar:na]
    at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:465) ~[server.jar:na]
    at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:437) ~[server.jar:na]
    at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:873) ~[server.jar:na]
    at java.lang.Thread.run(Thread.java:745) ~[na:1.8.0_92]

Jez P

unread,
Jan 6, 2017, 10:20:39 AM1/6/17
to vert.x
The current vertx-pac4j is built and tested against vertx 3.3.3 - have you tried using it with 3.3.3 and found it to fail (note the vertx version in the pom.xml for vertx-pac4j)? I'm the maintainer and don't see any issues raised suggesting that it has problems so please raise one on that github repo with a suitable reproducer if you have a problem. You should note that vertx-pac4j is part of the pac4j project and not part of the vertx project, so it won't be "part of" any vertx version or supported by the core vert.x team. 

The main difference is that the underlying pac4j libraries are blocking and therefore vertx-pac4j currently wraps pac4j's APIs in executeBlocking which could limit scalability if you were doing a lot of heavy (blocking) authentications simultaneously and you would have to be prepared to tune your worker thread pool. I am working on helping pac4j build an async API under which specifically non-blocking components can be provided where suitable APIs are available.  I suspect pac4j may have a wider set of auth clients than vertx provides but I don't know how many or the range of clients offered by vert.x. 

Jez P

unread,
Jan 6, 2017, 10:28:11 AM1/6/17
to vert.x
Could you share your code please?

Shahji

unread,
Jan 7, 2017, 1:20:25 AM1/7/17
to vert.x
public void startWebApp(Handler<AsyncResult<HttpServer>> next) {
   
// Create a router object.
   
Router router = Router.router(vertx);

   
// We need cookies, sessions and request bodies
    router
.route().handler(CookieHandler.create());
    router
.route().handler(BodyHandler.create());
    router
.route().handler(SessionHandler.create(LocalSessionStore.create(vertx)));

   
OAuth2ClientOptions credentials = new OAuth2ClientOptions()
           
.setClientID(Constants.GOOGLEAPIS_CLIENT_ID)
           
.setClientSecret(Constants.GOOGLEAPIS_CLIENT_SECRET)
           
.setSite("https://accounts.google.com")
           
.setTokenPath("https://www.googleapis.com/oauth2/v3/token")
           
.setAuthorizationPath("/o/oauth2/auth");

   
// Initialize the OAuth2 Library
   
OAuth2Auth authProvider = OAuth2Auth.create(vertx, OAuth2FlowType.CLIENT, credentials);
   
// create a oauth2 handler on our root: "/"
   
OAuth2AuthHandler authHandler = OAuth2AuthHandler.create(authProvider, "http://localhost:8082");
   
// these are the scopes
    authHandler
.addAuthority("profile");
   
// setup the callback handler for receiving the Google callback
    authHandler
.setupCallback(router.get("/oauth2callback"));

   
// We need a user session handler too to make sure the user
   
// is stored in the session between requests
    router
.route().handler(UserSessionHandler.create(authProvider));

   
// Any requests to URI starting BASE_URL require login
    router
.route(Constants.BASE_URL + "/*")
           
.handler(RedirectAuthHandler.create(authProvider, "/index.html"));

    router
.route("/login").handler(authHandler);

   
// Implement logout
    router
.route("/logout").handler(context -> {
        context
.clearUser();
       
// Redirect back to the index page
        context
.response().putHeader("location", "/").setStatusCode(302).end();
   
});

    router
.route().handler(StaticHandler
           
.create()
           
.setAllowRootFileSystemAccess(true)
           
.setCachingEnabled(false)
           
.setWebRoot("webroot"));

   
// Create the HTTP server and pass the "accept" method to the request handler.
   
HttpServerOptions options = new HttpServerOptions()
           
.setPort(config().getInteger("http.port", 8080));
    vertx
.createHttpServer(options)
           
.requestHandler(router::accept)
           
.listen(
                   
// Retrieve the port from the configuration,
                   
// default to 8080.
                    config
().getInteger("http.port", 8080),
                   
next::handle
           
);
}


Shahji

unread,
Jan 7, 2017, 1:30:51 AM1/7/17
to vert.x
I haven't attempted using vertx-pac4j yet. I was playing around with vertx-auth only so far. The above exception is from running v3.3.3 of vertx modules.

How soon do you think there will be a non-blocking version of vertx-pac4j.

I noticed you have reference to a demo app here - https://github.com/pac4j/vertx-pac4j-demo

I don't see a specific example for using google' oauth2 authentication. Is there a different example/demo that I can refer to?

Jez P

unread,
Jan 7, 2017, 2:18:22 AM1/7/17
to vert.x
>> How soon do you think there will be a non-blocking version of vertx-pac4j.

No idea, I have to create an async set of pac4j APIs first before we consider migrating vertx-pac4j to the async pac4j APIs. Then we have to start implementing the non-blocking clients once we have the async APIs to build against. It's likely to be quite some time, and frankly it's also likely to need others to step forward and contribute. Are you offering to help?

>> I don't see a specific example for using google' oauth2 authentication. Is there a different example/demo that I can refer to?

The demo clearly shows how to setup vertx with pac4j and set up several pac4j clients. You should ask on the pac4j group at https://groups.google.com/forum/#!forum/pac4j-users how to set up a pac4j client for google oAuth2 if the google OIDC client as used in the demo isn't sufficient for you, and read some of the pac4j documentation. However, not every client should need a demo, it's not that hard to set them up. 

I believe the Google oAuth2 client is at https://github.com/pac4j/pac4j/blob/master/pac4j-oauth/src/main/java/org/pac4j/oauth/client/Google2Client.java - however if you have further questions relating to pac4j such as client config (which is NOT vertx-pac4j specific) you should take those to the pac4j-users group rather than create noise on the vert.x group. I read both groups so it's not like you'll be forgotten. 

Jez P

unread,
Jan 7, 2017, 2:33:21 AM1/7/17
to vert.x
I may be unfamiliar with the latest version of OAuth2OauthHandler but why have you got a handler configured with a localhost:8082 url? That doesn't feel right to me. Shouldn't your callback be exposed by the same server (8080)?

Shahji

unread,
Jan 7, 2017, 5:09:13 AM1/7/17
to vert.x
I am going to try setting up using vertx-pac4j. Will loop back with results.

As for contributing, I am a C++ programmer by profession (12 yrs) and fairly new to Java. This is my second project. That said, if you can use my help, I am more than happy to contribute.

As for the noise, the initial question was about vertx-auth because that is what I was using. However, searching online lead me to believe that the other option i.e. vertx-pac4j might be more suitable but couldn't confirm one way or the other.

Shahji

unread,
Jan 7, 2017, 5:10:46 AM1/7/17
to vert.x
The config overrides it to 8082. The parameter is the code is just the fallback/default.

Jez P

unread,
Jan 7, 2017, 5:49:02 AM1/7/17
to vert.x
Sure, but please keep any pac4j (including vertx-pac4j) related questions on the pac4j group. I'm not sure either's "more suitable" unless you really need non-blocking authentication. I'd say whichever you find easier to get working. In addition Paulo may well be able to advise on the problem you're seeing with the oAuth handler.

As regards contributions, I'm still experimenting with the best general approach to the API and also providing a quick migration to it, and my spare time is very limited, so you have plenty of time to learn your way round pac4j before I propose the API and discuss with Jérôme (the project lead on pac4j), I don't think there's much need for general contributions in the interim, but there will be when we start porting clients, and some authorisers, to the new API. Realistically I don't see much progress for the next few months (I literally have about 15 - 20 mins a day to work on it, at best).
Reply all
Reply to author
Forward
0 new messages