Fix refreshtoken api
This commit is contained in:
parent
fd5f456a31
commit
ec150cd642
13 changed files with 144 additions and 6 deletions
|
|
@ -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;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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()));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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> {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
package br.com.rayankonecny.authserviceapi.services;
|
|
||||||
|
|
||||||
public class BadCredentialsExceptionion {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -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())));
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
package br.com.rayankonecny.hdcommoslib.models.exceptions;
|
||||||
|
|
||||||
|
public class RefreshTokenExpired extends RuntimeException {
|
||||||
|
public RefreshTokenExpired(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
@ -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
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
package br.com.rayankonecny.hdcommoslib.models.responses;
|
||||||
|
|
||||||
|
public record RefreshTokenResponse(String refreshToken) {}
|
||||||
10
rest.http
10
rest.http
|
|
@ -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"
|
||||||
|
}
|
||||||
Loading…
Add table
Reference in a new issue