mirror of
https://github.com/hansjone/netx.git
synced 2026-10-09 10:40:46 +08:00
Harden auth with revocable sessions, cookies, and single-login default.
Issue short-lived access JWTs backed by AuthSession rows, HttpOnly cookies with refresh rotation, idle timeout, session management UI, WebCRT ownership caps, and optional Redis login rate limits; new logins revoke other sessions by default. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
ba33ab5c4f
commit
20c2fcd496
34 changed files with 1462 additions and 149 deletions
|
|
@ -8,7 +8,7 @@ from .alarms import (
|
|||
ImportErrorRow,
|
||||
ImportJob,
|
||||
)
|
||||
from .auth import ApiToken, AppUser, AuditLog
|
||||
from .auth import ApiToken, AppUser, AuditLog, AuthSession
|
||||
from .config_sync import (
|
||||
ConfigSyncCycle,
|
||||
ConfigSyncPolicy,
|
||||
|
|
@ -93,6 +93,7 @@ __all__ = [
|
|||
"AppUser",
|
||||
"AuditLog",
|
||||
"ApiToken",
|
||||
"AuthSession",
|
||||
"ConfigSyncPolicy",
|
||||
"ConfigSyncCycle",
|
||||
"ConfigSyncTask",
|
||||
|
|
|
|||
|
|
@ -61,3 +61,21 @@ class ApiToken(Base):
|
|||
expires_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True, index=True)
|
||||
last_used_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True)
|
||||
revoked_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True)
|
||||
|
||||
|
||||
class AuthSession(Base):
|
||||
"""Server-side JWT session (jti). Logout / password change can revoke without waiting for exp."""
|
||||
|
||||
__tablename__ = "auth_session"
|
||||
|
||||
id: Mapped[str] = mapped_column(String(64), primary_key=True) # JWT jti
|
||||
user_id: Mapped[str] = mapped_column(String(64), index=True)
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime, default=utcnow_naive)
|
||||
expires_at: Mapped[datetime] = mapped_column(DateTime, index=True)
|
||||
revoked_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True, index=True)
|
||||
client_ip: Mapped[str] = mapped_column(String(128), default="")
|
||||
user_agent: Mapped[str] = mapped_column(String(512), default="")
|
||||
last_seen_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True)
|
||||
# Opaque refresh token (hashed); longer-lived than access JWT.
|
||||
refresh_token_hash: Mapped[str] = mapped_column(String(128), default="", index=True)
|
||||
refresh_expires_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True, index=True)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue