Tools & Connectors
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
| Strategy | Use it for |
|---|---|
NoAuth |
Public endpoints; default for HttpClient when no auth is supplied. |
ApiKeyAuth(api_key, header=..., query_param=..., prefix=None) |
Static API key sent via header or query string. Provide exactly one of header or query_param. |
BearerTokenAuth(token, scheme="Bearer") |
Long-lived bearer token. Supports custom header names and schemes (e.g. Token). |
BasicAuth(username, password) |
HTTP Basic credentials. Encodes the pair into the Authorization header. |
OAuth2BearerAuth(provider) |
Bearer auth fed by a caller-supplied OAuth2TokenProvider. The provider handles refresh. |
#Custom strategies
Subclass AuthStrategy for providers with bespoke 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 PKCEpkce = 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 aSecretRef and call the resolver only when they actually need the secret.
#Built-in resolvers
| Resolver | Source |
|---|---|
EnvironmentSecretResolver(env=None, prefix="") |
Process environment variables. |
StaticSecretResolver(secrets) |
An in-memory mapping. Intended for tests. |
ChainedSecretResolver([...]) |
Walks a list of resolvers and returns the first match. |
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.