feat(pat): token management API and admin revocation

Users list, create and revoke their own tokens under /api/user/tokens; the
plaintext is returned once at creation. Admins can list a user's tokens and
revoke any token, and the admin revoke-sessions action now revokes the user's
tokens too. Creation and revocation are written to auth_events.
This commit is contained in:
arc53-machine committed 2026-09-19 23:39:58 +01:00
1 parent 174130607a
commit 3a74aa23f0
2 files changed
+242 -1

No files matched your search

+13 -1
View File
@@ -23,6 +23,9 @@ from docsgpt.api.user.authz import ROLE_ADMIN, admin_required
from docsgpt.storage.db.repositories.admin_stats import AdminStatsRepository
from docsgpt.storage.db.repositories.auth_events import AuthEventsRepository
from docsgpt.storage.db.repositories.device_audit_log import DeviceAuditLogRepository
from docsgpt.storage.db.repositories.personal_access_tokens import (
PersonalAccessTokensRepository,
)
from docsgpt.storage.db.repositories.token_usage import TokenUsageRepository
from docsgpt.storage.db.repositories.user_roles import UserRolesRepository
from docsgpt.storage.db.repositories.users import UsersRepository
@@ -246,12 +249,21 @@ class AdminUserSessionsResource(Resource):
"""Force-logout: revoke the user's live OIDC sessions (best-effort)."""
ok = denylist.deny_user(user_id)
with db_session() as conn:
# A forced logout that left API credentials alive would not be one.
tokens_revoked = PersonalAccessTokensRepository(conn).revoke_all_for_user(
user_id, reason="admin_sessions_revoked"
)
AuthEventsRepository(conn).insert(
user_id,
"admin_sessions_revoked",
ip=request.remote_addr,
user_agent=request.headers.get("User-Agent"),
metadata={"by": _actor(), "via": "admin_api", "persisted": ok},
metadata={
"by": _actor(),
"via": "admin_api",
"persisted": ok,
"personal_access_tokens_revoked": tokens_revoked,
},
)
return make_response(jsonify({"success": True, "revoked": ok}), 200)