package com.jetdrone.vertx.yoke.middleware;
import com.jetdrone.vertx.yoke.Middleware;
import com.jetdrone.vertx.yoke.Yoke;
import com.jetdrone.vertx.yoke.core.YokeException;
import io.vertx.core.http.HttpMethod;
import org.jetbrains.annotations.NotNull;
import io.vertx.core.Handler;
import io.vertx.core.json.JsonObject;
import java.util.regex.Pattern;
public class JWT extends Middleware {
private static final Pattern BEARER = Pattern.compile("^Bearer$", Pattern.CASE_INSENSITIVE);
public interface JWTHandler {
public void handle(JsonObject token, Handler<Object> result);
}
private final String skip;
private com.jetdrone.vertx.yoke.security.JWT jwt;
private final JWTHandler handler;
public JWT() {
this.skip = null;
this.handler = null;
}
public JWT(final @NotNull String skip) {
this.skip = skip;
this.handler = null;
}
public JWT(final @NotNull String skip, final @NotNull JWTHandler handler) {
this.skip = skip;
this.handler = handler;
}
public JWT(final @NotNull JWTHandler handler) {
this.skip = null;
this.handler = handler;
}
@Override
public Middleware init(@NotNull final Yoke yoke, @NotNull final String mount) {
super.init(yoke, mount);
jwt = new com.jetdrone.vertx.yoke.security.JWT(yoke.security());
return this;
}
@Override
public void handle(@NotNull final YokeRequest request, @NotNull final Handler<Object> next) {
String token = null;
if (HttpMethod.OPTIONS.equals(request.method()) && request.getHeader("access-control-request-headers") != null) {
for (String ctrlReq : request.getHeader("access-control-request-headers").split(",")) {
if (ctrlReq.contains("authorization")) {
next.handle(null);
return;
}
}
}
if (skip != null && skip.contains(request.normalizedPath())) {
next.handle(null);
return;
}
final String authorization = request.getHeader("authorization");
if (authorization != null) {
String[] parts = authorization.split(" ");
if (parts.length == 2) {
final String scheme = parts[0],
credentials = parts[1];
if (BEARER.matcher(scheme).matches()) {
token = credentials;
}
} else {
next.handle(new YokeException(401, "Format is Authorization: Bearer [token]"));
return;
}
} else {
next.handle(new YokeException(401, "No Authorization header was found"));
return;
}
try {
final JsonObject jwtToken = jwt.decode(token);
// All dates in JWT are of type NumericDate
// a NumericDate is: numeric value representing the number of seconds from 1970-01-01T00:00:00Z UTC until
// the specified UTC date/time, ignoring leap seconds
final long now = System.currentTimeMillis() / 1000;
if (jwtToken.containsKey("iat")) {
Long iat = jwtToken.getLong("iat");
// issue at must be in the past
if (iat > now) {
next.handle(new YokeException(401, "Invalid Token!"));
return;
}
}
if (jwtToken.containsKey("nbf")) {
Long nbf = jwtToken.getLong("nbf");
// not before must be after now
if (nbf > now) {
next.handle(new YokeException(401, "Invalid Token!"));
return;
}
}
if (jwtToken.containsKey("exp")) {
Long exp = jwtToken.getLong("exp");
// expires must be after now
if (now > exp) {
next.handle(new YokeException(401, "Invalid Token!"));
return;
}
}
request.put("jwt", jwtToken);
if (handler == null) {
next.handle(null);
return;
}
handler.handle(jwtToken, next);
} catch (RuntimeException e) {
next.handle(new YokeException(401, e));
}
}
}