fix: return rotated refresh token from agent token refresh endpoint
All checks were successful
CI / build (push) Successful in 1m22s
CI / cleanup-branch (push) Has been skipped
CI / docker (push) Successful in 56s
CI / deploy (push) Successful in 47s
CI / deploy-feature (push) Has been skipped

Previously the refresh endpoint only returned a new accessToken, causing
agents to lose their refreshToken after the first refresh cycle and
forcing a full re-registration every ~2 hours.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
hsiegeln
2026-03-18 16:44:16 +01:00
parent 4085f42160
commit 17ef48e392
5 changed files with 14 additions and 6 deletions

View File

@@ -159,8 +159,9 @@ public class AgentRegistrationController {
List<String> roles = result.roles().isEmpty()
? List.of("AGENT") : result.roles();
String newAccessToken = jwtService.createAccessToken(agentId, agent.group(), roles);
String newRefreshToken = jwtService.createRefreshToken(agentId, agent.group(), roles);
return ResponseEntity.ok(new AgentRefreshResponse(newAccessToken));
return ResponseEntity.ok(new AgentRefreshResponse(newAccessToken, newRefreshToken));
}
@PostMapping("/{id}/heartbeat")

View File

@@ -3,5 +3,5 @@ package com.cameleer3.server.app.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
@Schema(description = "Refreshed access token")
public record AgentRefreshResponse(@NotNull String accessToken) {}
@Schema(description = "Refreshed access and refresh tokens")
public record AgentRefreshResponse(@NotNull String accessToken, @NotNull String refreshToken) {}

View File

@@ -79,6 +79,8 @@ class JwtRefreshIT extends AbstractPostgresIT {
JsonNode body = objectMapper.readTree(response.getBody());
assertThat(body.get("accessToken").asText()).isNotEmpty();
assertThat(body.get("refreshToken").asText()).isNotEmpty();
assertThat(body.get("refreshToken").asText()).isNotEqualTo(refreshToken);
}
@Test

View File

@@ -2913,14 +2913,18 @@
},
"AgentRefreshResponse": {
"type": "object",
"description": "Refreshed access token",
"description": "Refreshed access and refresh tokens",
"properties": {
"accessToken": {
"type": "string"
},
"refreshToken": {
"type": "string"
}
},
"required": [
"accessToken"
"accessToken",
"refreshToken"
]
},
"CommandRequest": {

View File

@@ -1069,9 +1069,10 @@ export interface components {
AgentRefreshRequest: {
refreshToken: string;
};
/** @description Refreshed access token */
/** @description Refreshed access and refresh tokens */
AgentRefreshResponse: {
accessToken: string;
refreshToken: string;
};
/** @description Command to send to agent(s) */
CommandRequest: {