Auth & Secrets
Auth strategies, secret resolvers, and credential safety.
#Auth and secrets
Almost every real connector needs a credential, and the two hard parts are always the same: attaching it to the request correctly, and getting it out of source control and into your runtime safely. maivn-tools keeps those concerns apart so you can mix and match them. Auth strategies decide how a credential rides on a request (a header, a query param, a refreshed OAuth2 token); secret resolvers decide where the credential comes from (an environment variable, an injected map, a chain of fallbacks). You compose the two, and secrets stay out of logs, metadata, and audit trails by default.
#Auth strategies
Every strategy implements AuthStrategy and provides two methods:
apply(request)mutates a request dictionary in place to add headers or query parameters.describe()returns a JSON-serializable dictionary that contains no secret material — safe to log or include in connector catalogs.
#Built-in strategies
#Custom strategies
Subclass AuthStrategy for providers with custom schemes — see the API policy for guarantees about the interface stability.
#OAuth 2.0 flow layer
The OAuth flow layer turns a token endpoint plus a client_id/secret into managed access tokens. It supports four grants out of the box:
- Authorization code (with optional PKCE)
- Refresh token
- Client credentials
- Device code
from maivn_tools import (
OAuth2EndpointConfig,
OAuth2Flow,
generate_pkce_challenge,
)
flow = OAuth2Flow(
client_id="...",
client_secret="...",
endpoints=OAuth2EndpointConfig(
authorize_url="https://idp.example.test/oauth/authorize",
token_url="https://idp.example.test/oauth/token",
),
)
# Authorization-code with PKCE
pkce = generate_pkce_challenge()
url = flow.authorization_url(
redirect_uri="https://app.example.test/callback",
scope="openid email",
state="random-state",
pkce=pkce,
)
# ... user grants, returns to callback with ?code=... ...
token = flow.exchange_code(code, redirect_uri="https://app.example.test/callback", pkce_verifier=pkce.verifier)#Token caches
For long-running agents, wrap a refresh strategy in a TokenCache:
cache = flow.refresh_token_cache(refresh_token, scope="api")
# Or for client-credentials services:
cache = flow.client_credentials_cache(scope="api")
connector = MyConnector(token=cache)TokenCache is callable and returns a current OAuth2Token on demand, refreshing only when the cached token is within the configured leeway of its expires_at. It is thread-safe.
#Device code
For headless installations or CLIs:
grant = flow.request_device_code(scope="api")
print(f"Visit {grant.verification_uri} and enter {grant.user_code}")
# Poll using grant.interval until the user completes the flow:
token = flow.poll_device_code(grant.device_code)#Secret resolvers
A SecretRef is a name for a secret; a SecretResolver turns it into the value. Provider connectors should accept either a resolved string or a SecretRef and call the resolver only when they actually need the secret.
#Built-in resolvers
from maivn_tools import ChainedSecretResolver, EnvironmentSecretResolver, SecretRef
resolver = ChainedSecretResolver([
EnvironmentSecretResolver(prefix="MAIVN_"),
EnvironmentSecretResolver(),
])
token = resolver.resolve(SecretRef("GITHUB_TOKEN"))MissingSecretError is raised when no resolver can provide the secret. Use try_resolve to get None instead.
#Credential safety
- Never put secrets into
ProviderMetadata,ConnectionMetadata, audit events, error messages, ordescribe()output. - Treat
SecretRefinstances as the safe wire-format. Resolve them as late as possible. - Strategies should not log raw tokens; the SDK request runtime never logs request headers by default.
- See the security policy for the full credential-safety policy.