WIP: feat(security): implement refresh tokens #49
35
src/main/java/com/ddf/vodsystem/entities/RefreshToken.java
Normal file
35
src/main/java/com/ddf/vodsystem/entities/RefreshToken.java
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
package com.ddf.vodsystem.entities;
|
||||||
|
|
||||||
|
import jakarta.persistence.*;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "refresh_tokens")
|
||||||
|
@Data
|
||||||
|
public class RefreshToken {
|
||||||
|
@Id
|
||||||
|
@Column(name = "id", nullable = false, unique = true)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
|
@JoinColumn(name = "user_id", nullable = false)
|
||||||
|
private User user;
|
||||||
|
|
||||||
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
|
@JoinColumn(name = "family_id", nullable = false)
|
||||||
|
private TokenFamily tokenFamily;
|
||||||
|
|
||||||
|
@Column(name = "token_hash", unique = true, nullable = false)
|
||||||
|
private byte[] tokenHash;
|
||||||
|
|
||||||
|
@Column(name = "expires_at", nullable = false)
|
||||||
|
private Instant expiresAt;
|
||||||
|
|
||||||
|
@Column(name = "used_at")
|
||||||
|
private Instant usedAt;
|
||||||
|
|
||||||
|
@Column(name = "revoked_at")
|
||||||
|
private Instant revokedAt;
|
||||||
|
}
|
||||||
25
src/main/java/com/ddf/vodsystem/entities/TokenFamily.java
Normal file
25
src/main/java/com/ddf/vodsystem/entities/TokenFamily.java
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
package com.ddf.vodsystem.entities;
|
||||||
|
|
||||||
|
import jakarta.persistence.*;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "token_families")
|
||||||
|
@Data
|
||||||
|
public class TokenFamily {
|
||||||
|
@Id
|
||||||
|
@Column(name = "id", nullable = false, unique = true)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
|
@JoinColumn(name = "user_id", nullable = false)
|
||||||
|
private User user;
|
||||||
|
|
||||||
|
@Column(name = "expires_at", nullable = false)
|
||||||
|
private Instant expiresAt;
|
||||||
|
|
||||||
|
@Column(name = "revoked_at")
|
||||||
|
private Instant revokedAt;
|
||||||
|
}
|
||||||
31
src/main/resources/db/migrations/V2__refresh_tokens.sql
Normal file
31
src/main/resources/db/migrations/V2__refresh_tokens.sql
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
CREATE TABLE IF NOT EXISTS token_families
|
||||||
|
(
|
||||||
|
id BIGSERIAL PRIMARY KEY NOT NULl,
|
||||||
|
user_id BIGSERIAL NOT NULL,
|
||||||
|
expires_at TIMESTAMPTZ NOT NULL,
|
||||||
|
revoked_at TIMESTAMPTZ,
|
||||||
|
|
||||||
|
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_token_families_user_id
|
||||||
|
ON token_families (user_id);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS refresh_tokens
|
||||||
|
(
|
||||||
|
id BIGSERIAL PRIMARY KEY NOT NULL,
|
||||||
|
user_id BIGSERIAL NOT NULL,
|
||||||
|
family_id BIGSERIAL NOT NULL,
|
||||||
|
token_hash BYTEA NOT NULL,
|
||||||
|
expires_at TIMESTAMPTZ NOT NULL,
|
||||||
|
used_at TIMESTAMPTZ,
|
||||||
|
revoked_at TIMESTAMPTZ,
|
||||||
|
|
||||||
|
CONSTRAINT refresh_tokens_hash_len CHECK (octet_length(token_hash) = 32),
|
||||||
|
|
||||||
|
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
|
||||||
|
FOREIGN KEY (family_id) REFERENCES token_families(id) ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_refresh_tokens_family_id
|
||||||
|
ON refresh_tokens (family_id);
|
||||||
Reference in New Issue
Block a user