API Reference
Rotated refresh token reuse detection.
RotatedRefreshToken
Bases: Base
Rotated refresh token for token reuse detection.
Attributes:
| Name | Type | Description |
|---|---|---|
id |
Mapped[int]
|
Primary key. |
token_family_id |
Mapped[str]
|
UUID of the token family. |
hashed_token |
Mapped[str]
|
Hashed old refresh token. |
rotation_count |
Mapped[int]
|
Which rotation this token belonged to. |
rotated_at |
Mapped[datetime]
|
When this token was rotated. |
expires_at |
Mapped[datetime]
|
Cleanup marker (rotated_at + 60 seconds). |
user_session |
Mapped[datetime]
|
Relationship to UsersSessions model. |
Source code in backend/app/users/users_sessions/rotated_refresh_tokens/models.py
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 | |
RotatedRefreshTokenCreate
Bases: BaseModel
Schema for creating a rotated refresh token record.
Attributes:
| Name | Type | Description |
|---|---|---|
token_family_id |
StrictStr
|
UUID of the token family. |
hashed_token |
StrictStr
|
Hashed old refresh token. |
rotation_count |
StrictInt
|
Sequential rotation number for this token. |
rotated_at |
datetime
|
Timestamp when this token was rotated. |
expires_at |
datetime
|
Cleanup marker timestamp. |
Source code in backend/app/users/users_sessions/rotated_refresh_tokens/schema.py
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | |
RotatedRefreshTokenRead
Bases: RotatedRefreshTokenCreate
Schema for reading a rotated refresh token record.
Attributes:
| Name | Type | Description |
|---|---|---|
id |
StrictInt
|
Unique identifier for the rotated token record. |
Source code in backend/app/users/users_sessions/rotated_refresh_tokens/schema.py
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 | |
check_token_reuse
check_token_reuse(raw_token, db)
Check if a refresh token has been reused (already rotated).
Uses HMAC-SHA256 with the server secret to hash the token for lookup, ensuring deterministic matching.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
raw_token
|
str
|
The raw refresh token to check. |
required |
db
|
Session
|
SQLAlchemy database session. |
required |
Returns:
| Type | Description |
|---|---|
tuple[bool, bool]
|
Tuple of (is_reused, in_grace_period): - (False, False): Token is valid, not reused. - (True, True): Reused but within 60s grace period. - (True, False): Reused after grace period - THEFT! |
Raises:
| Type | Description |
|---|---|
HTTPException
|
If lookup fails. |
Source code in backend/app/users/users_sessions/rotated_refresh_tokens/utils.py
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 | |
cleanup_expired_rotated_tokens
cleanup_expired_rotated_tokens()
Cleanup job to delete expired rotated tokens.
Called by the scheduler to periodically remove tokens that have exceeded the grace period. Should run every 1 minute. Exceptions are caught and logged to avoid breaking the scheduler.
Returns:
| Type | Description |
|---|---|
None
|
None. |
Source code in backend/app/users/users_sessions/rotated_refresh_tokens/utils.py
181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 | |
create_rotated_token
create_rotated_token(rotated_token, db)
Store a rotated refresh token in the database.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
rotated_token
|
RotatedRefreshTokenCreate
|
The rotated token data to store. |
required |
db
|
Session
|
SQLAlchemy database session. |
required |
Returns:
| Type | Description |
|---|---|
RotatedRefreshToken
|
The created RotatedRefreshToken object. |
Raises:
| Type | Description |
|---|---|
HTTPException
|
If database error occurs. |
Source code in backend/app/users/users_sessions/rotated_refresh_tokens/crud.py
37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 | |
delete_by_family
delete_by_family(token_family_id, db)
Delete all rotated tokens for a specific token family.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
token_family_id
|
str
|
The family ID to delete tokens for. |
required |
db
|
Session
|
SQLAlchemy database session. |
required |
Returns:
| Type | Description |
|---|---|
int
|
Number of tokens deleted. |
Raises:
| Type | Description |
|---|---|
HTTPException
|
If database error occurs. |
Source code in backend/app/users/users_sessions/rotated_refresh_tokens/crud.py
93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 | |
delete_expired_tokens
delete_expired_tokens(cutoff_time, db)
Delete rotated tokens older than the cutoff time.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
cutoff_time
|
datetime
|
Tokens with expires_at before this deleted. |
required |
db
|
Session
|
SQLAlchemy database session. |
required |
Returns:
| Type | Description |
|---|---|
int
|
Number of tokens deleted. |
Raises:
| Type | Description |
|---|---|
HTTPException
|
If database error occurs. |
Source code in backend/app/users/users_sessions/rotated_refresh_tokens/crud.py
70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 | |
get_rotated_token_by_hash
get_rotated_token_by_hash(hashed_token, db)
Retrieve a rotated token by its hashed value.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
hashed_token
|
str
|
The hashed refresh token to search for. |
required |
db
|
Session
|
SQLAlchemy database session. |
required |
Returns:
| Type | Description |
|---|---|
RotatedRefreshToken | None
|
The RotatedRefreshToken if found, None otherwise. |
Raises:
| Type | Description |
|---|---|
HTTPException
|
If database error occurs. |
Source code in backend/app/users/users_sessions/rotated_refresh_tokens/crud.py
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | |
hmac_hash_token
hmac_hash_token(token)
Compute HMAC-SHA256 hash of a token for secure lookup.
Uses the server's SECRET_KEY as the HMAC key, providing defense-in-depth: even if the database is compromised, an attacker cannot verify stolen tokens without the key.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
token
|
str
|
The raw refresh token to hash. |
required |
Returns:
| Type | Description |
|---|---|
str
|
Hex-encoded HMAC-SHA256 hash of the token. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If JWT_SECRET_KEY is not configured. |
Source code in backend/app/users/users_sessions/rotated_refresh_tokens/utils.py
22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | |
invalidate_token_family
invalidate_token_family(token_family_id, db)
Invalidate all sessions in a token family due to reuse detection.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
token_family_id
|
str
|
The family ID to invalidate. |
required |
db
|
Session
|
SQLAlchemy database session. |
required |
Returns:
| Type | Description |
|---|---|
int
|
Number of sessions invalidated. |
Raises:
| Type | Description |
|---|---|
HTTPException
|
If invalidation fails. |
Source code in backend/app/users/users_sessions/rotated_refresh_tokens/utils.py
145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 | |
store_rotated_token
store_rotated_token(raw_token, token_family_id, rotation_count, db)
Store an old refresh token after rotation for reuse detection.
Uses HMAC-SHA256 with the server secret to hash the token, enabling deterministic lookups while maintaining security.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
raw_token
|
str
|
The raw refresh token being rotated out. |
required |
token_family_id
|
str
|
UUID of the token family. |
required |
rotation_count
|
int
|
Current rotation count for this token. |
required |
db
|
Session
|
SQLAlchemy database session. |
required |
Raises:
| Type | Description |
|---|---|
HTTPException
|
If storage fails. |
Source code in backend/app/users/users_sessions/rotated_refresh_tokens/utils.py
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 | |