diff --git a/README.md b/README.md
index a56875c3..fe78dc38 100644
--- a/README.md
+++ b/README.md
@@ -24,6 +24,18 @@ e.g
or any other wildfly location
+## secret.env
+
+The project/application needs a secret.env file with the following variables set in order for authentication and tests to work
+
+```
+SECRET_ADMIN_NAME
+SECRET_JWT_HASH
+SECRET_DEFAULT_PW
+```
+
+For convenience the [plugin](https://plugins.jetbrains.com/plugin/7861-envfile) is recommended for reading the secret.env when tests are executed via IntelliJ
+
## Build Project and deploy application
- *In order for all used relative paths to work
diff --git a/gamertrack-IntegrationTest/src/test/java/com/gepardec/rest/impl/AuthResourceImplIT.java b/gamertrack-IntegrationTest/src/test/java/com/gepardec/rest/impl/AuthResourceImplIT.java
index a1480d95..323d9dc0 100644
--- a/gamertrack-IntegrationTest/src/test/java/com/gepardec/rest/impl/AuthResourceImplIT.java
+++ b/gamertrack-IntegrationTest/src/test/java/com/gepardec/rest/impl/AuthResourceImplIT.java
@@ -2,6 +2,7 @@
import com.gepardec.rest.model.command.AuthCredentialCommand;
import com.gepardec.rest.model.command.CreateUserCommand;
+import com.gepardec.rest.model.command.ValidateTokenCommand;
import io.github.cdimascio.dotenv.Dotenv;
import io.restassured.RestAssured;
import io.restassured.filter.log.LogDetail;
@@ -95,4 +96,53 @@ public void createTestUserWithAuthHeader() {
.path("token");
usedUserTokens.add(token);
}
+
+ @Test
+ public void ensureValidateTokenForInvalidTokenReturnsUnauthorized() {
+ ValidateTokenCommand validateTokenCommand = new ValidateTokenCommand("aksldfjalsdfjalskdjfaksdl.asdfasddfasdf.asdfsadff");
+ with().when()
+ .contentType("application/json")
+ .body(validateTokenCommand)
+ .post("/auth/validate")
+ .then()
+ .statusCode(401);
+ }
+
+ @Test
+ public void ensureValidateTokenForNotProvidedOrNullTokenReturnsUnauthorized() {
+ ValidateTokenCommand validateTokenCommand = new ValidateTokenCommand(null);
+ with().when()
+ .contentType("application/json")
+ .body(validateTokenCommand)
+ .post("/auth/validate")
+ .then()
+ .statusCode(401);
+ }
+
+ @Test
+ public void ensureValidateTokenForValidTokenReturns200Ok() {
+ //Login to get valid token
+ String authHeader = with().when()
+ .contentType("application/json")
+ .body(new AuthCredentialCommand(SECRET_ADMIN_NAME, SECRET_DEFAULT_PW))
+ .headers("Content-Type", ContentType.JSON,
+ "Accept", ContentType.JSON)
+ .request("POST", "/auth/login")
+ .then()
+ .statusCode(200)
+ .extract()
+ .header("Authorization");
+
+ var token = authHeader.replace("Bearer ", "");
+
+
+ //Validate token
+ ValidateTokenCommand validateTokenCommand = new ValidateTokenCommand(token);
+ with().when()
+ .contentType("application/json")
+ .body(validateTokenCommand)
+ .post("/auth/validate")
+ .then()
+ .statusCode(200);
+ }
}
diff --git a/gamertrack-application/src/main/java/com/gepardec/rest/api/AuthResource.java b/gamertrack-application/src/main/java/com/gepardec/rest/api/AuthResource.java
index 0499d1a1..0909baa2 100644
--- a/gamertrack-application/src/main/java/com/gepardec/rest/api/AuthResource.java
+++ b/gamertrack-application/src/main/java/com/gepardec/rest/api/AuthResource.java
@@ -1,6 +1,7 @@
package com.gepardec.rest.api;
import com.gepardec.rest.model.command.AuthCredentialCommand;
+import com.gepardec.rest.model.command.ValidateTokenCommand;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
@@ -16,4 +17,8 @@ public interface AuthResource {
@POST
@Path("/login")
Response login(AuthCredentialCommand authCredentialCommand);
+
+ @POST
+ @Path("/validate")
+ Response validateToken(ValidateTokenCommand token);
}
diff --git a/gamertrack-application/src/main/java/com/gepardec/rest/impl/AuthResourceImpl.java b/gamertrack-application/src/main/java/com/gepardec/rest/impl/AuthResourceImpl.java
index 24353b5a..dce91647 100644
--- a/gamertrack-application/src/main/java/com/gepardec/rest/impl/AuthResourceImpl.java
+++ b/gamertrack-application/src/main/java/com/gepardec/rest/impl/AuthResourceImpl.java
@@ -3,6 +3,7 @@
import com.gepardec.core.services.AuthService;
import com.gepardec.rest.api.AuthResource;
import com.gepardec.rest.model.command.AuthCredentialCommand;
+import com.gepardec.rest.model.command.ValidateTokenCommand;
import com.gepardec.rest.model.mapper.AuthCredentialRestMapper;
import com.gepardec.security.JwtUtil;
import jakarta.enterprise.context.RequestScoped;
@@ -36,4 +37,11 @@ public Response login(AuthCredentialCommand authCredentialCommand) {
return Response.status(Response.Status.UNAUTHORIZED).entity("Invalid credentials").build();
}
}
+
+ @Override
+ public Response validateToken(ValidateTokenCommand tokenCmd) {
+ if (tokenCmd.token() != null && authService.isTokenValid(tokenCmd.token())) return Response.ok().build();
+
+ return Response.status(Response.Status.UNAUTHORIZED).entity("Invalid token").build();
+ }
}
diff --git a/gamertrack-application/src/main/java/com/gepardec/rest/model/command/ValidateTokenCommand.java b/gamertrack-application/src/main/java/com/gepardec/rest/model/command/ValidateTokenCommand.java
new file mode 100644
index 00000000..11904665
--- /dev/null
+++ b/gamertrack-application/src/main/java/com/gepardec/rest/model/command/ValidateTokenCommand.java
@@ -0,0 +1,5 @@
+package com.gepardec.rest.model.command;
+
+public record ValidateTokenCommand(String token) {
+
+}
diff --git a/gamertrack-domain/pom.xml b/gamertrack-domain/pom.xml
index d1287a6b..ffede518 100644
--- a/gamertrack-domain/pom.xml
+++ b/gamertrack-domain/pom.xml
@@ -71,6 +71,35 @@
+
+ org.codehaus.mojo
+ properties-maven-plugin
+ 1.2.1
+
+
+ initialize
+
+ read-project-properties
+
+
+ true
+
+ ${project.parent.basedir}/secret.env
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ 3.5.2
+
+
+ ${SECRET_JWT_HASH}
+
+
+
\ No newline at end of file
diff --git a/gamertrack-domain/src/main/java/com/gepardec/core/services/AuthService.java b/gamertrack-domain/src/main/java/com/gepardec/core/services/AuthService.java
index 806b4947..d28b56e2 100644
--- a/gamertrack-domain/src/main/java/com/gepardec/core/services/AuthService.java
+++ b/gamertrack-domain/src/main/java/com/gepardec/core/services/AuthService.java
@@ -5,4 +5,5 @@
public interface AuthService {
boolean authenticate(AuthCredential credential);
boolean createDefaultUserIfNotExists();
+ boolean isTokenValid(String token);
}
diff --git a/gamertrack-domain/src/main/java/com/gepardec/impl/service/AuthServiceImpl.java b/gamertrack-domain/src/main/java/com/gepardec/impl/service/AuthServiceImpl.java
index f3f1c290..6b6800d5 100644
--- a/gamertrack-domain/src/main/java/com/gepardec/impl/service/AuthServiceImpl.java
+++ b/gamertrack-domain/src/main/java/com/gepardec/impl/service/AuthServiceImpl.java
@@ -6,6 +6,8 @@
import com.gepardec.model.AuthCredential;
import com.gepardec.security.JwtUtil;
import io.github.cdimascio.dotenv.Dotenv;
+import io.jsonwebtoken.JwtException;
+import io.jsonwebtoken.Jwts;
import jakarta.ejb.Stateless;
import jakarta.inject.Inject;
import jakarta.transaction.Transactional;
@@ -78,4 +80,16 @@ public boolean createDefaultUserIfNotExists() {
return false; //user was not created
}
}
+
+ @Override
+ public boolean isTokenValid(String token) {
+ boolean isValid = false;
+ try {
+ Jwts.parser().setSigningKey(jwtUtil.generateKey()).build().parseClaimsJws(token);
+ isValid = true;
+ } catch (JwtException e) {
+ log.error("Token validation failed {}", e.getMessage());
+ }
+ return isValid;
+ }
}
diff --git a/gamertrack-domain/src/test/java/com/gepardec/impl/service/AuthServiceImplTest.java b/gamertrack-domain/src/test/java/com/gepardec/impl/service/AuthServiceImplTest.java
index e102b4b5..9ec65da3 100644
--- a/gamertrack-domain/src/test/java/com/gepardec/impl/service/AuthServiceImplTest.java
+++ b/gamertrack-domain/src/test/java/com/gepardec/impl/service/AuthServiceImplTest.java
@@ -13,7 +13,7 @@
import java.util.Optional;
-import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;
@@ -29,6 +29,7 @@ public class AuthServiceImplTest {
@Mock
TokenService tokenService;
+
@Test
void ensureCreateDefaultUserIfNotExistsCreatesDefaultUser() {
when(authRepository.findByUsername(any())).thenReturn(Optional.empty());
@@ -68,4 +69,22 @@ void ensureAuthenticateReturnsTrueWhenCredentialsCorrect() {
assertEquals(authService.authenticate(new AuthCredential("admin", "CorrectPW")), true);
}
+ @Test
+ void ensureIsTokenValidReturnsFalseIfTokenIsNull() {
+ assertFalse(authService.isTokenValid(null));
+ }
+
+ @Test
+ void ensureIsTokenValidReturnsFalseIfTokenIsInvalid() {
+ assertFalse(authService.isTokenValid("invalidToken.shouldNotWork.shouldBeFalse"));
+ }
+
+ @Test
+ void ensureIsTokenValidReturnsTrueIfTokenIsValid() {
+ when(jwtUtil.generateToken(any())).thenCallRealMethod();
+ when(jwtUtil.generateKey()).thenCallRealMethod();
+
+
+ assertTrue(authService.isTokenValid(jwtUtil.generateToken("AnyUser")));
+ }
}