Pac4j indirect client selection

332 Views Asked by At

I would like to give users choice between a few (indirect) authentication options, similar to what stackoverflow and many other website offer. There would be a form login as well as OIDC options. I can do this by setting different endpoints protected by different indirect clients just like in jee-pac4j-demo, however I'm not sure how I can make handling of the original requested URL work.

Here's my configuration:

    <!-- form authentication -->
    <filter>
      <filter-name>FormFilter</filter-name>
      <filter-class>org.pac4j.jee.filter.SecurityFilter</filter-class>
      <init-param>
        <param-name>configFactory</param-name>
        <param-value>com.mycompany.authentication.Pac4jConfigFactory</param-value>
      </init-param>
      <init-param>
        <param-name>clients</param-name>
        <param-value>FormClient</param-value>
      </init-param>
      <init-param>
        <param-name>authorizers</param-name>
        <param-value>isAuthenticated</param-value>
      </init-param>
    </filter>
    <filter-mapping>
      <filter-name>FormFilter</filter-name>
      <url-pattern>/formLogin</url-pattern>
      <url-pattern>/private-url</url-pattern>
      <dispatcher>REQUEST</dispatcher>
    </filter-mapping>

    <!-- google oidc authentication -->
    <filter>
      <filter-name>GoogleOidcFilter</filter-name>
      <filter-class>org.pac4j.jee.filter.SecurityFilter</filter-class>
      <init-param>
        <param-name>configFactory</param-name>
        <param-value>com.mycompany.authentication.Pac4jConfigFactory</param-value>
      </init-param>
      <init-param>
        <param-name>clients</param-name>
        <param-value>GoogleOidcClient</param-value>
      </init-param>
      <init-param>
        <param-name>authorizers</param-name>
        <param-value>isAuthenticated</param-value>
      </init-param>
    </filter>
    <filter-mapping>
      <filter-name>GoogleOidcFilter</filter-name>
      <url-pattern>/googleOidcLogin</url-pattern>
      <dispatcher>REQUEST</dispatcher>
    </filter-mapping>

If the user calls a protected url (/private-url), he would be redirected to my FormClient login page where I have the username/password fields as well as a link to /googleOidcLogin.

If the user logs in with username/password everything is fine and the initial request is served after authentication. However, if the user clicks on the googleOidcLogin button, now that page is considered the initial request and it is the one restored after the authentication.

How can this be implemented with pac4j?

1

There are 1 best solutions below

0
On BEST ANSWER

Ok, I found a way to implement this.

Since we're always going to FormClient before we can choose any of the the other IndirectClients, all we need to do is instruct the other IndirectClients not to save the initial request, and therefore not overwriting the one recorded by FormClient. This way, when the other IndirectClient will restore the initial url it will restore the one saved by FormClient.

For that we need a custom implementation of SavedRequestHandler that only stores original url for the FormClient.

the FormClientOnlySavedRequestHandler:

public class FormClientOnlySavedRequestHandler extends DefaultSavedRequestHandler {

    @Override
    public void save(WebContext webContext, SessionStore sessionStore) {
        // if oidcLogin, don't save anything, we will reuse the previous one (from form client)
        if (webContext.getPath().endsWith("/oidcLogin"))
            return;

        super.save(webContext, sessionStore);
    }
    
}

And you register it in your ConfigFactory:

        final Config config = new Config(clients);

        DefaultCallbackLogic callbackLogic = new DefaultCallbackLogic();
        callbackLogic.setSavedRequestHandler(new FormClientOnlySavedRequestHandler());
        config.setCallbackLogic(callbackLogic);

        DefaultSecurityLogic securityLogic = new DefaultSecurityLogic();
        securityLogic.setSavedRequestHandler(new FormClientOnlySavedRequestHandler());
        config.setSecurityLogic(securityLogic);

        return config;