test(04-01): add failing tests for security services
- JwtService: 7 tests for access/refresh token creation and validation - Ed25519SigningService: 5 tests for keypair, signing, verification - BootstrapTokenValidator: 6 tests for token matching and rotation - Core interfaces and stub implementations (all throw UnsupportedOperationException) - Added nimbus-jose-jwt and spring-boot-starter-security dependencies Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -66,11 +66,25 @@
|
|||||||
<artifactId>org.eclipse.xtext.xbase.lib</artifactId>
|
<artifactId>org.eclipse.xtext.xbase.lib</artifactId>
|
||||||
<version>2.37.0</version>
|
<version>2.37.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-security</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.nimbusds</groupId>
|
||||||
|
<artifactId>nimbus-jose-jwt</artifactId>
|
||||||
|
<version>9.47</version>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-test</artifactId>
|
<artifactId>spring-boot-starter-test</artifactId>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.security</groupId>
|
||||||
|
<artifactId>spring-security-test</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.testcontainers</groupId>
|
<groupId>org.testcontainers</groupId>
|
||||||
<artifactId>testcontainers-clickhouse</artifactId>
|
<artifactId>testcontainers-clickhouse</artifactId>
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package com.cameleer3.server.app.security;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validates bootstrap tokens used for initial agent registration.
|
||||||
|
* Stub — to be implemented in GREEN phase.
|
||||||
|
*/
|
||||||
|
public class BootstrapTokenValidator {
|
||||||
|
|
||||||
|
private final SecurityProperties properties;
|
||||||
|
|
||||||
|
public BootstrapTokenValidator(SecurityProperties properties) {
|
||||||
|
this.properties = properties;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validates the provided token against the configured bootstrap token(s).
|
||||||
|
*
|
||||||
|
* @param provided the token to validate
|
||||||
|
* @return true if the token matches the current or previous bootstrap token
|
||||||
|
*/
|
||||||
|
public boolean validate(String provided) {
|
||||||
|
throw new UnsupportedOperationException("Not yet implemented");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package com.cameleer3.server.app.security;
|
||||||
|
|
||||||
|
import com.cameleer3.server.core.security.Ed25519SigningService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JDK 17 Ed25519 signing implementation.
|
||||||
|
* Stub — to be implemented in GREEN phase.
|
||||||
|
*/
|
||||||
|
public class Ed25519SigningServiceImpl implements Ed25519SigningService {
|
||||||
|
|
||||||
|
public Ed25519SigningServiceImpl() {
|
||||||
|
// KeyPair generation will go here
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String sign(String payload) {
|
||||||
|
throw new UnsupportedOperationException("Not yet implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getPublicKeyBase64() {
|
||||||
|
throw new UnsupportedOperationException("Not yet implemented");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
package com.cameleer3.server.app.security;
|
||||||
|
|
||||||
|
import com.cameleer3.server.core.security.InvalidTokenException;
|
||||||
|
import com.cameleer3.server.core.security.JwtService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HMAC-SHA256 JWT implementation using Nimbus JOSE+JWT.
|
||||||
|
* Stub — to be implemented in GREEN phase.
|
||||||
|
*/
|
||||||
|
public class JwtServiceImpl implements JwtService {
|
||||||
|
|
||||||
|
private final SecurityProperties properties;
|
||||||
|
|
||||||
|
public JwtServiceImpl(SecurityProperties properties) {
|
||||||
|
this.properties = properties;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String createAccessToken(String agentId, String group) {
|
||||||
|
throw new UnsupportedOperationException("Not yet implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String createRefreshToken(String agentId, String group) {
|
||||||
|
throw new UnsupportedOperationException("Not yet implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String validateAndExtractAgentId(String token) {
|
||||||
|
throw new UnsupportedOperationException("Not yet implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String validateRefreshToken(String token) {
|
||||||
|
throw new UnsupportedOperationException("Not yet implemented");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
package com.cameleer3.server.app.security;
|
||||||
|
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configuration properties for security settings.
|
||||||
|
* Bound from the {@code security.*} namespace in application.yml.
|
||||||
|
*/
|
||||||
|
@ConfigurationProperties(prefix = "security")
|
||||||
|
public class SecurityProperties {
|
||||||
|
|
||||||
|
private long accessTokenExpiryMs = 3_600_000;
|
||||||
|
private long refreshTokenExpiryMs = 604_800_000;
|
||||||
|
private String bootstrapToken;
|
||||||
|
private String bootstrapTokenPrevious;
|
||||||
|
|
||||||
|
public long getAccessTokenExpiryMs() {
|
||||||
|
return accessTokenExpiryMs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAccessTokenExpiryMs(long accessTokenExpiryMs) {
|
||||||
|
this.accessTokenExpiryMs = accessTokenExpiryMs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getRefreshTokenExpiryMs() {
|
||||||
|
return refreshTokenExpiryMs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRefreshTokenExpiryMs(long refreshTokenExpiryMs) {
|
||||||
|
this.refreshTokenExpiryMs = refreshTokenExpiryMs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getBootstrapToken() {
|
||||||
|
return bootstrapToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBootstrapToken(String bootstrapToken) {
|
||||||
|
this.bootstrapToken = bootstrapToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getBootstrapTokenPrevious() {
|
||||||
|
return bootstrapTokenPrevious;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBootstrapTokenPrevious(String bootstrapTokenPrevious) {
|
||||||
|
this.bootstrapTokenPrevious = bootstrapTokenPrevious;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
package com.cameleer3.server.app.security;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unit tests for {@link BootstrapTokenValidator}.
|
||||||
|
* No Spring context needed — implementation constructed directly.
|
||||||
|
*/
|
||||||
|
class BootstrapTokenValidatorTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void validate_correctToken_returnsTrue() {
|
||||||
|
SecurityProperties props = new SecurityProperties();
|
||||||
|
props.setBootstrapToken("my-secret-token");
|
||||||
|
BootstrapTokenValidator validator = new BootstrapTokenValidator(props);
|
||||||
|
|
||||||
|
assertTrue(validator.validate("my-secret-token"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void validate_wrongToken_returnsFalse() {
|
||||||
|
SecurityProperties props = new SecurityProperties();
|
||||||
|
props.setBootstrapToken("my-secret-token");
|
||||||
|
BootstrapTokenValidator validator = new BootstrapTokenValidator(props);
|
||||||
|
|
||||||
|
assertFalse(validator.validate("wrong-token"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void validate_previousToken_returnsTrueWhenSet() {
|
||||||
|
SecurityProperties props = new SecurityProperties();
|
||||||
|
props.setBootstrapToken("new-token");
|
||||||
|
props.setBootstrapTokenPrevious("old-token");
|
||||||
|
BootstrapTokenValidator validator = new BootstrapTokenValidator(props);
|
||||||
|
|
||||||
|
assertTrue(validator.validate("old-token"), "Previous token should be accepted during rotation");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void validate_nullToken_returnsFalse() {
|
||||||
|
SecurityProperties props = new SecurityProperties();
|
||||||
|
props.setBootstrapToken("my-secret-token");
|
||||||
|
BootstrapTokenValidator validator = new BootstrapTokenValidator(props);
|
||||||
|
|
||||||
|
assertFalse(validator.validate(null));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void validate_blankToken_returnsFalse() {
|
||||||
|
SecurityProperties props = new SecurityProperties();
|
||||||
|
props.setBootstrapToken("my-secret-token");
|
||||||
|
BootstrapTokenValidator validator = new BootstrapTokenValidator(props);
|
||||||
|
|
||||||
|
assertFalse(validator.validate(""));
|
||||||
|
assertFalse(validator.validate(" "));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void validate_previousTokenNotSet_onlyCurrentAccepted() {
|
||||||
|
SecurityProperties props = new SecurityProperties();
|
||||||
|
props.setBootstrapToken("current-token");
|
||||||
|
// bootstrapTokenPrevious is null by default
|
||||||
|
BootstrapTokenValidator validator = new BootstrapTokenValidator(props);
|
||||||
|
|
||||||
|
assertTrue(validator.validate("current-token"));
|
||||||
|
assertFalse(validator.validate("some-old-token"));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
package com.cameleer3.server.app.security;
|
||||||
|
|
||||||
|
import com.cameleer3.server.core.security.Ed25519SigningService;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.security.KeyFactory;
|
||||||
|
import java.security.PublicKey;
|
||||||
|
import java.security.Signature;
|
||||||
|
import java.security.spec.X509EncodedKeySpec;
|
||||||
|
import java.util.Base64;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unit tests for {@link Ed25519SigningServiceImpl}.
|
||||||
|
* No Spring context needed — implementation constructed directly.
|
||||||
|
*/
|
||||||
|
class Ed25519SigningServiceTest {
|
||||||
|
|
||||||
|
private Ed25519SigningService signingService;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setUp() {
|
||||||
|
signingService = new Ed25519SigningServiceImpl();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getPublicKeyBase64_returnsNonNullBase64String() {
|
||||||
|
String publicKeyBase64 = signingService.getPublicKeyBase64();
|
||||||
|
assertNotNull(publicKeyBase64);
|
||||||
|
assertFalse(publicKeyBase64.isBlank());
|
||||||
|
// Verify it's valid Base64
|
||||||
|
assertDoesNotThrow(() -> Base64.getDecoder().decode(publicKeyBase64));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void sign_returnsBase64SignatureString() {
|
||||||
|
String signature = signingService.sign("test payload");
|
||||||
|
assertNotNull(signature);
|
||||||
|
assertFalse(signature.isBlank());
|
||||||
|
assertDoesNotThrow(() -> Base64.getDecoder().decode(signature));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void sign_signatureVerifiesAgainstPublicKey() throws Exception {
|
||||||
|
String payload = "important config data";
|
||||||
|
String signatureBase64 = signingService.sign(payload);
|
||||||
|
String publicKeyBase64 = signingService.getPublicKeyBase64();
|
||||||
|
|
||||||
|
// Reconstruct public key from Base64
|
||||||
|
byte[] publicKeyBytes = Base64.getDecoder().decode(publicKeyBase64);
|
||||||
|
KeyFactory keyFactory = KeyFactory.getInstance("Ed25519");
|
||||||
|
PublicKey publicKey = keyFactory.generatePublic(new X509EncodedKeySpec(publicKeyBytes));
|
||||||
|
|
||||||
|
// Verify signature
|
||||||
|
Signature verifier = Signature.getInstance("Ed25519");
|
||||||
|
verifier.initVerify(publicKey);
|
||||||
|
verifier.update(payload.getBytes(java.nio.charset.StandardCharsets.UTF_8));
|
||||||
|
assertTrue(verifier.verify(Base64.getDecoder().decode(signatureBase64)),
|
||||||
|
"Signature should verify against the public key");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void sign_differentPayloadsProduceDifferentSignatures() {
|
||||||
|
String sig1 = signingService.sign("payload one");
|
||||||
|
String sig2 = signingService.sign("payload two");
|
||||||
|
assertNotEquals(sig1, sig2, "Different payloads should produce different signatures");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void sign_tamperedPayloadFailsVerification() throws Exception {
|
||||||
|
String payload = "original payload";
|
||||||
|
String signatureBase64 = signingService.sign(payload);
|
||||||
|
String publicKeyBase64 = signingService.getPublicKeyBase64();
|
||||||
|
|
||||||
|
byte[] publicKeyBytes = Base64.getDecoder().decode(publicKeyBase64);
|
||||||
|
KeyFactory keyFactory = KeyFactory.getInstance("Ed25519");
|
||||||
|
PublicKey publicKey = keyFactory.generatePublic(new X509EncodedKeySpec(publicKeyBytes));
|
||||||
|
|
||||||
|
// Verify against tampered payload
|
||||||
|
Signature verifier = Signature.getInstance("Ed25519");
|
||||||
|
verifier.initVerify(publicKey);
|
||||||
|
verifier.update("tampered payload".getBytes(java.nio.charset.StandardCharsets.UTF_8));
|
||||||
|
assertFalse(verifier.verify(Base64.getDecoder().decode(signatureBase64)),
|
||||||
|
"Tampered payload should fail verification");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
package com.cameleer3.server.app.security;
|
||||||
|
|
||||||
|
import com.cameleer3.server.core.security.InvalidTokenException;
|
||||||
|
import com.cameleer3.server.core.security.JwtService;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unit tests for {@link JwtServiceImpl}.
|
||||||
|
* No Spring context needed — implementations are constructed directly.
|
||||||
|
*/
|
||||||
|
class JwtServiceTest {
|
||||||
|
|
||||||
|
private JwtService jwtService;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setUp() {
|
||||||
|
SecurityProperties props = new SecurityProperties();
|
||||||
|
props.setAccessTokenExpiryMs(3_600_000); // 1 hour
|
||||||
|
props.setRefreshTokenExpiryMs(604_800_000); // 7 days
|
||||||
|
jwtService = new JwtServiceImpl(props);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void createAccessToken_returnsSignedJwtWithCorrectClaims() {
|
||||||
|
String token = jwtService.createAccessToken("agent-1", "group-a");
|
||||||
|
assertNotNull(token);
|
||||||
|
assertFalse(token.isBlank());
|
||||||
|
// JWT format: header.payload.signature
|
||||||
|
assertEquals(3, token.split("\\.").length, "JWT should have 3 parts");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void createAccessToken_canBeValidated() {
|
||||||
|
String token = jwtService.createAccessToken("agent-1", "group-a");
|
||||||
|
String agentId = jwtService.validateAndExtractAgentId(token);
|
||||||
|
assertEquals("agent-1", agentId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void createRefreshToken_returnsSignedJwt() {
|
||||||
|
String token = jwtService.createRefreshToken("agent-2", "group-b");
|
||||||
|
assertNotNull(token);
|
||||||
|
assertFalse(token.isBlank());
|
||||||
|
assertEquals(3, token.split("\\.").length, "JWT should have 3 parts");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void createRefreshToken_canBeValidatedWithRefreshMethod() {
|
||||||
|
String token = jwtService.createRefreshToken("agent-2", "group-b");
|
||||||
|
String agentId = jwtService.validateRefreshToken(token);
|
||||||
|
assertEquals("agent-2", agentId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void validateAndExtractAgentId_rejectsRefreshToken() {
|
||||||
|
String refreshToken = jwtService.createRefreshToken("agent-3", "group-c");
|
||||||
|
assertThrows(InvalidTokenException.class, () ->
|
||||||
|
jwtService.validateAndExtractAgentId(refreshToken),
|
||||||
|
"Access validation should reject refresh tokens");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void validateRefreshToken_rejectsAccessToken() {
|
||||||
|
String accessToken = jwtService.createAccessToken("agent-4", "group-d");
|
||||||
|
assertThrows(InvalidTokenException.class, () ->
|
||||||
|
jwtService.validateRefreshToken(accessToken),
|
||||||
|
"Refresh validation should reject access tokens");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void validateAndExtractAgentId_rejectsExpiredToken() {
|
||||||
|
// Create a service with 0ms expiry to produce already-expired tokens
|
||||||
|
SecurityProperties shortProps = new SecurityProperties();
|
||||||
|
shortProps.setAccessTokenExpiryMs(0);
|
||||||
|
shortProps.setRefreshTokenExpiryMs(604_800_000);
|
||||||
|
JwtService shortLivedService = new JwtServiceImpl(shortProps);
|
||||||
|
|
||||||
|
String token = shortLivedService.createAccessToken("agent-5", "group-e");
|
||||||
|
assertThrows(InvalidTokenException.class, () ->
|
||||||
|
jwtService.validateAndExtractAgentId(token),
|
||||||
|
"Should reject expired tokens");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
package com.cameleer3.server.core.security;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Service for Ed25519 digital signatures.
|
||||||
|
* <p>
|
||||||
|
* Used to sign configuration and command payloads pushed to agents via SSE,
|
||||||
|
* allowing agents to verify the authenticity of received data.
|
||||||
|
* The keypair is ephemeral (generated at startup); agents receive the public
|
||||||
|
* key during registration.
|
||||||
|
*/
|
||||||
|
public interface Ed25519SigningService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Signs the given payload using the server's Ed25519 private key.
|
||||||
|
*
|
||||||
|
* @param payload the string payload to sign
|
||||||
|
* @return Base64-encoded signature bytes
|
||||||
|
*/
|
||||||
|
String sign(String payload);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the server's Ed25519 public key as a Base64-encoded string
|
||||||
|
* (X.509 SubjectPublicKeyInfo DER format).
|
||||||
|
*
|
||||||
|
* @return Base64-encoded public key
|
||||||
|
*/
|
||||||
|
String getPublicKeyBase64();
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package com.cameleer3.server.core.security;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Thrown when a JWT token is invalid, expired, or of the wrong type.
|
||||||
|
*/
|
||||||
|
public class InvalidTokenException extends RuntimeException {
|
||||||
|
|
||||||
|
public InvalidTokenException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public InvalidTokenException(String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
package com.cameleer3.server.core.security;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Service for creating and validating JSON Web Tokens (JWT).
|
||||||
|
* <p>
|
||||||
|
* Access tokens are short-lived (default 1 hour) and used for API authentication.
|
||||||
|
* Refresh tokens are longer-lived (default 7 days) and used to obtain new access tokens.
|
||||||
|
*/
|
||||||
|
public interface JwtService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a signed access JWT with the given agent ID and group.
|
||||||
|
*
|
||||||
|
* @param agentId the agent identifier (becomes the {@code sub} claim)
|
||||||
|
* @param group the agent group (becomes the {@code group} claim)
|
||||||
|
* @return a signed JWT string
|
||||||
|
*/
|
||||||
|
String createAccessToken(String agentId, String group);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a signed refresh JWT with the given agent ID and group.
|
||||||
|
*
|
||||||
|
* @param agentId the agent identifier (becomes the {@code sub} claim)
|
||||||
|
* @param group the agent group (becomes the {@code group} claim)
|
||||||
|
* @return a signed JWT string
|
||||||
|
*/
|
||||||
|
String createRefreshToken(String agentId, String group);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validates an access token and extracts the agent ID.
|
||||||
|
* Rejects expired tokens and tokens that are not of type "access".
|
||||||
|
*
|
||||||
|
* @param token the JWT string to validate
|
||||||
|
* @return the agent ID from the {@code sub} claim
|
||||||
|
* @throws InvalidTokenException if the token is invalid, expired, or not an access token
|
||||||
|
*/
|
||||||
|
String validateAndExtractAgentId(String token);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validates a refresh token and extracts the agent ID.
|
||||||
|
* Rejects expired tokens and tokens that are not of type "refresh".
|
||||||
|
*
|
||||||
|
* @param token the JWT string to validate
|
||||||
|
* @return the agent ID from the {@code sub} claim
|
||||||
|
* @throws InvalidTokenException if the token is invalid, expired, or not a refresh token
|
||||||
|
*/
|
||||||
|
String validateRefreshToken(String token);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user