Skip to content

Instantly share code, notes, and snippets.

@simon04
Created December 30, 2021 14:52
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save simon04/c302d738e7f24b90267a1d341d720d6d to your computer and use it in GitHub Desktop.
Save simon04/c302d738e7f24b90267a1d341d720d6d to your computer and use it in GitHub Desktop.
Jetty Authenticator for JWT/JWKS
package at.tbbm.manual_input.util;
import com.auth0.jwk.Jwk;
import com.auth0.jwk.JwkException;
import com.auth0.jwk.JwkProvider;
import com.auth0.jwk.UrlJwkProvider;
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.interfaces.DecodedJWT;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.security.Authenticator;
import org.eclipse.jetty.security.DefaultUserIdentity;
import org.eclipse.jetty.security.UserAuthentication;
import org.eclipse.jetty.server.Authentication;
import org.eclipse.jetty.server.UserIdentity;
import javax.security.auth.Subject;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.net.URL;
import java.security.Principal;
import java.security.interfaces.RSAPublicKey;
import java.util.regex.Pattern;
public record JwtAuthenticator(Pattern skipPattern, JwkProvider provider) implements Authenticator {
public JwtAuthenticator(Pattern skipPattern, URL jwks) {
this(skipPattern, new UrlJwkProvider(jwks));
}
@Override
public String getAuthMethod() {
return "BEARER";
}
@Override
public void prepareRequest(ServletRequest request) {
}
@Override
public void setConfiguration(AuthConfiguration configuration) {
}
@Override
public Authentication validateRequest(ServletRequest request, ServletResponse response, boolean mandatory) {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
String requestPath = httpServletRequest.getRequestURI().substring(httpServletRequest.getContextPath().length());
if (skipPattern.matcher(requestPath).matches()) {
return Authentication.NOT_CHECKED;
}
try {
DecodedJWT jwt = decodedJWT(httpServletRequest);
verify(jwt);
return buildUserAuthentication(jwt);
} catch (JwkException | JWTVerificationException e) {
try {
((HttpServletResponse) response).sendError(HttpServletResponse.SC_UNAUTHORIZED);
} catch (IOException e2) {
throw new UncheckedIOException(e2);
}
return Authentication.UNAUTHENTICATED;
}
}
private DecodedJWT decodedJWT(HttpServletRequest httpServletRequest) {
String token = httpServletRequest.getHeader(HttpHeader.AUTHORIZATION.asString()).substring("Bearer ".length());
return JWT.decode(token);
}
private void verify(DecodedJWT jwt) throws JwkException {
Jwk jwk = provider.get(jwt.getKeyId());
Algorithm algorithm = Algorithm.RSA256((RSAPublicKey) jwk.getPublicKey(), null);
JWT.require(algorithm).build().verify(jwt);
}
private UserAuthentication buildUserAuthentication(DecodedJWT jwt) {
Principal email = () -> jwt.getClaims().get("email").asString();
UserIdentity identity = new DefaultUserIdentity(new Subject(), email, new String[]{"role1", "role2"});
return new UserAuthentication(getAuthMethod(), identity);
}
@Override
public boolean secureResponse(ServletRequest request, ServletResponse response, boolean mandatory, Authentication.User validatedUser) {
return true;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment