This is my JWTAuthenticationMechanism :
package xxx;
import io.jsonwebtoken.ExpiredJwtException;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.security.enterprise.AuthenticationStatus;
import jakarta.security.enterprise.authentication.mechanism.http.HttpAuthenticationMechanism;
import jakarta.security.enterprise.authentication.mechanism.http.HttpMessageContext;
import jakarta.security.enterprise.authentication.mechanism.http.RememberMe;
import jakarta.security.enterprise.credential.Credential;
import jakarta.security.enterprise.credential.UsernamePasswordCredential;
import jakarta.security.enterprise.identitystore.CredentialValidationResult;
import jakarta.security.enterprise.identitystore.DatabaseIdentityStoreDefinition;
import jakarta.security.enterprise.identitystore.IdentityStoreHandler;
import jakarta.security.enterprise.identitystore.Pbkdf2PasswordHash;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import javax.inject.Inject;
import static ir.vinapacs.configuration.security.jwt.Constants.*;
@DatabaseIdentityStoreDefinition(
dataSourceLookup = "${'java:jboss/datasources/MyDBJNDIDB'}",
callerQuery = "#{'select user_pss from EntityUser where user_login = ?'}",
groupsQuery = "select group_name from caller_groups where caller_name = ?",
hashAlgorithm = Pbkdf2PasswordHash.class,
priorityExpression = "#{100}",
hashAlgorithmParameters = {
"Pbkdf2PasswordHash.Iterations=1000",
"Pbkdf2PasswordHash.Algorithm=PBKDF2WithHmacSHA512",
"Pbkdf2PasswordHash.SaltSizeBytes=32",
"Pbkdf2PasswordHash.KeySizeBytes=32"
}
)
@RememberMe(
cookieMaxAgeSeconds = REMEMBERME_VALIDITY_SECONDS,
isRememberMeExpression = "self.isRememberMe(httpMessageContext)"
)
@ApplicationScoped
public class JWTAuthenticationMechanism implements HttpAuthenticationMechanism {
private static final Logger LOGGER = LogManager.getRootLogger();
/**
* Access to the
* IdentityStore(AuthenticationIdentityStore,AuthorizationIdentityStore) is
* abstracted by the IdentityStoreHandler to allow for multiple identity
* stores to logically act as a single IdentityStore
*/
@Inject
private IdentityStoreHandler identityStoreHandler;
@Inject
private TokenProvider tokenProvider;
@Override
public AuthenticationStatus validateRequest(HttpServletRequest request, HttpServletResponse response, HttpMessageContext context) {
LOGGER.info(request.getRequestURI());
// Get the (caller) name and password from the request
// NOTE: This is for the smallest possible example only. In practice
// putting the password in a request query parameter is highly insecure
String name = request.getParameter("name");
String password = request.getParameter("password");
System.out.println("login : " + name + "::" + password);
String token = extractToken(context);
if (name != null && password != null) {
LOGGER.info(String.valueOf(name + ":" + password));
// validation of the credential using the identity store
CredentialValidationResult result = identityStoreHandler.validate(new UsernamePasswordCredential(name, password));
System.out.println("result:" + result);
if (result.getStatus() == CredentialValidationResult.Status.VALID) {
// Communicate the details of the authenticated user to the container and return SUCCESS.
return createToken(result, context);
}
// if the authentication failed, we return the unauthorized status in the http response
return context.responseUnauthorized();
} else if (token != null) {
// validation of the jwt credential
return validateToken(token, context);
} else if (context.isProtected()) {
// A protected resource is a resource for which a constraint has been defined.
// if there are no credentials and the resource is protected, we response with unauthorized status
return context.responseUnauthorized();
}
// there are no credentials AND the resource is not protected,
// SO Instructs the container to "do nothing"
return context.doNothing();
}
/**
* To validate the JWT token e.g Signature check, JWT claims
* check(expiration) etc
*
* @param token The JWT access tokens
* @param context
* @return the AuthenticationStatus to notify the container
*/
private AuthenticationStatus validateToken(String token, HttpMessageContext context) {
try {
if (tokenProvider.validateToken(token)) {
JWTCredential credential = tokenProvider.getCredential(token);
return context.notifyContainerAboutLogin(credential.getPrincipal(), credential.getAuthorities());
}
// if token invalid, response with unauthorized status
return context.responseUnauthorized();
} catch (ExpiredJwtException eje) {
// LOGGER.log(Level.INFO, "Security exception for user {0} - {1}", new String[]{eje.getClaims().getSubject(), eje.getMessage()});
LOGGER.info(String.valueOf("Security exception for user " + eje.getClaims().getSubject() + " - " + eje.getMessage()));
return context.responseUnauthorized();
}
}
/**
* Create the JWT using CredentialValidationResult received from
* IdentityStoreHandler
*
* @param result the result from validation of UsernamePasswordCredential
* @param context
* @return the AuthenticationStatus to notify the container
*/
private AuthenticationStatus createToken(CredentialValidationResult result, HttpMessageContext context) {
if (!isRememberMe(context)) {
String jwt = tokenProvider.createToken(result.getCallerPrincipal().getName(), result.getCallerGroups(), false);
context.getResponse().setHeader(AUTHORIZATION_HEADER, BEARER + jwt);
}
return context.notifyContainerAboutLogin(result.getCallerPrincipal(), result.getCallerGroups());
}
/**
* To extract the JWT from Authorization HTTP header
*
* @param context
* @return The JWT access tokens
*/
private String extractToken(HttpMessageContext context) {
String authorizationHeader = context.getRequest().getHeader(AUTHORIZATION_HEADER);
if (authorizationHeader != null && authorizationHeader.startsWith(BEARER)) {
String token = authorizationHeader.substring(BEARER.length(), authorizationHeader.length());
return token;
}
return null;
}
/**
* this function invoked using RememberMe.isRememberMeExpression EL
* expression
*
* @param context
* @return The remember me flag
*/
public Boolean isRememberMe(HttpMessageContext context) {
return Boolean.valueOf(context.getRequest().getParameter("rememberme"));
}
}
And :
package xxxx;
import jakarta.enterprise.context.RequestScoped;
import jakarta.security.enterprise.credential.Credential;
import jakarta.security.enterprise.credential.UsernamePasswordCredential;
import jakarta.security.enterprise.identitystore.CredentialValidationResult;
import jakarta.security.enterprise.identitystore.IdentityStore;
import javax.annotation.PostConstruct;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import static jakarta.security.enterprise.identitystore.CredentialValidationResult.INVALID_RESULT;
import static jakarta.security.enterprise.identitystore.CredentialValidationResult.NOT_VALIDATED_RESULT;
import static jakarta.security.enterprise.identitystore.IdentityStore.ValidationType.VALIDATE;
import static java.util.Collections.singleton;
@RequestScoped
public class AuthenticationIdentityStore implements IdentityStore {
private Map<String, String> callerToPassword;
@PostConstruct
public void init() {
callerToPassword = new HashMap<>();
callerToPassword.put("payara", "fish");
callerToPassword.put("duke", "secret");
}
@Override
public CredentialValidationResult validate(Credential credential) {
CredentialValidationResult result;
if (credential instanceof UsernamePasswordCredential) {
UsernamePasswordCredential usernamePassword = (UsernamePasswordCredential) credential;
String expectedPW = callerToPassword.get(usernamePassword.getCaller());
if (expectedPW != null && expectedPW.equals(usernamePassword.getPasswordAsString())) {
result = new CredentialValidationResult(usernamePassword.getCaller());
} else {
result = INVALID_RESULT;
}
} else {
result = NOT_VALIDATED_RESULT;
}
return result;
}
@Override
public Set<ValidationType> validationTypes() {
return singleton(VALIDATE);
}
/*@Override
public int priority() {
return 100;
}*/
}
my pom is :
<!-- Jakarta EE 10 -->
<dependency>
<groupId>jakarta.platform</groupId>
<artifactId>jakarta.jakartaee-api</artifactId>
<version>${jakartaEE.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>jakarta.mvc</groupId>
<artifactId>jakarta.mvc-api</artifactId>
<version>${jakartaMVC.version}</version>
<!-- <scope>provided</scope>-->
</dependency>
<dependency>
<groupId>jakarta.activation</groupId>
<artifactId>jakarta.activation-api</artifactId>
<version>2.1.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.glassfish.jersey.media/jersey-media-multipart -->
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-multipart</artifactId>
<version>3.1.5</version>
</dependency>
<!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt-api -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.12.3</version>
</dependency>
<!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt-impl -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.12.3</version>
<scope>runtime</scope>
</dependency>
after deploy i get this exception:
ERROR [io.undertow.request] (default task-2) UT005023: Exception handling request to /VinaServer/api/j_security_check: java.lang.NullPointerException: Cannot invoke "jakarta.security.enterprise.identitystore.IdentityStoreHandler.validate(jakarta.security.enterprise.credential.Credential)" because "this.identityStoreHandler" is null
Why this exception is thrown?