Skip to content

Commit

Permalink
ChainAuthHandler should invoke postAuthentication (#2582)
Browse files Browse the repository at this point in the history
See #2408

Some auth handlers have postAuthentication methods that must be invoked.
For example, FormLoginHandler may have to redirect the client after authentication.

ChainAuthHandler was not able to invoke the post-authentication method of the handler that authenticated the user.

Signed-off-by: Thomas Segismont <[email protected]>
  • Loading branch information
tsegismont committed Mar 20, 2024
1 parent 3f38d9b commit f856512
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@
import io.vertx.ext.auth.User;
import io.vertx.ext.auth.authentication.AuthenticationProvider;
import io.vertx.ext.web.RoutingContext;
import io.vertx.ext.web.handler.*;
import io.vertx.ext.web.handler.AuthenticationHandler;
import io.vertx.ext.web.handler.ChainAuthHandler;
import io.vertx.ext.web.handler.HttpException;

import java.util.ArrayList;
import java.util.List;
Expand All @@ -17,6 +19,8 @@ public class ChainAuthHandlerImpl extends AuthenticationHandlerImpl<Authenticati

private static final Logger LOG = LoggerFactory.getLogger(ChainAuthHandler.class);

private static final String HANDLER_IDX = "__vertx.auth.chain.idx";

private final List<AuthenticationHandlerInternal> handlers = new ArrayList<>();
private final boolean all;

Expand Down Expand Up @@ -48,7 +52,7 @@ public synchronized ChainAuthHandler add(AuthenticationHandler other) {

@Override
public void authenticate(RoutingContext context, Handler<AsyncResult<User>> handler) {
if (handlers.size() == 0) {
if (handlers.isEmpty()) {
handler.handle(Future.failedFuture("No providers in the auth chain."));
} else {
// iterate all possible authN
Expand Down Expand Up @@ -108,6 +112,7 @@ private void iterate(final int idx, final RoutingContext ctx, User result, Throw
iterate(idx + 1, ctx, res.result(), null, handler);
} else {
// a single success is enough to signal the end of the validation
ctx.put(HANDLER_IDX, idx);
handler.handle(Future.succeededFuture(res.result()));
}
});
Expand All @@ -128,4 +133,15 @@ public boolean setAuthenticateHeader(RoutingContext ctx) {
}
return added;
}

@Override
public void postAuthentication(RoutingContext ctx) {
if (all) {
// Can't invoke post-processing for all handlers
ctx.next();
} else {
int idx = ctx.get(HANDLER_IDX);
handlers.get(idx).postAuthentication(ctx);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package io.vertx.ext.web.handler;

import io.vertx.core.MultiMap;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.http.HttpHeaders;
import io.vertx.core.http.HttpMethod;
import io.vertx.ext.auth.authentication.AuthenticationProvider;
import io.vertx.ext.auth.htdigest.HtdigestAuth;
Expand Down Expand Up @@ -91,4 +94,42 @@ public void testWithMultipleWWWAuthenticate() throws Exception {
assertTrue(headers.get(1).startsWith("Digest realm=\"[email protected]\""));
},401, "Unauthorized", "Unauthorized");
}

@Test
public void testWithPostAuthenticationAction() throws Exception {
router.clear();

router.route().handler(SessionHandler.create(LocalSessionStore.create(vertx)));

chain = ChainAuthHandler.any()
// Direct login is implemented as a post-authentication action
.add(FormLoginHandler.create(authProvider).setDirectLoggedInOKURL("/welcome"));

router.post("/login")
.handler(BodyHandler.create())
.handler(chain);

testRequest(HttpMethod.POST, "/login", req -> {
String boundary = "dLV9Wyq26L_-JQxk6ferf-RT153LhOO";
Buffer buffer = Buffer.buffer();
String str =
"--" + boundary + "\r\n" +
"Content-Disposition: form-data; name=\"" + FormLoginHandler.DEFAULT_USERNAME_PARAM + "\"\r\n\r\ntim\r\n" +
"--" + boundary + "\r\n" +
"Content-Disposition: form-data; name=\"" + FormLoginHandler.DEFAULT_PASSWORD_PARAM + "\"\r\n\r\ndelicious:sausages\r\n" +
"--" + boundary + "--\r\n";
buffer.appendString(str);
req.putHeader("content-length", String.valueOf(buffer.length()));
req.putHeader("content-type", "multipart/form-data; boundary=" + boundary);
req.write(buffer);
}, resp -> {
MultiMap headers = resp.headers();
// session will be upgraded
String setCookie = headers.get("set-cookie");
assertNotNull(setCookie);
// client will be redirected
assertTrue(headers.contains(HttpHeaders.LOCATION, "/welcome", false));
}, 302, "Found", null);
}

}

0 comments on commit f856512

Please sign in to comment.