Feat v0.6.9 cookie fix roadmap (#44)
Co-authored-by: Jeffrey Smith <jasafpro@gmail.com> Co-committed-by: Jeffrey Smith <jasafpro@gmail.com>
This commit was merged in pull request #44.
This commit is contained in:
@@ -13,6 +13,13 @@ import (
|
||||
// ErrSystemGroup is returned when attempting to delete a system-sourced group.
|
||||
var ErrSystemGroup = errors.New("system groups cannot be deleted")
|
||||
|
||||
// RefreshTokenInfo holds extended metadata for a refresh token row.
|
||||
type RefreshTokenInfo struct {
|
||||
UserID string
|
||||
KeepLogin bool
|
||||
LastActivityAt *time.Time
|
||||
}
|
||||
|
||||
// =========================================
|
||||
// STORES — Data Access Layer
|
||||
// =========================================
|
||||
@@ -79,11 +86,13 @@ type UserStore interface {
|
||||
ListActiveUserIDs(ctx context.Context) ([]string, error)
|
||||
|
||||
// Refresh tokens
|
||||
CreateRefreshToken(ctx context.Context, userID, tokenHash string, expiresAt time.Time) error
|
||||
CreateRefreshToken(ctx context.Context, userID, tokenHash string, expiresAt time.Time, keepLogin bool) error
|
||||
GetRefreshToken(ctx context.Context, tokenHash string) (userID string, err error)
|
||||
GetRefreshTokenInfo(ctx context.Context, tokenHash string) (*RefreshTokenInfo, error)
|
||||
RevokeRefreshToken(ctx context.Context, tokenHash string) error
|
||||
RevokeAllRefreshTokens(ctx context.Context, userID string) error
|
||||
CleanExpiredTokens(ctx context.Context) error
|
||||
UpdateRefreshTokenActivity(ctx context.Context, userID string) error
|
||||
|
||||
// ── CS1 additions ──
|
||||
|
||||
|
||||
@@ -142,8 +142,10 @@ func (s *UserStore) ListActiveUserIDs(ctx context.Context) ([]string, error) {
|
||||
|
||||
// ── Refresh Tokens ──────────────────────────
|
||||
|
||||
func (s *UserStore) CreateRefreshToken(ctx context.Context, userID, tokenHash string, expiresAt time.Time) error {
|
||||
_, err := DB.ExecContext(ctx, `INSERT INTO refresh_tokens (user_id, token_hash, expires_at) VALUES ($1, $2, $3)`, userID, tokenHash, expiresAt)
|
||||
func (s *UserStore) CreateRefreshToken(ctx context.Context, userID, tokenHash string, expiresAt time.Time, keepLogin bool) error {
|
||||
_, err := DB.ExecContext(ctx,
|
||||
`INSERT INTO refresh_tokens (user_id, token_hash, expires_at, keep_login, last_activity_at) VALUES ($1, $2, $3, $4, NOW())`,
|
||||
userID, tokenHash, expiresAt, keepLogin)
|
||||
return err
|
||||
}
|
||||
func (s *UserStore) GetRefreshToken(ctx context.Context, tokenHash string) (string, error) {
|
||||
@@ -151,6 +153,17 @@ func (s *UserStore) GetRefreshToken(ctx context.Context, tokenHash string) (stri
|
||||
err := DB.QueryRowContext(ctx, `SELECT user_id FROM refresh_tokens WHERE token_hash = $1 AND revoked_at IS NULL AND expires_at > NOW()`, tokenHash).Scan(&userID)
|
||||
return userID, err
|
||||
}
|
||||
func (s *UserStore) GetRefreshTokenInfo(ctx context.Context, tokenHash string) (*store.RefreshTokenInfo, error) {
|
||||
var info store.RefreshTokenInfo
|
||||
err := DB.QueryRowContext(ctx,
|
||||
`SELECT user_id, COALESCE(keep_login, false), last_activity_at
|
||||
FROM refresh_tokens WHERE token_hash = $1 AND revoked_at IS NULL AND expires_at > NOW()`,
|
||||
tokenHash).Scan(&info.UserID, &info.KeepLogin, &info.LastActivityAt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &info, nil
|
||||
}
|
||||
func (s *UserStore) RevokeRefreshToken(ctx context.Context, tokenHash string) error {
|
||||
_, err := DB.ExecContext(ctx, "UPDATE refresh_tokens SET revoked_at = NOW() WHERE token_hash = $1", tokenHash)
|
||||
return err
|
||||
@@ -163,6 +176,12 @@ func (s *UserStore) CleanExpiredTokens(ctx context.Context) error {
|
||||
_, err := DB.ExecContext(ctx, "DELETE FROM refresh_tokens WHERE expires_at < NOW() - INTERVAL '30 days'")
|
||||
return err
|
||||
}
|
||||
func (s *UserStore) UpdateRefreshTokenActivity(ctx context.Context, userID string) error {
|
||||
_, err := DB.ExecContext(ctx,
|
||||
"UPDATE refresh_tokens SET last_activity_at = NOW() WHERE user_id = $1 AND revoked_at IS NULL",
|
||||
userID)
|
||||
return err
|
||||
}
|
||||
|
||||
// ── Internal ────────────────────────────────
|
||||
|
||||
|
||||
@@ -148,9 +148,10 @@ func (s *UserStore) ListActiveUserIDs(ctx context.Context) ([]string, error) {
|
||||
|
||||
// ── Refresh Tokens ──────────────────────────
|
||||
|
||||
func (s *UserStore) CreateRefreshToken(ctx context.Context, userID, tokenHash string, expiresAt time.Time) error {
|
||||
_, err := DB.ExecContext(ctx, `INSERT INTO refresh_tokens (id, user_id, token_hash, expires_at) VALUES (?, ?, ?, ?)`,
|
||||
store.NewID(), userID, tokenHash, expiresAt.Format(timeFmt))
|
||||
func (s *UserStore) CreateRefreshToken(ctx context.Context, userID, tokenHash string, expiresAt time.Time, keepLogin bool) error {
|
||||
_, err := DB.ExecContext(ctx,
|
||||
`INSERT INTO refresh_tokens (id, user_id, token_hash, expires_at, keep_login, last_activity_at) VALUES (?, ?, ?, ?, ?, datetime('now'))`,
|
||||
store.NewID(), userID, tokenHash, expiresAt.Format(timeFmt), keepLogin)
|
||||
return err
|
||||
}
|
||||
func (s *UserStore) GetRefreshToken(ctx context.Context, tokenHash string) (string, error) {
|
||||
@@ -158,6 +159,23 @@ func (s *UserStore) GetRefreshToken(ctx context.Context, tokenHash string) (stri
|
||||
err := DB.QueryRowContext(ctx, `SELECT user_id FROM refresh_tokens WHERE token_hash = ? AND revoked_at IS NULL AND expires_at > datetime('now')`, tokenHash).Scan(&userID)
|
||||
return userID, err
|
||||
}
|
||||
func (s *UserStore) GetRefreshTokenInfo(ctx context.Context, tokenHash string) (*store.RefreshTokenInfo, error) {
|
||||
var info store.RefreshTokenInfo
|
||||
var lastAct *string
|
||||
err := DB.QueryRowContext(ctx,
|
||||
`SELECT user_id, COALESCE(keep_login, 0), last_activity_at
|
||||
FROM refresh_tokens WHERE token_hash = ? AND revoked_at IS NULL AND expires_at > datetime('now')`,
|
||||
tokenHash).Scan(&info.UserID, &info.KeepLogin, &lastAct)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if lastAct != nil {
|
||||
if t, err := time.Parse(timeFmt, *lastAct); err == nil {
|
||||
info.LastActivityAt = &t
|
||||
}
|
||||
}
|
||||
return &info, nil
|
||||
}
|
||||
func (s *UserStore) RevokeRefreshToken(ctx context.Context, tokenHash string) error {
|
||||
_, err := DB.ExecContext(ctx, "UPDATE refresh_tokens SET revoked_at = datetime('now') WHERE token_hash = ?", tokenHash)
|
||||
return err
|
||||
@@ -170,6 +188,12 @@ func (s *UserStore) CleanExpiredTokens(ctx context.Context) error {
|
||||
_, err := DB.ExecContext(ctx, "DELETE FROM refresh_tokens WHERE expires_at < datetime('now', '-30 days')")
|
||||
return err
|
||||
}
|
||||
func (s *UserStore) UpdateRefreshTokenActivity(ctx context.Context, userID string) error {
|
||||
_, err := DB.ExecContext(ctx,
|
||||
"UPDATE refresh_tokens SET last_activity_at = datetime('now') WHERE user_id = ? AND revoked_at IS NULL",
|
||||
userID)
|
||||
return err
|
||||
}
|
||||
|
||||
// ── Internal ────────────────────────────────
|
||||
|
||||
|
||||
Reference in New Issue
Block a user