use of io.vertx.ext.auth.htdigest.HtdigestCredentials in project vertx-web by vert-x3.
the class WebClientTest method testDigestAuthentication.
@Test
public void testDigestAuthentication() throws Exception {
testRequest(client -> client.get("somehost", "/dir/index.html").authentication(// like on wikipedia
new HtdigestCredentials("Mufasa", "Circle Of Life").applyHttpChallenge("Digest realm=\"testrealm@host.com\", qop=\"auth,auth-int\", nonce=\"dcd98b7102dd2f0e8b11d0f600bfb0c093\", opaque=\"5ccc069c403ebaf9f0171e9517f40e41\"", HttpMethod.GET, "/dir/index.html", 1, "0a4f113b")), req -> {
String auth = req.headers().get(HttpHeaders.AUTHORIZATION);
String expected = "Digest username=\"Mufasa\", realm=\"testrealm@host.com\", nonce=\"dcd98b7102dd2f0e8b11d0f600bfb0c093\", uri=\"/dir/index.html\", qop=auth, nc=1, cnonce=\"0a4f113b\", response=\"95c727b8ed724ea2be8e9318e0e4f619\", opaque=\"5ccc069c403ebaf9f0171e9517f40e41\"";
assertEquals("Was expecting authorization header to contain a digest authentication string", expected, auth);
});
}
use of io.vertx.ext.auth.htdigest.HtdigestCredentials in project vertx-web by vert-x3.
the class DigestAuthHandlerImpl method authenticate.
@Override
public void authenticate(RoutingContext context, Handler<AsyncResult<User>> handler) {
// clean up nonce
long now = System.currentTimeMillis();
if (now - lastExpireRun > nonceExpireTimeout / 2) {
Set<String> toRemove = new HashSet<>();
nonces.forEach((String key, Nonce n) -> {
if (n != null && n.createdAt + nonceExpireTimeout < now) {
toRemove.add(key);
}
});
for (String n : toRemove) {
nonces.remove(n);
}
lastExpireRun = now;
}
parseAuthorization(context, parseAuthorization -> {
if (parseAuthorization.failed()) {
handler.handle(Future.failedFuture(parseAuthorization.cause()));
return;
}
final HtdigestCredentials authInfo = new HtdigestCredentials();
try {
// Split the parameters by comma.
String[] tokens = SPLITTER.split(parseAuthorization.result());
// Parse parameters.
int i = 0;
int len = tokens.length;
while (i < len) {
// Strip quotes and whitespace.
Matcher m = PARSER.matcher(tokens[i]);
if (m.find()) {
switch(m.group(1)) {
case "algorithm":
authInfo.setAlgorithm(m.group(2));
break;
case "cnonce":
authInfo.setCnonce(m.group(2));
break;
case "method":
authInfo.setMethod(m.group(2));
break;
case "nc":
authInfo.setNc(m.group(2));
break;
case "nonce":
authInfo.setNonce(m.group(2));
break;
case "opaque":
authInfo.setOpaque(m.group(2));
break;
case "qop":
authInfo.setQop(m.group(2));
break;
case "realm":
authInfo.setRealm(m.group(2));
break;
case "response":
authInfo.setResponse(m.group(2));
break;
case "uri":
authInfo.setUri(m.group(2));
break;
case "username":
authInfo.setUsername(m.group(2));
break;
default:
LOG.info("Uknown parameter: " + m.group(1));
}
}
++i;
}
final String nonce = authInfo.getNonce();
// check for expiration
if (!nonces.containsKey(nonce)) {
handler.handle(Future.failedFuture(UNAUTHORIZED));
return;
}
// check for nonce counter (prevent replay attack)
if (authInfo.getQop() != null) {
int nc = Integer.parseInt(authInfo.getNc(), 16);
final Nonce n = nonces.get(nonce);
if (nc <= n.count) {
handler.handle(Future.failedFuture(UNAUTHORIZED));
return;
}
// update the nounce count
nonces.put(nonce, new Nonce(n.createdAt, nc));
}
} catch (RuntimeException e) {
handler.handle(Future.failedFuture(e));
return;
}
// validate the opaque value
final Session session = context.session();
if (session != null) {
String opaque = (String) session.data().get("opaque");
if (opaque != null && !opaque.equals(authInfo.getOpaque())) {
handler.handle(Future.failedFuture(UNAUTHORIZED));
return;
}
}
// we now need to pass some extra info
authInfo.setMethod(context.request().method().name());
authProvider.authenticate(authInfo, authn -> {
if (authn.failed()) {
handler.handle(Future.failedFuture(new HttpException(401, authn.cause())));
} else {
handler.handle(authn);
}
});
});
}
Aggregations