authgent-server 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. authgent_server/__init__.py +3 -0
  2. authgent_server/app.py +155 -0
  3. authgent_server/cli.py +1103 -0
  4. authgent_server/config.py +128 -0
  5. authgent_server/crypto.py +33 -0
  6. authgent_server/db.py +81 -0
  7. authgent_server/dependencies.py +210 -0
  8. authgent_server/endpoints/__init__.py +32 -0
  9. authgent_server/endpoints/agents.py +88 -0
  10. authgent_server/endpoints/audit.py +95 -0
  11. authgent_server/endpoints/authorize.py +204 -0
  12. authgent_server/endpoints/device.py +204 -0
  13. authgent_server/endpoints/health.py +51 -0
  14. authgent_server/endpoints/introspect.py +49 -0
  15. authgent_server/endpoints/register.py +22 -0
  16. authgent_server/endpoints/revoke.py +33 -0
  17. authgent_server/endpoints/stepup.py +97 -0
  18. authgent_server/endpoints/token.py +120 -0
  19. authgent_server/endpoints/token_check.py +151 -0
  20. authgent_server/endpoints/token_inspect.py +134 -0
  21. authgent_server/endpoints/wellknown.py +107 -0
  22. authgent_server/errors.py +138 -0
  23. authgent_server/logging.py +107 -0
  24. authgent_server/middleware/__init__.py +1 -0
  25. authgent_server/middleware/cors.py +23 -0
  26. authgent_server/middleware/error_handler.py +77 -0
  27. authgent_server/middleware/rate_limit.py +76 -0
  28. authgent_server/middleware/request_id.py +38 -0
  29. authgent_server/models/__init__.py +33 -0
  30. authgent_server/models/agent.py +39 -0
  31. authgent_server/models/audit_log.py +22 -0
  32. authgent_server/models/authorization_code.py +26 -0
  33. authgent_server/models/base.py +29 -0
  34. authgent_server/models/consent.py +29 -0
  35. authgent_server/models/delegation_receipt.py +19 -0
  36. authgent_server/models/device_code.py +25 -0
  37. authgent_server/models/oauth_client.py +35 -0
  38. authgent_server/models/refresh_token.py +25 -0
  39. authgent_server/models/signing_key.py +20 -0
  40. authgent_server/models/stepup_request.py +24 -0
  41. authgent_server/models/token_blocklist.py +17 -0
  42. authgent_server/models/user.py +19 -0
  43. authgent_server/providers/__init__.py +1 -0
  44. authgent_server/providers/attestation.py +15 -0
  45. authgent_server/providers/events.py +60 -0
  46. authgent_server/providers/hitl.py +167 -0
  47. authgent_server/providers/keys.py +15 -0
  48. authgent_server/providers/policy.py +17 -0
  49. authgent_server/providers/protocols.py +116 -0
  50. authgent_server/schemas/__init__.py +1 -0
  51. authgent_server/schemas/agent.py +65 -0
  52. authgent_server/schemas/client.py +73 -0
  53. authgent_server/schemas/common.py +42 -0
  54. authgent_server/schemas/token.py +36 -0
  55. authgent_server/services/__init__.py +1 -0
  56. authgent_server/services/agent_service.py +166 -0
  57. authgent_server/services/audit_service.py +47 -0
  58. authgent_server/services/client_service.py +162 -0
  59. authgent_server/services/consent_service.py +60 -0
  60. authgent_server/services/delegation_service.py +152 -0
  61. authgent_server/services/dpop_service.py +156 -0
  62. authgent_server/services/external_oidc.py +220 -0
  63. authgent_server/services/jwks_service.py +204 -0
  64. authgent_server/services/stepup_service.py +90 -0
  65. authgent_server/services/token_service.py +647 -0
  66. authgent_server/templates/consent.html +88 -0
  67. authgent_server/utils.py +22 -0
  68. authgent_server-0.1.0.dist-info/METADATA +258 -0
  69. authgent_server-0.1.0.dist-info/RECORD +71 -0
  70. authgent_server-0.1.0.dist-info/WHEEL +4 -0
  71. authgent_server-0.1.0.dist-info/entry_points.txt +2 -0
