Access Control
Enterprise-grade access control for AI agents using adk-auth.
Overview
adk-auth provides role-based access control (RBAC) with audit logging and SSO support for ADK agents. It enables secure, fine-grained control over which users can access which tools.
Architecture
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Agent Request β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β SSO Token Validation β
β βββββββββββββββ βββββββββββββββ βββββββββββββββββββββββββββ β
β β Google β β Azure AD β β OIDC Discovery β β
β β Provider β β Provider β β (Okta, Auth0, etc) β β
β βββββββββββββββ βββββββββββββββ βββββββββββββββββββββββββββ β
β β β
β ββββββββ΄βββββββ β
β β JWKS Cache β β Auto-refresh keys β
β βββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βΌ TokenClaims
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Claims Mapper β
β β
β IdP Groups β adk-auth Roles β
β βββββββββββββββββββββββββββββββββββββββββ β
β "AdminGroup" β "admin" β
β "DataAnalysts" β "analyst" β
β (default) β "viewer" β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βΌ Roles
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Access Control β
β β
β Role: admin β
β βββ allow: AllTools β
β βββ allow: AllAgents β
β β
β Role: analyst β
β βββ allow: Tool("search") β
β βββ allow: Tool("summarize") β
β βββ deny: Tool("code_exec") β Deny takes precedence β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βΌ Check Result
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Audit Logging β
β β
β {"user":"alice","resource":"search","outcome":"allowed"} β
β {"user":"bob","resource":"exec","outcome":"denied"} β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Tool Execution β
β (only if access granted) β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Design Principles
1. Deny Precedence
When a role has both allow and deny rules, deny always wins:
let role = Role::new("limited")
.allow(Permission::AllTools) // Allow everything...
.deny(Permission::Tool("admin")); // ...except admin
// Result: Can access any tool EXCEPT "admin"
2. Multi-Role Union
Users with multiple roles get the union of permissions, but deny rules from any role still apply:
let ac = AccessControl::builder()
.role(reader) // allow: search
.role(writer) // allow: write
.assign("alice", "reader")
.assign("alice", "writer")
.build()?;
// Alice can access both "search" AND "write"
3. Explicit Over Implicit
Permissions are explicit - no access is granted by default:
let role = Role::new("empty");
// This role grants NO permissions
ac.check("user", &Permission::Tool("anything")); // β Denied
4. Separation of Authentication and Authorization
- Authentication (SSO): Verifies WHO the user is (via JWT)
- Authorization (RBAC): Determines WHAT they can access
// Authentication: validate JWT, extract claims
let claims = provider.validate(token).await?;
// Authorization: check specific permission
ac.check(&claims.sub, &Permission::Tool("search"))?;
// Combined: SsoAccessControl does both
sso.check_token(token, &permission).await?;
Installation
[dependencies]
adk-auth = "0.2.0"
# For SSO/OAuth support
adk-auth = { version = "0.2.0", features = ["sso"] }
Core Components
Permission
pub enum Permission {
Tool(String), // Specific tool by name
AllTools, // Wildcard: all tools
Agent(String), // Specific agent by name
AllAgents, // Wildcard: all agents
}
Role
let analyst = Role::new("analyst")
.allow(Permission::Tool("search".into()))
.allow(Permission::Tool("summarize".into()))
.deny(Permission::Tool("code_exec".into()));
AccessControl
let ac = AccessControl::builder()
.role(admin)
.role(analyst)
.assign("alice@company.com", "admin")
.assign("bob@company.com", "analyst")
.build()?;
// Check permission
ac.check("bob@company.com", &Permission::Tool("search".into()))?;
ProtectedTool
Wraps a tool with automatic permission checking:
use adk_auth::ToolExt;
let protected = my_tool.with_access_control(Arc::new(ac));
// When executed, checks permission before running
protected.execute(ctx, args).await?;
AuthMiddleware
Batch-protect multiple tools:
let middleware = AuthMiddleware::new(ac);
let protected_tools = middleware.protect_all(tools);
SSO Integration
Supported Providers
| Provider | Constructor | Issuer |
|---|---|---|
GoogleProvider::new(client_id) | accounts.google.com | |
| Azure AD | AzureADProvider::new(tenant, client) | login.microsoftonline.com |
| Okta | OktaProvider::new(domain, client) | {domain}/oauth2/default |
| Auth0 | Auth0Provider::new(domain, audience) | {domain}/ |
| Generic | OidcProvider::from_discovery(issuer, client) | Any OIDC provider |
TokenClaims
Claims extracted from validated JWTs:
pub struct TokenClaims {
pub sub: String, // Subject (user ID)
pub email: Option<String>, // Email
pub name: Option<String>, // Display name
pub groups: Vec<String>, // IdP groups
pub roles: Vec<String>, // IdP roles
pub hd: Option<String>, // Google hosted domain
pub tid: Option<String>, // Azure tenant ID
// ... more standard OIDC claims
}
ClaimsMapper
Maps IdP groups to adk-auth roles:
let mapper = ClaimsMapper::builder()
.map_group("AdminGroup", "admin")
.map_group("Users", "viewer")
.default_role("guest")
.user_id_from_email()
.build();
SsoAccessControl
Combines SSO validation with RBAC in one call:
let sso = SsoAccessControl::builder()
.validator(GoogleProvider::new("client-id"))
.mapper(mapper)
.access_control(ac)
.audit_sink(audit)
.build()?;
// Validate token + check permission + audit log
let claims = sso.check_token(token, &Permission::Tool("search".into())).await?;
Audit Logging
FileAuditSink
let audit = FileAuditSink::new("/var/log/adk/audit.jsonl")?;
let middleware = AuthMiddleware::with_audit(ac, audit);
Output Format (JSONL)
{"timestamp":"2025-01-01T10:30:00Z","user":"bob","session_id":"sess-123","event_type":"tool_access","resource":"search","outcome":"allowed"}
{"timestamp":"2025-01-01T10:30:01Z","user":"bob","session_id":"sess-123","event_type":"tool_access","resource":"code_exec","outcome":"denied"}
Custom Audit Sink
use adk_auth::{AuditSink, AuditEvent, AuthError};
use async_trait::async_trait;
pub struct DatabaseAuditSink { /* ... */ }
#[async_trait]
impl AuditSink for DatabaseAuditSink {
async fn log(&self, event: AuditEvent) -> Result<(), AuthError> {
// Insert into database
sqlx::query("INSERT INTO audit_log ...")
.bind(event.user)
.bind(event.resource)
.execute(&self.pool)
.await?;
Ok(())
}
}
Examples
# Core RBAC
cargo run --example auth_basic # Role-based access control
cargo run --example auth_audit # Audit logging
# SSO (requires --features sso)
cargo run --example auth_sso --features sso # Complete SSO flow
cargo run --example auth_jwt --features sso # JWT validation
cargo run --example auth_oidc --features sso # OIDC discovery
cargo run --example auth_google --features sso # Google Identity
Security Best Practices
| Practice | Description |
|---|---|
| Deny by default | Only grant permissions explicitly needed |
| Explicit denies | Add deny rules for dangerous operations |
| Audit everything | Enable logging for compliance |
| Validate server-side | Always validate JWTs on the server |
| Use HTTPS | JWKS endpoints require secure connections |
| Rotate keys | JWKS cache auto-refreshes every hour |
| Limit token lifetime | Use short-lived access tokens |
Error Handling
use adk_auth::{AccessDenied, AuthError};
use adk_auth::sso::TokenError;
// RBAC errors
match ac.check("user", &Permission::Tool("admin".into())) {
Ok(()) => { /* access granted */ }
Err(AccessDenied { user, permission }) => {
eprintln!("Denied: {} cannot access {}", user, permission);
}
}
// SSO errors
match provider.validate(token).await {
Ok(claims) => { /* token valid */ }
Err(TokenError::Expired) => { /* token expired */ }
Err(TokenError::InvalidSignature) => { /* signature invalid */ }
Err(TokenError::InvalidIssuer { expected, actual }) => { /* wrong issuer */ }
Err(e) => { /* other error */ }
}
Previous: β Evaluation | Next: Development Guidelines β