API Reference
User API key management module.
This module provides API key lifecycle management including creation, validation, revocation, and deletion.
Exports
- CRUD: get_api_keys_by_user_id, get_api_key_by_id, get_api_key_by_hash, create_api_key, update_last_used, revoke_api_key, delete_api_key
- Schemas: UsersApiKeyCreate, UsersApiKeyRead, UsersApiKeyCreated
- Models: UsersApiKeys (ORM model)
- Utils: generate_api_key, hash_api_key, validate_api_key_scopes
UsersApiKeyCreate
Bases: BaseModel
Schema for creating a new API key.
API-key creation is a sensitive, persistent grant of
account access — it must be gated by step-up
verification. The caller MUST supply current_password,
and an MFA code when MFA is enabled on the account.
For SSO-only accounts (no local password set), the password
field may be omitted and the password check is skipped — see
:func:users.users.utils.verify_step_up_credentials for the
rationale and the known coverage gap.
Attributes:
| Name | Type | Description |
|---|---|---|
name |
StrictStr
|
User-friendly label for the key. |
scopes |
list[StrictStr]
|
List containing the supported API key scope. |
expires_at |
datetime | None
|
Optional expiration datetime. None means the key never expires. |
current_password |
StrictStr | None
|
Caller's existing password (step-up verification). Required when the account has a local password; may be omitted for SSO-only accounts. |
mfa_code |
StrictStr | None
|
TOTP or backup code, required when MFA is enabled on the account. |
Source code in backend/app/users/users_api_keys/schema.py
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 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 86 87 88 89 90 91 92 93 94 95 96 97 | |
scopes_must_be_valid
classmethod
scopes_must_be_valid(v)
Validate API keys only grant activity upload access.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
v
|
list[str]
|
List of scope strings to validate. |
required |
Returns:
| Type | Description |
|---|---|
list[str]
|
Validated list of scope strings. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If any scope is not supported. |
Source code in backend/app/users/users_api_keys/schema.py
73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 | |
UsersApiKeyCreated
Bases: UsersApiKeyRead
API key creation response schema.
Extends UsersApiKeyRead with the raw key, returned only once at creation time. The raw key is never stored and cannot be retrieved again.
Attributes:
| Name | Type | Description |
|---|---|---|
key |
StrictStr
|
The full raw API key. Show to the user once and discard. |
Source code in backend/app/users/users_api_keys/schema.py
138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 | |
UsersApiKeyRead
Bases: BaseModel
API key read schema for list/detail responses.
Never exposes the raw key or its hash. Safe to return in all authenticated GET responses.
Attributes:
| Name | Type | Description |
|---|---|---|
id |
StrictStr
|
Unique key identifier (UUID). |
user_id |
int
|
Owner's user ID. |
name |
StrictStr
|
User-friendly label. |
key_prefix |
StrictStr
|
First 8 chars of the random part. |
scopes |
StrictStr
|
JSON-encoded list of granted scopes. |
expires_at |
datetime | None
|
Optional expiration timestamp. |
last_used_at |
datetime | None
|
Last successful use timestamp. |
created_at |
datetime
|
Key creation timestamp. |
is_active |
StrictBool
|
Whether the key is currently active. |
Source code in backend/app/users/users_api_keys/schema.py
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 | |
UsersApiKeysModel
Bases: Base
User API key for third-party integrations.
Attributes:
| Name | Type | Description |
|---|---|---|
id |
Mapped[str]
|
Unique key identifier (UUID). |
user_id |
Mapped[int]
|
Foreign key to users table. |
name |
Mapped[str]
|
User-friendly label for the key. |
key_prefix |
Mapped[str]
|
First 8 chars of the random part, shown in UI for identification. |
key_hash |
Mapped[str]
|
SHA-256 hex digest of the full raw key. Never stored plaintext. |
scopes |
Mapped[str]
|
JSON-encoded list of granted scopes. |
expires_at |
Mapped[datetime | None]
|
Optional expiration timestamp. NULL means the key never expires. |
last_used_at |
Mapped[datetime | None]
|
Timestamp of last successful authentication. Updated on each use. |
created_at |
Mapped[datetime]
|
Key creation timestamp. |
is_active |
Mapped[bool]
|
Whether the key is active. Set to False to revoke without deleting. |
users |
Mapped[Users]
|
Relationship to Users model. |
Source code in backend/app/users/users_api_keys/models.py
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 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 86 87 88 89 90 91 92 93 94 | |
create_api_key
create_api_key(user_id, data, db)
Create a new API key for a user.
Generates a cryptographically random key, hashes it with SHA-256, and stores only the hash. Returns both the ORM object and the raw key so the caller can include it in the response (shown once only).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
user_id
|
int
|
The ID of the owning user. |
required |
data
|
UsersApiKeyCreate
|
Validated creation schema. |
required |
db
|
Session
|
SQLAlchemy database session. |
required |
Returns:
| Type | Description |
|---|---|
tuple[UsersApiKeys, str]
|
Tuple of (UsersApiKeys ORM object, raw key string). |
Raises:
| Type | Description |
|---|---|
HTTPException
|
If a database error occurs. |
Source code in backend/app/users/users_api_keys/crud.py
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 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 | |
delete_api_key
delete_api_key(api_key_id, user_id, db)
Permanently delete an API key.
Hard-delete for GDPR-style removal. The key hash is gone and cannot be authenticated against after this.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
api_key_id
|
str
|
The UUID of the API key. |
required |
user_id
|
int
|
The ID of the owning user. |
required |
db
|
Session
|
SQLAlchemy database session. |
required |
Raises:
| Type | Description |
|---|---|
HTTPException
|
If the key is not found or does not belong to the user (404), or a database error occurs. |
Source code in backend/app/users/users_api_keys/crud.py
236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 | |
generate_api_key
generate_api_key()
Generate a new raw API key.
Keys have the format endurain_<token> where
<token> is 32 cryptographically random bytes
encoded as base64url (43 characters). Total entropy
is 256 bits.
Returns:
| Type | Description |
|---|---|
str
|
A new raw API key string. |
Source code in backend/app/users/users_api_keys/utils.py
8 9 10 11 12 13 14 15 16 17 18 19 20 | |
get_api_key_by_hash
get_api_key_by_hash(key_hash, db)
Retrieve an API key by its SHA-256 hash.
Used during request authentication to look up the key record from the hashed incoming value.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
key_hash
|
str
|
SHA-256 hex digest of the raw key. |
required |
db
|
Session
|
SQLAlchemy database session. |
required |
Returns:
| Type | Description |
|---|---|
UsersApiKeys | None
|
The API key object if found, None otherwise. |
Raises:
| Type | Description |
|---|---|
HTTPException
|
If a database error occurs. |
Source code in backend/app/users/users_api_keys/crud.py
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 | |
get_api_key_by_id
get_api_key_by_id(api_key_id, user_id, db)
Retrieve a single API key by its ID and owner.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
api_key_id
|
str
|
The UUID of the API key. |
required |
user_id
|
int
|
The ID of the owning user. |
required |
db
|
Session
|
SQLAlchemy database session. |
required |
Returns:
| Type | Description |
|---|---|
UsersApiKeys | None
|
The API key object if found, None otherwise. |
Raises:
| Type | Description |
|---|---|
HTTPException
|
If a database error occurs. |
Source code in backend/app/users/users_api_keys/crud.py
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | |
get_api_keys_by_user_id
get_api_keys_by_user_id(user_id, db)
Retrieve all API keys for a user.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
user_id
|
int
|
The ID of the owning user. |
required |
db
|
Session
|
SQLAlchemy database session. |
required |
Returns:
| Type | Description |
|---|---|
list[UsersApiKeys]
|
List of API key objects ordered by creation |
list[UsersApiKeys]
|
date descending. |
Raises:
| Type | Description |
|---|---|
HTTPException
|
If a database error occurs. |
Source code in backend/app/users/users_api_keys/crud.py
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 | |
hash_api_key
hash_api_key(raw_key)
Compute the SHA-256 hex digest of a raw API key.
High-entropy secrets do not require a slow KDF (Argon2/bcrypt). SHA-256 is the industry standard for hashing tokens of this entropy level.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
raw_key
|
str
|
The plain-text API key to hash. |
required |
Returns:
| Type | Description |
|---|---|
str
|
Lowercase hex-encoded SHA-256 digest (64 chars). |
Source code in backend/app/users/users_api_keys/utils.py
23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | |
revoke_api_key
revoke_api_key(api_key_id, user_id, db)
Revoke an API key by setting is_active to False.
Soft-delete: the record is retained for audit purposes but the key will be rejected on next use.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
api_key_id
|
str
|
The UUID of the API key. |
required |
user_id
|
int
|
The ID of the owning user. |
required |
db
|
Session
|
SQLAlchemy database session. |
required |
Raises:
| Type | Description |
|---|---|
HTTPException
|
If the key is not found or does not belong to the user (404), or a database error occurs. |
Source code in backend/app/users/users_api_keys/crud.py
193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 | |
update_last_used
update_last_used(api_key_id, db)
Update the last_used_at timestamp for an API key.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
api_key_id
|
str
|
The UUID of the API key. |
required |
db
|
Session
|
SQLAlchemy database session. |
required |
Raises:
| Type | Description |
|---|---|
HTTPException
|
If the key is not found (404) or a database error occurs. |
Source code in backend/app/users/users_api_keys/crud.py
162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 | |
validate_api_key_scopes
validate_api_key_scopes(requested_scopes, _user_access_type)
Validate requested scopes against supported API-key scopes.
API keys currently support only activity uploads. Keeping this allow-list separate from the full JWT scope set prevents API keys from silently gaining access when new unified-auth endpoints are added later.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
requested_scopes
|
list[str]
|
List of scopes the caller wants to assign to the new API key. |
required |
_user_access_type
|
str
|
The owner's access type. Accepted for router compatibility but not used while API-key scopes are restricted to activity uploads. |
required |
Raises:
| Type | Description |
|---|---|
ValueError
|
If any requested scope is not supported. |
Source code in backend/app/users/users_api_keys/utils.py
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 68 | |