Cap refresh chain lifetime via RefreshChainAbsoluteTTL

Sessions had an absolute cap (created_at + SessionAbsoluteTTL) but the
JWT path only had per-token TTL on the refresh row, letting a
well-behaved client refresh indefinitely. Add chain_started_at to
authkit_tokens, copy it forward on every rotation, and reject in
RefreshJWT when now > chainStartedAt + RefreshChainAbsoluteTTL.
Default 30d, mirroring SessionAbsoluteTTL.

Schema, verifier, queries, model, and integration test updated.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
juancwu 2026-04-26 23:41:02 +00:00
commit ca5525d4bd
11 changed files with 129 additions and 53 deletions

View file

@ -15,8 +15,8 @@ func (a *Auth) storeCreateToken(ctx context.Context, t *Token) error {
}
_, err := a.db.ExecContext(ctx, a.q.createToken,
t.Hash, string(t.Kind), uuidArg(t.UserID), chainArg(t.ChainID),
nullableTime(t.ConsumedAt), nullableInt(t.AttemptsRemaining),
t.CreatedAt, t.ExpiresAt)
nullableTime(t.ChainStartedAt), nullableTime(t.ConsumedAt),
nullableInt(t.AttemptsRemaining), t.CreatedAt, t.ExpiresAt)
if err != nil {
return errx.Wrap(op, err)
}
@ -110,14 +110,15 @@ func (a *Auth) storeDeleteExpiredTokens(ctx context.Context, now time.Time) (int
func scanToken(row rowScanner) (*Token, error) {
var (
t Token
kind string
userIDStr string
chainID sql.NullString
consumedAt sql.NullTime
attempts sql.NullInt32
t Token
kind string
userIDStr string
chainID sql.NullString
chainStartedAt sql.NullTime
consumedAt sql.NullTime
attempts sql.NullInt32
)
if err := row.Scan(&t.Hash, &kind, &userIDStr, &chainID,
if err := row.Scan(&t.Hash, &kind, &userIDStr, &chainID, &chainStartedAt,
&consumedAt, &attempts, &t.CreatedAt, &t.ExpiresAt); err != nil {
return nil, err
}
@ -128,6 +129,7 @@ func scanToken(row rowScanner) (*Token, error) {
}
t.UserID = uid
t.ChainID = scanNullStringPtr(chainID)
t.ChainStartedAt = scanNullTimePtr(chainStartedAt)
t.ConsumedAt = scanNullTimePtr(consumedAt)
t.AttemptsRemaining = scanNullIntPtr(attempts)
return &t, nil