Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
94 commits
Select commit Hold shift + click to select a range
015dc9e
feat(dto): add ForgotPasswordRequest record
EdgarEldy Nov 9, 2026
95a9afa
feat(dto): add ResetPasswordRequest record
EdgarEldy Nov 9, 2026
e7c7231
feat(service): add UserService interface for token-driven user operat…
EdgarEldy Nov 9, 2026
7252e10
feat(entity): add enabled field to AppUser
EdgarEldy Nov 9, 2026
b1a3bd0
feat(flyway): add enabled column to app_users (V10)
EdgarEldy Nov 9, 2026
dd296cb
refactor(service): send activation email on register in AuthServiceImpl
EdgarEldy Nov 9, 2026
2bb1831
refactor(security): enforce enabled flag in UserDetailsServiceImpl
EdgarEldy Nov 9, 2026
88f9154
feat(service): add UserServiceImpl for account activation and passwor…
EdgarEldy Nov 9, 2026
71678c5
feat(controller): add activate, forgot-password, reset-password endpo…
EdgarEldy Nov 9, 2026
3e7b829
test(controller): add tests for activate, forgot-password, reset-pass…
EdgarEldy Nov 9, 2026
1236e2a
test(service): add UserServiceImplTest for activation and password reset
EdgarEldy Nov 9, 2026
6223336
fix(security): permit activate, forgot-password, reset-password endpo…
EdgarEldy Nov 9, 2026
6bcacfd
fix(test): enable user in SecurityAuthorizationTest after register
EdgarEldy Nov 9, 2026
e3e6899
fix(test): add missing mocks in AuthServiceImplTest for register flow
EdgarEldy Nov 9, 2026
7575e02
fix(test): add @ActiveProfiles("test") to UniqueValidatorTest
EdgarEldy Nov 9, 2026
9e29a04
merge(feature/token-management): cascade @RequestParam token and @Pas…
EdgarEldy Nov 9, 2026
845dd3d
feat(exception): add specific handlers for LockedException (423), Dis…
EdgarEldy Nov 9, 2026
bf743d4
test(exception): add tests for LockedException 423, DisabledException…
EdgarEldy Nov 9, 2026
8b7b676
feat(security): add refreshExpirationMs to JwtProperties for refresh …
EdgarEldy Nov 9, 2026
99e4c80
chore(config): add jwt.refresh-expiration-ms property (7 days default)
EdgarEldy Nov 9, 2026
90849a2
feat(security): add generateRefreshToken, isRefreshToken, extractAllC…
EdgarEldy Nov 9, 2026
875005a
test(security): update JwtServiceTest for 3-arg JwtProperties and new…
EdgarEldy Nov 9, 2026
1a1b2a1
feat(controller): add GET /auth/token-info endpoint for JWT introspec…
EdgarEldy Nov 9, 2026
187588a
test(controller): add tests for GET /auth/token-info endpoint
EdgarEldy Nov 9, 2026
928657a
merge(feature/auth): cascade exception handlers, JwtService methods, …
EdgarEldy Nov 9, 2026
4a3cc3f
merge(feature/roles-permissions): cascade exception handlers, JwtServ…
EdgarEldy Nov 9, 2026
c933da6
feat(entity): introduce AuditableToken @MappedSuperclass for token hi…
EdgarEldy Nov 9, 2026
6899492
refactor(entity): extend AuditableToken in ActivationToken
EdgarEldy Nov 9, 2026
c1e7f5f
refactor(entity): extend AuditableToken in BlacklistedToken
EdgarEldy Nov 9, 2026
f37c268
refactor(entity): extend AuditableToken in PasswordResetToken
EdgarEldy Nov 9, 2026
28ed771
flyway(token): V11 - add created_at to password_reset_tokens
EdgarEldy Nov 9, 2026
5aad639
fix(token): set createdAt in PasswordResetTokenServiceImpl.generate()
EdgarEldy Nov 9, 2026
255b1e1
feat(repository): add deleteByUser to ActivationTokenRepository
EdgarEldy Nov 9, 2026
c394052
feat(repository): add deleteByUser to PasswordResetTokenRepository
EdgarEldy Nov 9, 2026
2805df4
feat(token): purge old activation tokens before generating a new one
EdgarEldy Nov 9, 2026
4bfe987
feat(token): purge old password-reset tokens before generating a new one
EdgarEldy Nov 9, 2026
016ac06
feat(exception): add TokenExpiredException extending InvalidTokenExce…
EdgarEldy Nov 9, 2026
27c2341
feat(token): throw TokenExpiredException instead of InvalidTokenExcep…
EdgarEldy Nov 9, 2026
8858de2
feat(entity): add RefreshToken extending AuditableToken
EdgarEldy Nov 9, 2026
4ac9059
flyway(token): V12 - create refresh_tokens table
EdgarEldy Nov 9, 2026
6620f23
feat(repository): add RefreshTokenRepository
EdgarEldy Nov 9, 2026
f50b38a
feat(service): add RefreshTokenService interface
EdgarEldy Nov 9, 2026
da5c208
feat(service): implement RefreshTokenServiceImpl
EdgarEldy Nov 9, 2026
fc44a92
feat(dto): add refresh_token field to AuthResponse
EdgarEldy Nov 9, 2026
bc755de
feat(service): add refresh() to AuthService contract
EdgarEldy Nov 9, 2026
bb26a32
feat(service): issue and rotate refresh tokens in AuthServiceImpl
EdgarEldy Nov 9, 2026
c7b9251
feat(controller): add POST /api/v1/auth/refresh-token endpoint
EdgarEldy Nov 9, 2026
ebf694c
feat(security): permit POST /api/v1/auth/refresh-token without authen…
EdgarEldy Nov 9, 2026
d71da9d
test(dto): update AuthDtoMaskingTest for 4-arg AuthResponse and refre…
EdgarEldy Nov 9, 2026
dea8ed1
test(controller): update AuthControllerTest for RefreshTokenService a…
EdgarEldy Nov 9, 2026
3dd1a52
test(service): update AuthServiceImplTest for RefreshTokenService and…
EdgarEldy Nov 9, 2026
b3b08f2
test(token): update ActivationTokenServiceImplTest for purge and Toke…
EdgarEldy Nov 9, 2026
ada8833
test(token): update PasswordResetTokenServiceImplTest for purge behavior
EdgarEldy Nov 9, 2026
61b51e6
test(service): add RefreshTokenServiceImplTest
EdgarEldy Nov 9, 2026
c59c3b6
feat(scheduler): purge expired refresh tokens daily
EdgarEldy Nov 9, 2026
978acae
merge(feature/token-management): integrate refresh token system into …
EdgarEldy Nov 9, 2026
b326710
feat(service): guard forgotPassword against disabled or locked accounts
EdgarEldy Nov 9, 2026
f21be97
test(service): cover forgotPassword guard for disabled and locked acc…
EdgarEldy Nov 9, 2026
6f921d8
feat(service): add getUserByToken to ActivationTokenService
EdgarEldy Nov 9, 2026
6133be6
feat(service): auto-resend activation email on expired token in activ…
EdgarEldy Nov 9, 2026
fcc346f
test(service): cover getUserByToken happy path and missing-token error
EdgarEldy Nov 9, 2026
fa16cc4
test(service): cover auto-resend activation email on expired token
EdgarEldy Nov 9, 2026
216aa8e
feat(service): add removeRoleFromUser to UserService interface
EdgarEldy Nov 9, 2026
3b9c638
feat(service): implement removeRoleFromUser in UserServiceImpl
EdgarEldy Nov 9, 2026
3aaa4cd
feat(controller): add DELETE /api/v1/users/{id}/roles/{roleId} endpoint
EdgarEldy Nov 9, 2026
8eb7f44
test(controller): cover DELETE /users/{id}/roles/{roleId} - success a…
EdgarEldy Nov 9, 2026
3a9c874
test(service): cover removeRoleFromUser - success and role-not-found 422
EdgarEldy Nov 9, 2026
f145593
fix(service): make assignRoles additive instead of replacing the full…
EdgarEldy Nov 9, 2026
2f16183
test(service): use mutable HashSet in setUp and add additive assignRo…
EdgarEldy Nov 9, 2026
b72601d
test(service): add _04b_ asserting assignRoles preserves pre-existing…
EdgarEldy Nov 9, 2026
9f865ce
fix(test): use email instead of username in LoginRequest for _14_ and…
EdgarEldy Nov 9, 2026
99c9520
merge(feature/auth): cascade security fixes and new JWT methods into …
EdgarEldy Nov 9, 2026
9836b7d
fix(test): enable babbage before wrong-password login in _15_
EdgarEldy Nov 9, 2026
4fe35c5
fix(test): set createdAt in PasswordResetTokenRepositoryTest builders
EdgarEldy Nov 9, 2026
29a83b3
merge(feature/token-management): cascade test fixes into user-management
EdgarEldy Nov 9, 2026
0d63f03
fix(test): replace undefined enableUser() with inline lambda in _18_
EdgarEldy Nov 9, 2026
7762f4d
fix(test): enable appUser before forgotPassword in _09_
EdgarEldy Nov 9, 2026
a560a73
refactor(dto): rename token to access_token and drop username in Auth…
EdgarEldy Nov 9, 2026
f645f13
refactor(auth): update AuthServiceImpl.login() for new 2-field AuthRe…
EdgarEldy Nov 9, 2026
7fd6637
test(auth): update AuthResponse constructor in AuthServiceImplTest to…
EdgarEldy Nov 9, 2026
ac8aa44
test(auth): update AuthControllerTest for 2-field AuthResponse and ac…
EdgarEldy Nov 9, 2026
5128fc3
test(auth): update SecurityAuthorizationTest to read access_token JSO…
EdgarEldy Nov 9, 2026
a2ab639
test(auth): update AuthDtoMaskingTest for 2-field AuthResponse (no us…
EdgarEldy Nov 9, 2026
a257eea
merge(feature/auth): cascade access_token rename and username removal
EdgarEldy Nov 9, 2026
71c9f40
feat(security): expose getExpirationMs() on JwtService for expires_in…
EdgarEldy Nov 9, 2026
093a20d
feat(dto): add expires_in field to AuthResponse
EdgarEldy Nov 9, 2026
96e0a03
feat(auth): pass expires_in to AuthResponse in login() and refresh()
EdgarEldy Nov 9, 2026
b837f0a
test(auth): stub getExpirationMs() and update AuthResponse to 4-field…
EdgarEldy Nov 9, 2026
7f47eb2
test(auth): update AuthResponse to 4-field and assert expires_in in A…
EdgarEldy Nov 9, 2026
fc5b567
test(auth): update AuthDtoMaskingTest for 4-field AuthResponse with e…
EdgarEldy Nov 9, 2026
4c3a922
fix(test): update _16_ to read access_token JSON field in SecurityAut…
EdgarEldy Nov 9, 2026
d8d8a86
merge(feature/token-management): cascade access_token rename, expires…
EdgarEldy Nov 9, 2026
0f37cb2
fix(test): read access_token field in _20_ of SecurityAuthorizationTest
EdgarEldy Nov 9, 2026
ab91f45
Merge pull request #20 from EdgarEldy/feature/user-management
EdgarEldy Nov 9, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,8 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti
.authorizeHttpRequests(authorize -> authorize
.requestMatchers(HttpMethod.POST,
"/api/v1/auth/register", "/api/v1/auth/login",
"/api/v1/auth/forgot-password", "/api/v1/auth/reset-password")
"/api/v1/auth/forgot-password", "/api/v1/auth/reset-password",
"/api/v1/auth/refresh-token")
.permitAll()
.requestMatchers(HttpMethod.GET, "/api/v1/auth/activate")
.permitAll()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@
import com.edgareldy.springboottutorial.dto.auth.ResetPasswordRequest;
import com.edgareldy.springboottutorial.dto.auth.UserResponse;
import com.edgareldy.springboottutorial.dto.common.ApiResponse;
import com.edgareldy.springboottutorial.security.JwtService;
import com.edgareldy.springboottutorial.service.AuthService;
import com.edgareldy.springboottutorial.service.UserService;
import jakarta.validation.Valid;
import java.util.Map;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
Expand Down Expand Up @@ -43,6 +45,16 @@ public class AuthController {

private final AuthService authService;
private final UserService userService;
private final JwtService jwtService;

@PostMapping("/refresh-token")
public ResponseEntity<ApiResponse<AuthResponse>> refreshToken(
@RequestHeader("Authorization") String authHeader) {
String rawToken = authHeader.startsWith(BEARER_PREFIX)
? authHeader.substring(BEARER_PREFIX.length())
: authHeader;
return ResponseEntity.ok(ApiResponse.success(authService.refresh(rawToken), "Token refreshed successfully"));
}

@PostMapping("/register")
public ResponseEntity<ApiResponse<UserResponse>> register(@Valid @RequestBody RegisterRequest request) {
Expand All @@ -68,6 +80,16 @@ public ResponseEntity<ApiResponse<UserResponse>> me(@AuthenticationPrincipal Use
return ResponseEntity.ok(ApiResponse.success(authService.me(userDetails.getUsername()), "Profile retrieved successfully"));
}

@GetMapping("/token-info")
public ResponseEntity<ApiResponse<Map<String, Object>>> tokenInfo(
@RequestHeader("Authorization") String authHeader) {
String token = authHeader.startsWith(BEARER_PREFIX)
? authHeader.substring(BEARER_PREFIX.length())
: authHeader;
Map<String, Object> claims = jwtService.extractAllClaims(token);
return ResponseEntity.ok(ApiResponse.success(claims, "Token claims retrieved successfully"));
}

@GetMapping("/activate")
public ResponseEntity<ApiResponse<UserResponse>> activate(@RequestParam String token) {
return ResponseEntity.ok(ApiResponse.success(userService.activate(token), "Account activated successfully"));
Expand All @@ -80,8 +102,10 @@ public ResponseEntity<ApiResponse<Void>> forgotPassword(@Valid @RequestBody Forg
}

@PostMapping("/reset-password")
public ResponseEntity<ApiResponse<Void>> resetPassword(@Valid @RequestBody ResetPasswordRequest request) {
userService.resetPassword(request.token(), request.newPassword());
public ResponseEntity<ApiResponse<Void>> resetPassword(
@RequestParam String token,
@Valid @RequestBody ResetPasswordRequest request) {
userService.resetPassword(token, request.newPassword());
return ResponseEntity.ok(ApiResponse.success(null, "Password reset successfully"));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,14 @@ public ResponseEntity<ApiResponse<UserResponse>> assignRoles(
return ResponseEntity.ok(ApiResponse.success(userService.assignRoles(id, request), "Roles assigned successfully"));
}

@DeleteMapping("/{id}/roles/{roleId}")
public ResponseEntity<ApiResponse<UserResponse>> removeRole(
@PathVariable Long id,
@PathVariable Long roleId) {
return ResponseEntity.ok(ApiResponse.success(
userService.removeRoleFromUser(id, roleId), "Role removed successfully"));
}

@DeleteMapping("/{id}")
public ResponseEntity<ApiResponse<Void>> delete(@PathVariable Long id) {
userService.delete(id);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,32 @@
import com.fasterxml.jackson.annotation.JsonProperty;

/**
* Payload returned by {@code POST /api/v1/auth/login}: the JWT to send back
* as a {@code Bearer} token on subsequent requests, plus enough profile
* information for the client to avoid an extra call to {@code /auth/me}.
* The JSON contract is snake_case, decoupled from the Java field's own
* camelCase name via an explicit {@link JsonProperty} annotation.
* Payload returned by {@code POST /api/v1/auth/login} and
* {@code POST /api/v1/auth/refresh-token}. Fields follow the OAuth 2.0
* naming convention: {@code access_token}, {@code token_type},
* {@code refresh_token}, and {@code expires_in} (access token lifetime in
* seconds). The client uses {@code expires_in} to schedule a proactive
* refresh before the access token expires, avoiding a failed request.
* {@code username} is omitted since it is already the {@code sub} claim
* inside the JWT itself.
* <p>
* Created edgar.muhamyangabo on 7/8/26
* Author : edgar.muhamyangabo
* Date : 7/8/26
* Project : spring-boot-tutorial
*/
public record AuthResponse(
String token,
@JsonProperty("access_token")
String accessToken,

@JsonProperty("token_type")
String tokenType,

String username
@JsonProperty("refresh_token")
String refreshToken,

@JsonProperty("expires_in")
long expiresIn
) {

// Overrides the compiler-generated toString(): LoggingAspect logs every
Expand All @@ -29,6 +37,7 @@ public record AuthResponse(
// a live bearer credential, in the application logs.
@Override
public String toString() {
return "AuthResponse[token=***, tokenType=%s, username=%s]".formatted(tokenType, username);
return "AuthResponse[accessToken=***, tokenType=%s, refreshToken=***, expiresIn=%d]"
.formatted(tokenType, expiresIn);
}
}
Original file line number Diff line number Diff line change
@@ -1,25 +1,33 @@
package com.edgareldy.springboottutorial.dto.auth;

import com.edgareldy.springboottutorial.validation.password.PasswordsMatch;
import com.edgareldy.springboottutorial.validation.password.ValidPassword;
import com.fasterxml.jackson.annotation.JsonProperty;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;

/**
* Request body for the POST /auth/reset-password endpoint. Carries the
* one-time reset token (delivered by email) and the new plain-text password
* to set after the token is validated and consumed.
* Request body for the POST /auth/reset-password endpoint. Carries the new
* password and its confirmation; the one-time reset token is passed as a
* query parameter so the frontend can forward it directly from the email
* link without re-embedding it in a JSON payload.
* <p>
* Created edgar.muhamyangabo on 10/30/26
* Author : edgar.muhamyangabo
* Date : 10/30/26
* Project : spring-boot-tutorial
*/
@PasswordsMatch(passwordField = "newPassword", confirmPasswordField = "confirmPassword",
message = "Passwords must match")
public record ResetPasswordRequest(
@NotBlank(message = "Token must not be blank")
@JsonProperty("token") String token,

@NotBlank(message = "New password must not be blank")
@Size(min = 8, message = "New password must be at least 8 characters")
@JsonProperty("new_password") String newPassword
@ValidPassword
@JsonProperty("new_password") String newPassword,

@NotBlank(message = "Password confirmation must not be blank")
@JsonProperty("confirm_password") String confirmPassword
) {
@Override
public String toString() {
return "ResetPasswordRequest[newPassword=***, confirmPassword=***]";
}
}
Original file line number Diff line number Diff line change
@@ -1,22 +1,15 @@
package com.edgareldy.springboottutorial.entity.token;

import com.edgareldy.springboottutorial.entity.user.AppUser;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import java.time.Instant;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.experimental.SuperBuilder;

/**
* JPA entity mapping the {@code activation_tokens} table. Generated by
Expand All @@ -34,23 +27,9 @@
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
@EqualsAndHashCode(of = "id")
public class ActivationToken {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id", nullable = false)
private AppUser user;

@Column(name = "token", nullable = false)
private String token;

@Column(name = "created_at", nullable = false)
private Instant createdAt;
@SuperBuilder
@EqualsAndHashCode(callSuper = true)
public class ActivationToken extends AuditableToken {

@Column(name = "expires_at")
private Instant expiresAt;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package com.edgareldy.springboottutorial.entity.token;

import com.edgareldy.springboottutorial.entity.user.AppUser;
import jakarta.persistence.Column;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.MappedSuperclass;
import java.time.Instant;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.experimental.SuperBuilder;

/**
* Common mapped superclass for all token entities (activation, password-reset,
* blacklisted JWT, refresh). Centralises the primary key, the owning user
* relationship, the raw token string, and the creation timestamp so each
* concrete entity only declares the fields that make it specific.
* <p>
* Created edgar.muhamyangabo on 11/9/26
* Author : edgar.muhamyangabo
* Date : 11/9/26
* Project : spring-boot-tutorial
*/
@MappedSuperclass
@Getter
@Setter
@NoArgsConstructor
@SuperBuilder
@EqualsAndHashCode(of = "id")
public abstract class AuditableToken {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id", nullable = false)
private AppUser user;

@Column(name = "token", nullable = false, length = 768)
private String token;

@Column(name = "created_at", nullable = false)
private Instant createdAt;
}
Original file line number Diff line number Diff line change
@@ -1,22 +1,15 @@
package com.edgareldy.springboottutorial.entity.token;

import com.edgareldy.springboottutorial.entity.user.AppUser;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import java.time.Instant;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.experimental.SuperBuilder;

/**
* JPA entity mapping the {@code blacklisted_tokens} table. Recorded by
Expand All @@ -35,30 +28,16 @@
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
@EqualsAndHashCode(of = "id")
public class BlacklistedToken {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id", nullable = false)
private AppUser user;

@Column(name = "token", nullable = false, length = 768)
private String token;
@SuperBuilder
@EqualsAndHashCode(callSuper = true)
public class BlacklistedToken extends AuditableToken {

@Column(name = "jti", nullable = false, unique = true)
private String jti;

@Column(name = "blacklisted_at")
private Instant blacklistedAt;

@Column(name = "created_at", nullable = false)
private Instant createdAt;

@Column(name = "expires_at")
private Instant expiresAt;
}
Original file line number Diff line number Diff line change
@@ -1,22 +1,15 @@
package com.edgareldy.springboottutorial.entity.token;

import com.edgareldy.springboottutorial.entity.user.AppUser;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import java.time.Instant;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.experimental.SuperBuilder;

/**
* JPA entity mapping the {@code password_reset_tokens} table. Generated by
Expand All @@ -35,20 +28,9 @@
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
@EqualsAndHashCode(of = "id")
public class PasswordResetToken {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id", nullable = false)
private AppUser user;

@Column(name = "token", nullable = false)
private String token;
@SuperBuilder
@EqualsAndHashCode(callSuper = true)
public class PasswordResetToken extends AuditableToken {

@Column(name = "type", nullable = false)
private String type;
Expand Down
Loading
Loading