agentauthlayer 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.
agent_auth/users.py ADDED
@@ -0,0 +1,173 @@
1
+ from __future__ import annotations
2
+
3
+ import secrets
4
+ from dataclasses import dataclass
5
+ from datetime import datetime, timedelta, timezone
6
+ from typing import Protocol, TypeVar
7
+
8
+ import bcrypt
9
+
10
+ from agent_auth.principals import user_scopes_for_role
11
+
12
+
13
+ UserT = TypeVar("UserT")
14
+ InviteT = TypeVar("InviteT")
15
+
16
+ DEFAULT_ADMIN_EMAIL = "admin@agentauth.dev"
17
+ LEGACY_ADMIN_EMAIL = "admin@agent-auth.local"
18
+ ADMIN_SCOPES = ["admin_agents", "admin_tokens", "read_audit", "admin_users"]
19
+
20
+
21
+ class UserStore(Protocol[UserT, InviteT]):
22
+ def get_admin_hash(self) -> str | None:
23
+ ...
24
+
25
+ def set_admin_hash(self, password_hash: str) -> None:
26
+ ...
27
+
28
+ def create_user(self, email: str, password_hash: str, role: str, invited_by: str | None = None) -> UserT:
29
+ ...
30
+
31
+ def get_user_by_email(self, email: str) -> UserT | None:
32
+ ...
33
+
34
+ def get_user_by_id(self, user_id: str) -> UserT | None:
35
+ ...
36
+
37
+ def list_users(self) -> list[UserT]:
38
+ ...
39
+
40
+ def update_password(self, user_id: str, password_hash: str) -> UserT | None:
41
+ ...
42
+
43
+ def update_user(self, user_id: str, *, role: str | None = None, status: str | None = None) -> UserT | None:
44
+ ...
45
+
46
+ def create_invite(self, email: str, role: str, invited_by: str, invite_token: str, expires_at: datetime) -> InviteT:
47
+ ...
48
+
49
+ def get_invite_by_token(self, invite_token: str) -> InviteT | None:
50
+ ...
51
+
52
+ def list_invites(self) -> list[InviteT]:
53
+ ...
54
+
55
+ def mark_invite_accepted(self, invite_token: str) -> None:
56
+ ...
57
+
58
+
59
+ @dataclass(frozen=True, slots=True)
60
+ class EffectiveAccess:
61
+ user_id: str
62
+ email: str
63
+ role: str
64
+ scopes: list[str]
65
+ can_manage_users: bool
66
+ can_manage_agents: bool
67
+ can_manage_tokens: bool
68
+ can_read_audit: bool
69
+
70
+
71
+ class CoreUserService:
72
+ """Core human-user identity, password, invite, and access helper logic."""
73
+
74
+ def __init__(self, store: UserStore[UserT, InviteT]):
75
+ self.store = store
76
+
77
+ def create_admin(self, password: str) -> UserT:
78
+ password_hash = self.hash_password(password)
79
+
80
+ existing_default = self.store.get_user_by_email(DEFAULT_ADMIN_EMAIL)
81
+ if existing_default:
82
+ return self.store.update_password(existing_default.user_id, password_hash) or existing_default
83
+
84
+ existing_legacy = self.store.get_user_by_email(LEGACY_ADMIN_EMAIL)
85
+ if existing_legacy:
86
+ return self.store.update_password(existing_legacy.user_id, password_hash) or existing_legacy
87
+
88
+ return self.store.create_user(DEFAULT_ADMIN_EMAIL, password_hash, "admin", invited_by="bootstrap")
89
+
90
+ def authenticate(self, email: str, password: str) -> UserT | None:
91
+ user = self.store.get_user_by_email(email)
92
+ if not user and email == DEFAULT_ADMIN_EMAIL:
93
+ user = self.store.get_user_by_email(LEGACY_ADMIN_EMAIL)
94
+ elif not user and email == LEGACY_ADMIN_EMAIL:
95
+ user = self.store.get_user_by_email(DEFAULT_ADMIN_EMAIL)
96
+ if not user or getattr(user, "status", None) != "active":
97
+ return None
98
+ if not bcrypt.checkpw(password.encode(), getattr(user, "password_hash").encode()):
99
+ return None
100
+ return user
101
+
102
+ def invite_user(self, email: str, role: str, invited_by: str, ttl_hours: int = 24) -> InviteT:
103
+ token = secrets.token_urlsafe(32)
104
+ expires_at = datetime.now(timezone.utc) + timedelta(hours=ttl_hours)
105
+ return self.store.create_invite(email, role, invited_by, token, expires_at)
106
+
107
+ def get_invite_status(self, invite_token: str) -> InviteT | None:
108
+ return self.store.get_invite_by_token(invite_token)
109
+
110
+ def claim_invite(self, invite_token: str, password: str) -> UserT | None:
111
+ invite = self.store.get_invite_by_token(invite_token)
112
+ if not invite or getattr(invite, "accepted"):
113
+ return None
114
+ expires_at = getattr(invite, "expires_at")
115
+ if expires_at.tzinfo is None:
116
+ expires_at = expires_at.replace(tzinfo=timezone.utc)
117
+ if expires_at <= datetime.now(timezone.utc):
118
+ return None
119
+ if self.store.get_user_by_email(getattr(invite, "email")):
120
+ return None
121
+ password_hash = self.hash_password(password)
122
+ user = self.store.create_user(getattr(invite, "email"), password_hash, getattr(invite, "role"), invited_by=getattr(invite, "invited_by"))
123
+ self.store.mark_invite_accepted(invite_token)
124
+ return user
125
+
126
+ def list_users(self) -> list[UserT]:
127
+ return self.store.list_users()
128
+
129
+ def list_invites(self) -> list[InviteT]:
130
+ return self.store.list_invites()
131
+
132
+ def get_user_by_id(self, user_id: str) -> UserT | None:
133
+ return self.store.get_user_by_id(user_id)
134
+
135
+ def get_user_by_email(self, email: str) -> UserT | None:
136
+ return self.store.get_user_by_email(email)
137
+
138
+ def get_current_user(self, principal_id: str | None, email: str | None) -> UserT | None:
139
+ if principal_id:
140
+ user = self.store.get_user_by_id(principal_id)
141
+ if user:
142
+ return user
143
+ normalized_email = email.lower() if email else None
144
+ if normalized_email:
145
+ user = self.store.get_user_by_email(normalized_email)
146
+ if user:
147
+ return user
148
+ if normalized_email in {DEFAULT_ADMIN_EMAIL, LEGACY_ADMIN_EMAIL}:
149
+ return self.store.get_user_by_email(DEFAULT_ADMIN_EMAIL) or self.store.get_user_by_email(LEGACY_ADMIN_EMAIL)
150
+ return None
151
+
152
+ def effective_access(self, user: UserT) -> EffectiveAccess:
153
+ scopes = user_scopes_for_role(getattr(user, "role"))
154
+ return EffectiveAccess(
155
+ user_id=getattr(user, "user_id"),
156
+ email=getattr(user, "email"),
157
+ role=getattr(user, "role"),
158
+ scopes=scopes,
159
+ can_manage_users="admin_users" in scopes,
160
+ can_manage_agents="admin_agents" in scopes,
161
+ can_manage_tokens="admin_tokens" in scopes,
162
+ can_read_audit="read_audit" in scopes,
163
+ )
164
+
165
+ def update_user(self, user_id: str, *, role: str | None = None, status: str | None = None) -> UserT | None:
166
+ return self.store.update_user(user_id, role=role, status=status)
167
+
168
+ def change_password(self, user_id: str, password: str) -> UserT | None:
169
+ return self.store.update_password(user_id, self.hash_password(password))
170
+
171
+ @staticmethod
172
+ def hash_password(password: str) -> str:
173
+ return bcrypt.hashpw(password.encode(), bcrypt.gensalt()).decode()
@@ -0,0 +1,131 @@
1
+ Metadata-Version: 2.4
2
+ Name: agentauthlayer
3
+ Version: 0.1.0
4
+ Summary: Library-first authentication and authorization SDK for AI agents
5
+ Author: Vaibhav Ahluwalia
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/VaibhavAhluwalia/Agent-Auth-
8
+ Project-URL: Repository, https://github.com/VaibhavAhluwalia/Agent-Auth-
9
+ Keywords: agents,auth,authorization,iam,security,sdk
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3 :: Only
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Operating System :: OS Independent
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Topic :: Security
16
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
17
+ Requires-Python: >=3.10
18
+ Description-Content-Type: text/markdown
19
+ Requires-Dist: requests>=2.31.0
20
+ Requires-Dist: python-jose>=3.3.0
21
+
22
+ # Agent Auth SDK
23
+
24
+ A library-first authentication and authorization SDK for AI agents.
25
+
26
+ ## What this package is
27
+
28
+ `agent-auth-sdk` is the reusable Python SDK layer of the Agent Auth platform. It is designed for developers who want to:
29
+ - register agents
30
+ - issue and verify access with the Agent Auth control plane
31
+ - sync tool definitions
32
+ - evaluate permissions
33
+ - embed policy enforcement into agent runtimes
34
+
35
+ This package is the **developer-facing library**, not the full FastAPI server or React admin UI.
36
+
37
+ ## Install
38
+
39
+ ```bash
40
+ pip install agent-auth-sdk
41
+ ```
42
+
43
+ For local development:
44
+
45
+ ```bash
46
+ pip install -e .
47
+ ```
48
+
49
+ ## Quickstart
50
+
51
+ ```python
52
+ from agent_auth import AuthAPIClient
53
+
54
+ client = AuthAPIClient(
55
+ base_url="http://127.0.0.1:8002",
56
+ token="YOUR_ADMIN_OR_SERVICE_TOKEN",
57
+ )
58
+
59
+ agent = client.create_agent(
60
+ agent_id="research-bot-01",
61
+ name="Research Bot 01",
62
+ owner="vaibhav@company.com",
63
+ role="research_agent",
64
+ scopes=[],
65
+ project_id="ai-platform",
66
+ )
67
+
68
+ print(agent)
69
+ ```
70
+
71
+ ## Environment variables
72
+
73
+ If you prefer, the SDK reads these automatically:
74
+
75
+ ```bash
76
+ export AGENT_AUTH_URL=http://127.0.0.1:8002
77
+ export AGENT_AUTH_TOKEN=YOUR_ADMIN_OR_SERVICE_TOKEN
78
+ ```
79
+
80
+ Then:
81
+
82
+ ```python
83
+ from agent_auth import AuthAPIClient
84
+
85
+ client = AuthAPIClient()
86
+ print(client.health())
87
+ ```
88
+
89
+ ## Common capabilities
90
+
91
+ ### Sync tools from code
92
+
93
+ ```python
94
+ client.sync_tools([
95
+ {"action": "tool.search_web", "description": "Search the web"},
96
+ {"action": "docs.read", "description": "Read protected docs"},
97
+ ])
98
+ ```
99
+
100
+ ### Evaluate access
101
+
102
+ ```python
103
+ decision = client.evaluate(
104
+ principal_id="research-bot-01",
105
+ action="tool.search_web",
106
+ resource="web/*",
107
+ role="research_agent",
108
+ )
109
+
110
+ print(decision)
111
+ ```
112
+
113
+ ## Package scope
114
+
115
+ This package currently exposes the reusable SDK and policy primitives under `agent_auth/`.
116
+ The full control plane server, admin UI, and demos remain in the same repository but are not part of the SDK package build.
117
+
118
+ ## Dev-ready validation checklist
119
+
120
+ Before publishing, verify:
121
+
122
+ ```bash
123
+ pip install -e .
124
+ python -c "import agent_auth; print(agent_auth.__all__)"
125
+ python -m build
126
+ pip install dist/*.whl
127
+ ```
128
+
129
+ ## Repository
130
+
131
+ - Source: <https://github.com/VaibhavAhluwalia/Agent-Auth->
@@ -0,0 +1,24 @@
1
+ agent_auth/__init__.py,sha256=c1P2iMJyKefBl6iKUsdEyWtt6EmtRlNVtOzUnoXZrqI,1454
2
+ agent_auth/__main__.py,sha256=7Hk2nPvyEFjvnclLhcq8Zw3pkyiZvhgC196qaT_ISVM,71
3
+ agent_auth/agents.py,sha256=uk9Ai6qIvYxmvKB9VCmtAlM3HqVAgyYncmr4h_pD61k,1075
4
+ agent_auth/audit.py,sha256=Be_opy-omGYMbUfi_trMkTAZpenxLK18vTMoQixSJyc,1399
5
+ agent_auth/auth.py,sha256=GwflUUnmyk8faO3TylvCVLx0NdAf5RBof9v0JPuibSI,1191
6
+ agent_auth/cli.py,sha256=iQeYtPG-CNFLEXMS8GiqAIpKHsJJCB7qSc-P6EH4VMI,1082
7
+ agent_auth/client.py,sha256=IlFE-vZ7bP4_LNdtg8mAksDkQlqwBqVGgmehUSCwaPs,3979
8
+ agent_auth/context.py,sha256=yxfBw9OazgywpCN7OUO3RKfJPF1-HR3YGL62jy-xWsI,455
9
+ agent_auth/core.py,sha256=0A2-gSDDZ7djPyKoCjcukl9j3-StHqDNo0DHta0S5Iw,22897
10
+ agent_auth/delegation.py,sha256=Q7r3bDQs78cpxtTob0E5_9cXnZMAi7sSl1yoNSE1UyE,375
11
+ agent_auth/exceptions.py,sha256=VDyR-UD1OZTZvWcwJEaRyC738LrRu191q3q7tTfJ5tw,1465
12
+ agent_auth/models.py,sha256=CrbxHDrPE-sNWKA5ZEAs7BCRQoPF5beod_ZDqav3cvU,1586
13
+ agent_auth/policy.py,sha256=OA9uOp84uvoKlljOCXrEFPhO_HRCYzvNNH3IC_7W3RQ,10370
14
+ agent_auth/policy_service.py,sha256=WZVuFBuaB947bEq5pj5FPym0ozMtFFDLWcGkQRN2OoA,6585
15
+ agent_auth/principals.py,sha256=aSNnm3M0TqJzEjKhZn9Mz7ToYVaMYJxRuI67dApxnY4,1110
16
+ agent_auth/registry.py,sha256=hmX91rKwZZdGhB_inr45sWJBSalBdu9ND9RgzT23FeU,2652
17
+ agent_auth/session.py,sha256=4WrGnfVn9tFz_tqDbMt5LgqLKd_JxBKguQOzRP9LVqo,4804
18
+ agent_auth/storage.py,sha256=96pOXbjTcRI6Nqkh1BNj7GryEhq-Xqgi9iU7R9Ul9gc,18400
19
+ agent_auth/tokens.py,sha256=J5_a0hbbNlk9HK2BsqAN0Uoc7845owjXFBbvqqpd9go,2435
20
+ agent_auth/users.py,sha256=HKgk_pzo8qESKlzCJdSQbWAPqoFo7UxwwV-nCfGN07U,6511
21
+ agentauthlayer-0.1.0.dist-info/METADATA,sha256=mnRnUbxDc5abi7yf2Y7Nxlci-wZGlM4l7LtLa8nBSGE,3055
22
+ agentauthlayer-0.1.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
23
+ agentauthlayer-0.1.0.dist-info/top_level.txt,sha256=ySYpKFR5CtHd9OgGNxTuV4-p8gpkSVJtzFCG0pzhGXw,11
24
+ agentauthlayer-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (82.0.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1 @@
1
+ agent_auth