adk-auth 0.4.0

Access control and authentication for Rust Agent Development Kit (ADK-Rust)
Documentation

adk-auth

Access control and authentication for Rust Agent Development Kit (ADK-Rust).

Crates.io Documentation License

Overview

adk-auth provides enterprise-grade access control for AI agents:

  • Declarative Scope-Based Security - Tools declare required scopes, framework enforces automatically
  • Role-Based Access - Define roles with tool/agent permissions (allow/deny, deny precedence)
  • Audit Logging - Log all access attempts to JSONL files
  • SSO/OAuth - JWT validation with Google, Azure AD, Okta, Auth0, and generic OIDC providers
  • Auth Bridge - Flow authenticated identity from HTTP requests into agent execution via adk-server

Features

Core Role Based Access Control (RBAC), scope-based security, and audit logging are always available with no feature flags required.

Feature Description
sso JWT/OIDC providers (Google, Azure AD, Okta, Auth0, generic OIDC)
auth-bridge JwtRequestContextExtractor for adk-server identity flow (implies sso)

Declarative Scope-Based Security

Tools declare what scopes they need. The framework enforces before execution — no imperative checks in your handlers:

use adk_tool::FunctionTool;
use adk_auth::{ScopeGuard, ContextScopeResolver, StaticScopeResolver};

// Tool declares its required scopes
let transfer = FunctionTool::new("transfer", "Transfer funds", handler)
    .with_scopes(&["finance:write", "verified"]);

// ScopeGuard enforces automatically
let guard = ScopeGuard::new(ContextScopeResolver);
let protected = guard.protect(transfer);

// Or wrap all tools at once
let protected_tools = guard.protect_all(tools);

With audit logging:

let guard = ScopeGuard::with_audit(ContextScopeResolver, audit_sink);
let protected = guard.protect(transfer);
// All scope checks (allowed + denied) are logged

Pluggable resolvers:

Resolver Source
ContextScopeResolver Delegates to ToolContext::user_scopes() (JWT claims, session state)
StaticScopeResolver Fixed scopes — useful for testing
Custom impl ScopeResolver Any async source (database, external IdP, etc.)

For resolvers that call external services, cache the resolved scopes at the request or session layer to avoid repeated lookups during multi-tool runs.

Role-Based Access Control

use adk_auth::{Permission, Role, AccessControl, AuthMiddleware};

// Define roles
let admin = Role::new("admin").allow(Permission::AllTools);
let user = Role::new("user")
    .allow(Permission::Tool("search".into()))
    .deny(Permission::Tool("code_exec".into()));

// Build access control
let ac = AccessControl::builder()
    .role(admin)
    .role(user)
    .assign("alice@example.com", "admin")
    .assign("bob@example.com", "user")
    .build()?;

// Protect tools
let middleware = AuthMiddleware::new(ac);
let protected_tools = middleware.protect_all(tools);

Combining RBAC + Scopes

Use RBAC for coarse tool/agent entitlement and scopes for request-level constraints:

use std::sync::Arc;
use adk_auth::{AuthMiddleware, ContextScopeResolver, ScopeGuard};

let rbac = AuthMiddleware::new(ac);
let scoped = ScopeGuard::new(ContextScopeResolver);

let protected = scoped.protect(rbac.protect(transfer_tool));

SSO Integration

Enable with features = ["sso"]:

use adk_auth::sso::{GoogleProvider, ClaimsMapper, SsoAccessControl};

// Create provider
let provider = GoogleProvider::new("your-client-id");

// Map IdP groups to roles
let mapper = ClaimsMapper::builder()
    .map_group("AdminGroup", "admin")
    .default_role("viewer")
    .user_id_from_email()
    .build();

// Combined SSO + RBAC
let sso = SsoAccessControl::builder()
    .validator(provider)
    .mapper(mapper)
    .access_control(ac)
    .build()?;

// Validate token and check permission
let claims = sso.check_token(token, &Permission::Tool("search".into())).await?;
println!("User: {}", claims.user_id());

user_id_from_email() only uses the email claim when email_verified == true; otherwise it falls back to sub.

Providers

Provider Usage
Google GoogleProvider::new(client_id)
Azure AD AzureADProvider::new(tenant_id, client_id) or ::multi_tenant(client_id).with_allowed_tenants(["tenant-id"])
Okta OktaProvider::new(domain, client_id) or ::with_auth_server(domain, server_id, client_id)
Auth0 Auth0Provider::new(domain, audience)
Generic OIDC OidcProvider::from_discovery(issuer, client_id).await or ::new(issuer, client_id, jwks_uri)

AzureADProvider::multi_tenant() accepts tokens from any tenant targeting the configured audience unless you restrict it with with_allowed_tenants(...).

Auth Bridge

Enable with features = ["auth-bridge"] to validate Bearer tokens directly into adk-server request contexts:

use adk_auth::auth_bridge::JwtRequestContextExtractor;
use adk_auth::sso::{ClaimsMapper, GoogleProvider};

let extractor = JwtRequestContextExtractor::builder()
    .validator(GoogleProvider::new("your-client-id"))
    .mapper(ClaimsMapper::builder().user_id_from_email().build())
    .build()?;

The extractor maps:

  • user_id from the configured ClaimsMapper
  • scopes from JWT scope and scp claims
  • metadata including issuer, subject, email, and tenant ID when present

Audit Logging

use adk_auth::FileAuditSink;

let audit = FileAuditSink::new("/var/log/adk/audit.jsonl")?;
let middleware = AuthMiddleware::with_audit(ac, audit);

Output:

{"timestamp":"2025-01-01T10:30:00Z","user":"bob","event_type":"tool_access","resource":"search","outcome":"allowed"}

Examples

cargo run -p adk-examples --example auth_basic                  # RBAC basics
cargo run -p adk-examples --example auth_audit                  # Audit logging
cargo run -p adk-examples --example auth_bridge                 # Auth bridge with server
cargo run -p adk-examples --example auth_sso --features sso     # SSO integration
cargo run -p adk-examples --example auth_jwt --features sso     # JWT validation
cargo run -p adk-examples --example auth_oidc --features sso    # OIDC discovery
cargo run -p adk-examples --example auth_google --features sso  # Google Identity

License

Apache-2.0

Part of ADK-Rust

This crate is part of the ADK-Rust framework for building AI agents in Rust.

Security Notes

  • Prefer short-lived access tokens and rotate signing keys regularly.
  • The JWKS cache refreshes hourly by default; lower the refresh interval if your IdP rotates keys aggressively.
  • OidcProvider::from_discovery() now rejects discovery documents whose issuer does not match the requested issuer URL.
  • Token revocation and blacklist checks are not built in. If you need immediate revocation, enforce it in a custom TokenValidator or request extractor.