## Add backend refresh tokens
Adds rotating server-side refresh tokens so the access token can be short-lived (5m) without forcing frequent re-login.
- New `POST /api/v1/users/refresh` exchanges the `refreshToken` cookie for a new refresh token and JWT
- Each login starts a token family; reusing a used token after the 5s grace period revokes the whole family
- Logout revokes the family; an hourly job deletes dead families
- Google ID token verification extracted into `GoogleVerifierService`; config moved to a validated `AuthProperties`
- Unauthenticated requests now return 401 instead of 403
- Migration `V2__refresh_tokens.sql` adds `token_families` and `refresh_tokens`
**Breaking changes**
- Cookie `token` renamed to `accessToken` (existing sessions are logged out)
- `/login` and `/refresh` no longer return the JWT in the body, only `{ accessTokenExpiresIn }`
- `jwt.secret.key` / `jwt.expiration` replaced by `app.security.*`; `JWT_SECRET_KEY` must be 32+ chars
- Frontend must call `/refresh` on 401 or before the access token expires
Reviewed-on: #52
32 lines
754 B
Java
32 lines
754 B
Java
package com.ddf.vodsystem.entities;
|
|
|
|
import jakarta.persistence.*;
|
|
import lombok.Data;
|
|
import org.hibernate.annotations.CreationTimestamp;
|
|
|
|
import java.time.Instant;
|
|
|
|
@Entity
|
|
@Table(name = "token_families")
|
|
@Data
|
|
public class TokenFamily {
|
|
@Id
|
|
@Column(name = "id", nullable = false, unique = true)
|
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
|
private Long id;
|
|
|
|
@ManyToOne(fetch = FetchType.LAZY)
|
|
@JoinColumn(name = "user_id", nullable = false)
|
|
private User user;
|
|
|
|
@Column(name = "created_at", nullable = false)
|
|
@CreationTimestamp
|
|
private Instant createdAt;
|
|
|
|
@Column(name = "expires_at", nullable = false)
|
|
private Instant expiresAt;
|
|
|
|
@Column(name = "revoked_at")
|
|
private Instant revokedAt;
|
|
}
|