Access Control on Tools
Tools must check that the user (not just the agent) is authorized — agents are not principals.
Access Control on Tools is a free AI Agents lesson on CoddyKit — lesson 4 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the AI Agents learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.
Tools Are Not Principals
The model "calls a tool" but the actual ACTION is performed by your code. The user — not the agent — is the principal whose permissions matter.
Authorise based on the USER's identity, never just because "the agent decided to".
Authorise at the Tool Boundary
def refund_order(order_id, *, user):
order = db.get_order(order_id)
if order.customer_id != user.id and not user.is_staff:
raise PermissionError(f'User {user.id} cannot refund order {order_id}')
return stripe.refund(order.payment_id)Pass User Context
Plumb the user object into every tool call:
def dispatch(tool_call, user):
args = json.loads(tool_call.function.arguments)
fn = TOOLS[tool_call.function.name]
return fn(**args, user=user)Per-Tool Permissions
Define which tools each role can use:
ROLE_TOOLS = {
'customer': ['search_orders', 'open_ticket'],
'support': ['search_orders', 'refund_order', 'cancel_order'],
'admin': ['*']
}
visible_tools = [t for t in ALL_TOOLS if t.name in ROLE_TOOLS[user.role] or 'admin' in ROLE_TOOLS[user.role]]Scoping Data Access
Tools that query the DB must scope by user automatically:
def search_orders(query, user):
# NEVER let the model bypass this WHERE clause
return db.query('SELECT * FROM orders WHERE customer_id = %s AND ...', (user.id,))Audit Every Tool Call
Log: who, what, when, with what args, result. This is your forensic trail when something goes wrong:
audit_log({
'user_id': user.id,
'tool': tool_name,
'args': args,
'result_status': result.status,
'timestamp': now()
})Confirm Destructive Actions
Even for authorised users, double-check destructive ops:
def delete_account(user_id, confirmed=False, user=None):
if not confirmed:
return {'requires_confirmation': True, 'msg': 'Confirm deletion of account.'}
db.execute('DELETE ...', (user_id,))OAuth / Token-Scoped Tools
For agents acting on a user's behalf in external services, scope OAuth tokens narrowly:
# Token only has gmail.send scope, not gmail.read
send_email = build_with_token(user_token_scoped_send)Service-Account Boundaries
The agent's own service account should have minimum permissions:
- Read-only DB user for analytics tools
- Per-customer S3 buckets
- VPC-isolated networks
Rate Limits Per Tool
def ratelimit(key, per_user_per_day=10):
counts = {}
def decorator(func):
def wrapper(user_id, *args, **kwargs):
counts.setdefault(user_id, 0)
if counts[user_id] >= per_user_per_day:
print(f'{key}: user {user_id} rate limited')
return None
counts[user_id] += 1
return func(user_id, *args, **kwargs)
return wrapper
return decorator
@ratelimit(key='tool:refund', per_user_per_day=2)
def refund_order(user_id, order_id):
print(f'refunding order {order_id} for user {user_id}')
return True
for i in range(3):
refund_order('user1', f'order{i}')
User-Visible Permissions UI
Show the user what the agent can do on their behalf. Let them revoke per-tool permissions. Critical for trust.
Re-Authentication for High-Stakes
Make sensitive tools require fresh authentication (last sign-in < 5 minutes):
if tool.is_sensitive and user.last_auth_age_seconds > 300:
raise StaleAuthError('Please re-authenticate to perform this action.')Test Authz Like Code
Write tests that verify "user A cannot refund user B's order" — never assume access controls work. They're the #1 source of agent security bugs.
Authz Principle
Whose permissions are checked when the agent calls a tool?
Recap
The user is the principal. Plumb user identity to every tool. Scope data by user. Audit every call. Confirm destructive operations. Test authz like you test code.
Frequently asked questions
Is the “Access Control on Tools” lesson free?
Yes — the full text of “Access Control on Tools” is free to read here on the web, and the AI Agents course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the AI Agents course, upgrade to CoddyKit PRO.
What will I learn in “Access Control on Tools”?
Tools must check that the user (not just the agent) is authorized — agents are not principals. You practise AI Agents with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.
Do I need any experience to start AI Agents?
No prior experience is required. AI Agents on CoddyKit is structured for beginners through advanced learners; this is — lesson 4 of 4, so you can start here or from the beginning and move at your own pace.
How long does the “Access Control on Tools” lesson take?
Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.
Can I write and run code in this AI Agents lesson?
Yes. Every AI Agents lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.