@@ -0,0 +1,3 @@
1
+ """authgent-server — The open-source identity provider for AI agents."""
2
+
3
+ __version__ = "0.1.0"
authgent_server/app.py ADDED
@@ -0,0 +1,155 @@
1
+ """FastAPI app factory with lifespan, middleware, and cleanup tasks."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import asyncio
6
+ from collections.abc import AsyncGenerator
7
+ from contextlib import asynccontextmanager
8
+ from datetime import UTC, datetime
9
+
10
+ import structlog
11
+ from fastapi import FastAPI
12
+ from sqlalchemy import text
13
+
14
+ from authgent_server.config import Settings, get_settings
15
+ from authgent_server.db import get_engine, get_session_factory
16
+ from authgent_server.endpoints import api_router
17
+ from authgent_server.errors import AuthgentError
18
+ from authgent_server.middleware.cors import setup_cors
19
+ from authgent_server.middleware.error_handler import (
20
+ authgent_error_handler,
21
+ unhandled_exception_handler,
22
+ )
23
+ from authgent_server.middleware.rate_limit import RateLimitMiddleware
24
+ from authgent_server.middleware.request_id import RequestIdMiddleware
25
+ from authgent_server.models.base import Base
26
+ from authgent_server.services.jwks_service import JWKSService
27
+
28
+ logger = structlog.get_logger()
29
+
30
+ # Cleanup queries — explicit per-table, no string interpolation
31
+ _CLEANUP_QUERIES = {
32
+ "token_blocklist": text("DELETE FROM token_blocklist WHERE expires_at < :now"),
33
+ "authorization_codes": text("DELETE FROM authorization_codes WHERE expires_at < :now"),
34
+ "device_codes": text("DELETE FROM device_codes WHERE expires_at < :now"),
35
+ "refresh_tokens": text("DELETE FROM refresh_tokens WHERE expires_at < :now"),
36
+ "stepup_requests": text(
37
+ "UPDATE stepup_requests SET status = 'expired' "
38
+ "WHERE expires_at < :now AND status = 'pending'"
39
+ ),
40
+ }
41
+
42
+ # Cleanup intervals in seconds
43
+ _CLEANUP_INTERVALS = {
44
+ "token_blocklist": 3600,
45
+ "authorization_codes": 900,
46
+ "device_codes": 900,
47
+ "refresh_tokens": 3600,
48
+ "stepup_requests": 60,
49
+ }
50
+
51
+
52
+ async def _cleanup_loop(
53
+ table: str,
54
+ interval: int,
55
+ shutdown: asyncio.Event,
56
+ session_factory: object,
57
+ ) -> None:
58
+ """Background cleanup task for expired records."""
59
+ query = _CLEANUP_QUERIES[table]
60
+ while not shutdown.is_set():
61
+ try:
62
+ async with session_factory() as session: # type: ignore[operator]
63
+ await session.execute(query, {"now": datetime.now(UTC)})
64
+ await session.commit()
65
+ except Exception as e:
66
+ logger.warning("cleanup_failed", table=table, error=str(e))
67
+ try:
68
+ await asyncio.wait_for(shutdown.wait(), timeout=interval)
69
+ break
70
+ except TimeoutError:
71
+ continue
72
+
73
+
74
+ def _configure_logging(debug: bool = False) -> None:
75
+ """Configure structlog with JSON output and secret redaction."""
76
+ from authgent_server.logging import configure_logging
77
+
78
+ configure_logging(debug=debug, json_output=not debug)
79
+
80
+
81
+ @asynccontextmanager
82
+ async def lifespan(app: FastAPI) -> AsyncGenerator[None, None]: # type: ignore[type-arg]
83
+ """Server lifecycle — startup and shutdown."""
84
+ settings = get_settings()
85
+ _configure_logging(settings.debug)
86
+
87
+ engine = get_engine(settings)
88
+ session_factory = get_session_factory(settings)
89
+
90
+ # Create tables (for dev/SQLite — production uses Alembic)
91
+ async with engine.begin() as conn:
92
+ await conn.run_sync(Base.metadata.create_all)
93
+
94
+ # Ensure signing key exists
95
+ jwks = JWKSService(settings)
96
+ async with session_factory() as session:
97
+ await jwks.get_active_key(session)
98
+
99
+ # Start background cleanup tasks
100
+ shutdown_event = asyncio.Event()
101
+ cleanup_tasks = [
102
+ asyncio.create_task(_cleanup_loop(table, interval, shutdown_event, session_factory))
103
+ for table, interval in _CLEANUP_INTERVALS.items()
104
+ ]
105
+
106
+ logger.info(
107
+ "server_started",
108
+ host=settings.host,
109
+ port=settings.port,
110
+ database=settings.database_url.split("://")[0],
111
+ )
112
+
113
+ yield
114
+
115
+ # Shutdown
116
+ logger.info("server_shutting_down")
117
+ shutdown_event.set()
118
+ done, pending = await asyncio.wait(cleanup_tasks, timeout=5.0)
119
+ for task in pending:
120
+ task.cancel()
121
+ await engine.dispose()
122
+ logger.info("server_stopped")
123
+
124
+
125
+ def create_app(settings: Settings | None = None) -> FastAPI:
126
+ """App factory — creates and configures the FastAPI application."""
127
+ if settings is None:
128
+ settings = get_settings()
129
+
130
+ app = FastAPI(
131
+ title="authgent",
132
+ description="The open-source identity provider for AI agents",
133
+ version="0.1.0",
134
+ lifespan=lifespan,
135
+ docs_url="/docs",
136
+ redoc_url="/redoc",
137
+ )
138
+
139
+ # Error handlers
140
+ app.add_exception_handler(AuthgentError, authgent_error_handler) # type: ignore[arg-type]
141
+ app.add_exception_handler(Exception, unhandled_exception_handler) # type: ignore[arg-type]
142
+
143
+ # Middleware (order matters — outermost first)
144
+ app.add_middleware(RequestIdMiddleware)
145
+ setup_cors(app, settings)
146
+ app.add_middleware(
147
+ RateLimitMiddleware,
148
+ rate=settings.token_rate_limit,
149
+ paths=["/token"],
150
+ )
151
+
152
+ # Routes
153
+ app.include_router(api_router)
154
+
155
+ return app