Fix refreshtoken api

This commit is contained in:
rayankonecny 2025-12-18 01:55:39 +00:00
parent fd5f456a31
commit ec150cd642
13 changed files with 144 additions and 6 deletions

View file

@ -6,7 +6,10 @@ import org.springframework.web.bind.annotation.RequestMapping;
import br.com.rayankonecny.hdcommoslib.models.exceptions.StandardError; import br.com.rayankonecny.hdcommoslib.models.exceptions.StandardError;
import br.com.rayankonecny.hdcommoslib.models.requests.AuthenticateRequest; import br.com.rayankonecny.hdcommoslib.models.requests.AuthenticateRequest;
import br.com.rayankonecny.hdcommoslib.models.requests.RefreshTokenRequest;
import br.com.rayankonecny.hdcommoslib.models.responses.AuthenticationResponse; import br.com.rayankonecny.hdcommoslib.models.responses.AuthenticationResponse;
import br.com.rayankonecny.hdcommoslib.models.responses.RefreshTokenResponse;
import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestBody;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Content;
@ -32,4 +35,16 @@ public interface AuthController {
@Valid @Valid
final AuthenticateRequest requests) throws Exception; final AuthenticateRequest requests) throws Exception;
@Operation(summary = "Refresh token")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "Token refreshed", content = @Content(mediaType = APPLICATION_JSON_VALUE, schema = @Schema(implementation = RefreshTokenResponse.class))),
@ApiResponse(responseCode = "400", description = "Bad request", content = @Content(mediaType = APPLICATION_JSON_VALUE, schema = @Schema(implementation = StandardError.class))),
@ApiResponse(responseCode = "401", description = "Unauthorized", content = @Content(mediaType = APPLICATION_JSON_VALUE, schema = @Schema(implementation = StandardError.class))),
@ApiResponse(responseCode = "404", description = "Username not found", content = @Content(mediaType = APPLICATION_JSON_VALUE, schema = @Schema(implementation = StandardError.class))),
@ApiResponse(responseCode = "500", description = "Internal server error", content = @Content(mediaType = APPLICATION_JSON_VALUE, schema = @Schema(implementation = StandardError.class))) })
@PostMapping("/refreshtoken")
ResponseEntity<RefreshTokenResponse> refreshToken(@Valid
@RequestBody
final RefreshTokenRequest refreshToken) throws Exception;
} }

View file

@ -4,9 +4,13 @@ import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import br.com.rayankonecny.authserviceapi.controllers.AuthController; import br.com.rayankonecny.authserviceapi.controllers.AuthController;
import br.com.rayankonecny.authserviceapi.models.RefreshToken;
import br.com.rayankonecny.authserviceapi.services.AuthService; import br.com.rayankonecny.authserviceapi.services.AuthService;
import br.com.rayankonecny.authserviceapi.services.RefreshTokenService;
import br.com.rayankonecny.hdcommoslib.models.requests.AuthenticateRequest; import br.com.rayankonecny.hdcommoslib.models.requests.AuthenticateRequest;
import br.com.rayankonecny.hdcommoslib.models.requests.RefreshTokenRequest;
import br.com.rayankonecny.hdcommoslib.models.responses.AuthenticationResponse; import br.com.rayankonecny.hdcommoslib.models.responses.AuthenticationResponse;
import br.com.rayankonecny.hdcommoslib.models.responses.RefreshTokenResponse;
import jakarta.validation.Valid; import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
@ -15,6 +19,7 @@ import lombok.RequiredArgsConstructor;
public class AuthControllerImpl implements AuthController { public class AuthControllerImpl implements AuthController {
private final AuthService authService; private final AuthService authService;
private final RefreshTokenService refreshService;
@Override @Override
public ResponseEntity<AuthenticationResponse> authenticate(@Valid public ResponseEntity<AuthenticationResponse> authenticate(@Valid
@ -23,4 +28,10 @@ public class AuthControllerImpl implements AuthController {
return ResponseEntity.ok(authService.authenticate(request)); return ResponseEntity.ok(authService.authenticate(request));
} }
@Override
public ResponseEntity<RefreshTokenResponse> refreshToken(@Valid
RefreshTokenRequest request) throws Exception {
return ResponseEntity.ok().body(refreshService.refreshToken(request.refreshToken()));
}
} }

View file

@ -0,0 +1,22 @@
package br.com.rayankonecny.authserviceapi.models;
import java.time.LocalDateTime;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
import lombok.Builder;
import lombok.Getter;
@Builder
@Getter
@Document
public class RefreshToken {
@Id
private String id;
private String username;
private LocalDateTime createdAt;
private LocalDateTime expiresAt;
}

View file

@ -0,0 +1,11 @@
package br.com.rayankonecny.authserviceapi.repository;
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.stereotype.Repository;
import br.com.rayankonecny.authserviceapi.models.RefreshToken;
@Repository
public interface RefreshTokenRepository extends MongoRepository<RefreshToken, String> {
}

View file

@ -5,6 +5,7 @@ import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import br.com.rayankonecny.authserviceapi.models.RefreshToken;
import br.com.rayankonecny.authserviceapi.utils.JWTUtils; import br.com.rayankonecny.authserviceapi.utils.JWTUtils;
import br.com.rayankonecny.hdcommoslib.models.requests.AuthenticateRequest; import br.com.rayankonecny.hdcommoslib.models.requests.AuthenticateRequest;
import br.com.rayankonecny.hdcommoslib.models.responses.AuthenticationResponse; import br.com.rayankonecny.hdcommoslib.models.responses.AuthenticationResponse;
@ -15,6 +16,7 @@ import lombok.RequiredArgsConstructor;
public class AuthService { public class AuthService {
private final UserDetailsServiceImpl userDetailsService; private final UserDetailsServiceImpl userDetailsService;
private final RefreshTokenService refreshTokenService;
private final PasswordEncoder passwordEncoder; private final PasswordEncoder passwordEncoder;
private final JWTUtils jwtUtils; private final JWTUtils jwtUtils;
@ -27,7 +29,8 @@ public class AuthService {
} }
String token = jwtUtils.generateToken(user); String token = jwtUtils.generateToken(user);
RefreshToken refresh = refreshTokenService.save(user.getUsername());
return new AuthenticationResponse(token,user.getUsername()); return new AuthenticationResponse(token, refresh.getId(), user.getUsername());
} }
} }

View file

@ -1,5 +0,0 @@
package br.com.rayankonecny.authserviceapi.services;
public class BadCredentialsExceptionion {
}

View file

@ -0,0 +1,48 @@
package br.com.rayankonecny.authserviceapi.services;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.stereotype.Service;
import br.com.rayankonecny.authserviceapi.models.RefreshToken;
import br.com.rayankonecny.authserviceapi.repository.RefreshTokenRepository;
import br.com.rayankonecny.authserviceapi.security.UserDetailsDTO;
import br.com.rayankonecny.authserviceapi.utils.JWTUtils;
import br.com.rayankonecny.hdcommoslib.models.exceptions.RefreshTokenExpired;
import br.com.rayankonecny.hdcommoslib.models.exceptions.ResourceNotFoundException;
import br.com.rayankonecny.hdcommoslib.models.responses.RefreshTokenResponse;
import lombok.RequiredArgsConstructor;
import static java.time.LocalDateTime.now;
import java.util.UUID;
@Service
@RequiredArgsConstructor
public class RefreshTokenService {
@Value("${jwt.expiration-sec.refresh-token}")
private Long refreshTokenExpirationSec;
private final RefreshTokenRepository repository;
private final UserDetailsService userDetail;
private final JWTUtils jwtUtils;
public RefreshToken save(final String username) {
return repository.save(RefreshToken.builder().id(UUID.randomUUID().toString())
.expiresAt(now().plusSeconds(refreshTokenExpirationSec)).username(username).build());
}
public RefreshTokenResponse refreshToken(final String refreshTokenId) {
final var refreshToken = repository.findById(refreshTokenId)
.orElseThrow(() -> new ResourceNotFoundException("Refresh token not found. Id: " + refreshTokenId));
if (refreshToken.getExpiresAt().isBefore((now()))) {
throw new RefreshTokenExpired("Refresh token expired. Id: " + refreshTokenId);
}
return new RefreshTokenResponse(
jwtUtils.generateToken((UserDetailsDTO) userDetail.loadUserByUsername(refreshToken.getUsername())));
}
}

View file

@ -9,3 +9,4 @@ spring:
enabled: false enabled: false
jwt.secret: "IHf3Yua/byvtA+iIcGWmkrLvpKEXTb5ClkXaZ0VDmYbr/6b1otCs38x68bidvZLAOB7anUtVQlCid6YDULO5XA==" jwt.secret: "IHf3Yua/byvtA+iIcGWmkrLvpKEXTb5ClkXaZ0VDmYbr/6b1otCs38x68bidvZLAOB7anUtVQlCid6YDULO5XA=="
jwt.expiration: 120000 jwt.expiration: 120000
jwt.expiration-sec.refresh-token: 3600

View file

@ -0,0 +1,7 @@
package br.com.rayankonecny.hdcommoslib.models.exceptions;
public class RefreshTokenExpired extends RuntimeException {
public RefreshTokenExpired(String message) {
super(message);
}
}

View file

@ -0,0 +1,11 @@
package br.com.rayankonecny.hdcommoslib.models.requests;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
public record RefreshTokenRequest(
@Size(min = 16, max = 30, message = "Refresh token must be between 16 and 30 characters")
@NotBlank(message = "Refresh token is required")
String refreshToken
) {
}

View file

@ -5,6 +5,7 @@ import lombok.Builder;
@Builder @Builder
public record AuthenticationResponse( public record AuthenticationResponse(
String token, String token,
String refreshToken,
String type String type
) { ) {
} }

View file

@ -0,0 +1,3 @@
package br.com.rayankonecny.hdcommoslib.models.responses;
public record RefreshTokenResponse(String refreshToken) {}

View file

@ -45,3 +45,13 @@ Accept: application/json
"email": "rayanvix@gmail.com", "email": "rayanvix@gmail.com",
"password": "123456" "password": "123456"
} }
POST http://175.15.15.91:8080/auth/refresh-token
Content-Type: application/json
Accept: application/json
# Authorization: Bearer {{token}}
{
"refreshToken": "73b32ec3-9933-4a60-ae06-b23c965dd1b2"
}