O

OAuth2 minter

Auth & signers available

Mint, cache and refresh OAuth2 tokens; serve them to VUs.

Install
$ loadr plugin install oauth2-minter
examples/plugins/oauth2-minter.yaml
# 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.