Files
vod-system/src/main/java/com/ddf/vodsystem/entities/TokenFamily.java
Dylan De Faoite 222b0b60f7
Some checks failed
CI / Maven unit tests (push) Successful in 15s
CI / Build and push images (push) Has been cancelled
CI / Deploy to server (push) Has been cancelled
feat(security): add refresh token capability to API (#52)
## 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
2026-10-16 20:56:02 +01:00

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;
}