O
OAuth2 minter
Auth & signers availableMint, cache and refresh OAuth2 tokens; serve them to VUs.
$ loadr plugin install oauth2-minter
# One shared, auto-refreshed OAuth2 bearer token for the whole fleet, driven by
# the `loadr-plugin-oauth2-minter` native SERVICE plugin.
#
# Like the redis-loader feeder, a service plugin has a start/stop lifecycle:
# loadr calls `start(config)` once before the run and `stop()` once after. On
# start this plugin runs an OAuth2 client-credentials (or refresh-token) grant
# ONCE — over pure hyper + hyper-rustls, no OAuth SDK — caches the bearer token,
# then binds a tiny local line endpoint and returns its address, e.g.:
#
# → oauth2-minter token endpoint at 127.0.0.1:47021
#
# Every VU that opens that endpoint and reads a line gets the CURRENT shared
# token. A background task re-mints it at `expires_in − refresh_skew`, so the
# fleet always attaches a live token without any VU making its own auth
# round-trip: one grant per token lifetime instead of one per VU.
#
# Build + install the plugin, then run:
# cargo build -p loadr-plugin-oauth2-minter --release
# mkdir -p dist && cp plugins/loadr-plugin-oauth2-minter/plugin.toml dist/ \
# && cp target/release/libloadr_plugin_oauth2_minter.so dist/
# loadr plugin install dist
# OAUTH_CLIENT_ID=... OAUTH_CLIENT_SECRET=... loadr run examples/plugins/oauth2-minter.yaml
#
# Or point the plan's `plugins:` entry at the built artifact directly (below).
name: oauth2-minter
description: One shared, auto-refreshed OAuth2 bearer token handed to every VU
plugins:
# Resolve `oauth2-minter` by name from the plugins dir (after
# `loadr plugin install`). To run straight from a build tree instead, set:
# path: target/release/libloadr_plugin_oauth2_minter.so
- name: oauth2-minter
config:
# OAuth2 token endpoint the grant is POSTed to. Must be http(s)://….
token_url: https://id.example.com/oauth2/token
# Client credentials — keep secrets in the environment, never inline.
client_id: ${env.OAUTH_CLIENT_ID}
client_secret: ${env.OAUTH_CLIENT_SECRET}
# "client_credentials" (default) or "refresh_token".
grant_type: client_credentials
# Space-separated scopes (optional).
scope: api.read api.write
# How credentials are presented: "body" (default) or "basic".
auth_style: body
# Refresh this long before expiry (seconds, or "30s"/"5m").
refresh_skew: 30s
# Fallback lifetime when the endpoint omits expires_in.
default_ttl: 1h
# Local address the token endpoint binds to. Port 0 = ephemeral; the bound
# address is printed when the run starts.
bind: 127.0.0.1:0
scenarios:
# Steady authed traffic: every VU across every worker rides the ONE shared
# token minted on start. Point requests' Authorization header at the token
# drawn from the endpoint address printed at run start.
authed_traffic:
executor: constant-vus
vus: 50
duration: 30s
flow:
# Each VU reads the current shared bearer token from the minter endpoint.
# One request line -> the live token + a newline.
- request:
name: fetch shared token
url: tcp://127.0.0.1:0 # replace with the printed token endpoint address
socket:
send_text: "TOKEN\n" # one request line -> current token + newline
read_bytes: 4096
read_timeout: 2s
checks:
- { type: duration, name: minter is fast, max: 25ms }
thresholds:
checks: [ "rate>0.99" ]
A real run: install from the signed index, then watch the plugin work.
A runtime plugin, never in the binary
Installing pulls a per-platform driver from the signed index, verifies its SHA-256 and checks its ABI before it ever loads. Remove it any time with loadr plugin remove oauth2-minter.