oauth-guard
v1.0.0
Published
A zero-config local proxy that automatically fetches and refreshes OAuth2 Client Credentials tokens for API testing tools like Postman and curl.
Maintainers
Readme
🛡️ oauth-guard
A zero-config, zero-dependency local proxy server that automatically manages, caches, and refreshes OAuth2 Client Credentials tokens. Built specifically to solve token expiration friction in API testing tools like Postman, Insomnia, and curl.
🛑 The Friction
When testing APIs secured by the OAuth2 Client Credentials grant, developers face a persistent workflow interruption.
According to the OAuth2 specification (RFC 6749, sections 4.2.2 and 4.4.3), auth servers should not issue a refresh_token for Client Credentials. Because of this, popular desktop API clients (like Postman) cannot automatically refresh tokens when they expire.
This leaves developers with three painful choices during testing:
- Manually trigger token requests and copy-paste new
Authorizationheaders every hour. - Write custom pre-request scripts in Postman that execute on every single request, slowing down response times and cluttering test collections.
- Deal with unexpected
401 Unauthorizederrors mid-flow.
⚡ What It Is (and What It Isn't)
oauth-guard runs a lightweight proxy on your local machine. You direct your API client (Postman, curl, or a local dev frontend) to http://localhost:8787 instead of the real API base URL.
The proxy intercepts incoming calls, checks process memory for a valid token, auto-refreshes it proactively if it has expired (or is about to expire), attaches it to the Authorization header, and forwards your request to the target API.
- What it is: A simple, single-binary-equivalent Node.js developer tool configured with one JSON file. It is optimized for local loopback testing.
- What it isn't: It is not an enterprise API gateway, production sidecar, or Kubernetes service mesh component. If you need advanced cluster ingress token-management, mutual TLS (mTLS), or cluster-level caching, check out infrastructure-focused utilities like Go's
observatorium/token-refresheror Go libraries likeudhos/oauth2.
🔄 How It Works
sequenceDiagram
autonumber
actor Client as API Client (Postman / curl)
participant Proxy as localhost:8787 (oauth-guard)
participant Auth as OAuth Provider (Auth0/Okta)
participant API as Target API (e.g. Gateway)
Client->>Proxy: GET /v1/users
alt Token is missing or expires in < 30s
Proxy->>Auth: POST /oauth/token (grant_type=client_credentials)
Auth-->>Proxy: Return JSON (access_token + expires_in)
end
Proxy->>API: GET /v1/users (Authorization: Bearer <token>)
API-->>Proxy: Return Data (JSON/Binary/Stream)
Proxy-->>Client: Pipe back response unchanged (200 OK)🛠️ Concurrency Safety (Singleflight Request Coalescing)
If your test suite launches multiple requests in parallel (or you open a dashboard making 10 concurrent API calls) while the cached token is expired or missing, standard proxies trigger 10 simultaneous token requests. This can lead to rate-limiting blocks or invalidating previously generated tokens.
oauth-guard implements single-flight request coalescing. When multiple concurrent requests arrive, only one network request is made to the token endpoint. All other concurrent requests wait in a queue and resolve safely once that single request completes.
🚀 Installation & Usage
Option 1: Run with npx (No Global Installation)
npx oauth-guard start --config config.jsonOption 2: Install Globally
npm install -g oauth-guard📂 Configuration Examples
Create a config.json file in your workspace (copy from config.example.json to start).
Example 1: Auth0 (Requires audience and Form Body Credentials)
{
"tokenEndpoint": "https://dev-your-tenant.us.auth0.com/oauth/token",
"clientId": "your_auth0_client_id",
"clientSecret": "your_auth0_client_secret",
"credentialStyle": "body",
"audience": "https://api.yourdomain.local",
"targetApiBaseUrl": "https://api.yourdomain.com",
"listenPort": 8787
}Example 2: Okta / Standard OAuth (Basic Auth Credentials + Custom Timeout)
{
"tokenEndpoint": "https://your-org.okta.com/oauth2/default/v1/token",
"clientId": "your_client_id",
"clientSecret": "your_client_secret",
"credentialStyle": "basic",
"targetApiBaseUrl": "https://api.yourdomain.com",
"listenPort": 8787,
"scope": "read:users write:users",
"targetTimeoutMs": 15000
}⚙️ Configuration Reference
| Parameter | Type | Required | Default | Description |
| :--- | :--- | :---: | :---: | :--- |
| tokenEndpoint | string | Yes | — | The full URL to request OAuth tokens (e.g., https://auth.company.com/oauth/token). |
| clientId | string | Yes | — | The client ID for application authentication. |
| clientSecret | string | Yes | — | The client secret for application authentication. |
| credentialStyle| string | Yes | — | Location of client credentials. Must be "body" (sent in POST request body) or "basic" (sent in HTTP Basic Auth headers). |
| targetApiBaseUrl| string | Yes | — | The base target API URL where requests will be forwarded. |
| listenPort | number | Yes | — | Local port the proxy binds to (e.g., 8787). Must be an integer between 1 and 65535. |
| audience | string | No | "" | The resource audience identifier (required by Auth0). |
| scope | string | No | "" | Optional space-separated list of scopes to request. |
| targetTimeoutMs| number | No | 30000 | Connection timeout in milliseconds for forwarded requests to the target API. |
💻 CLI Commands
1. oauth-guard start
Starts the HTTP proxy server on the configured local port.
- Command:
oauth-guard start --config <path> - Flags:
-c, --config <path>: Path to the JSON configuration file.-v, --verbose: Enables verbose logging. Prints full outbound headers (with redacted access tokens), request paths, and response latency.
2. oauth-guard status
A one-shot diagnostics tool to verify credentials and connectivity without starting the server.
- Command:
oauth-guard status --config <path> - Output: Displays endpoint reachability latency, credential authentication status, token lifetime remaining, and a redacted preview of the returned token.
[oauth-guard] [status] ℹ️ Checking token endpoint: https://dev-tenant.us.auth0.com/oauth/token
[oauth-guard] 🔄 Fetching new token from https://dev-tenant.us.auth0.com/oauth/token...
[oauth-guard] ✅ Token acquired, expires in 86400s
[oauth-guard] [status] ✅ Diagnostic Summary:
• Token endpoint reachability : REACHABLE (took 452ms)
• Authentication status : SUCCESSFUL
• Token lifetime / expiry : 86400s remaining (23h 59m 59s)
• Token confirmation : eyJhbG*** [redacted]🔒 Security Notes
[!IMPORTANT] Because
oauth-guardhandles active credentials and authentication tokens, please observe the following guidelines:
- Local-Only Binding: The proxy server binds and listens on
localhost(127.0.0.1) only. Never expose the configured port to public networks or external interfaces.- Git Safety: Add
config.jsonto your local.gitignoreimmediately. Never commit real credentials to source control. Useconfig.example.jsonto share schemas with team members.- Memory Caching: Credentials and access tokens are kept purely in-memory. They are never written to disk or serialized to local temporary folders.
⚠️ Limitations & Scope
- Single Target API: Each instance of the proxy is dedicated to one
targetApiBaseUrl. If you need to test multiple separate target APIs, start multiple proxy instances on separate local ports. - Flow Support: Supports Client Credentials grant type only. Authorization Code, Implicit, and Password grants are out of scope.
- No Persistence: Restarting the proxy clears the cached token, meaning a fresh token will be acquired on the first request after launch.
📄 License
MIT License. See LICENSE for details.
