@wristband/typescript-m2m-auth
v0.1.1
Published
A runtime-agnostic SDK for Wristband Machine-to-Machine OAuth2 authentication. Works in any environment with a native fetch API.
Downloads
202
Readme
Wristband Machine-to-Machine (M2M) Authentication SDK for TypeScript
This TypeScript SDK enables Wristband machine-to-machine (M2M) OAuth2 clients to securely retrieve, cache, and refresh access tokens. Designed for server-to-server communication, it automates M2M token management with zero user interaction and zero runtime dependencies.
Compatible with Node.js, Deno, Bun, Cloudflare Workers, and other TypeScript server runtimes.
You can learn more about how authentication works in Wristband in our documentation:
Table of Contents
- Requirements
- Usage
- Token Caching and Refresh
- SDK Configuration Options
- API
- Runtime-specific Setup
- Related Wristband SDKs
- Wristband Express M2M Demo App
- Questions
Requirements
- Node.js >= 20
Usage
1) Installation
# With npm
npm install @wristband/typescript-m2m-auth
# With yarn
yarn add @wristband/typescript-m2m-auth
# With pnpm
pnpm add @wristband/typescript-m2m-auth2) Wristband Configuration
- First, make sure you have a Wristband account set up. You can sign up here.
- From the Dashboard home page, add a new Application, and then navigate to the "Application Settings" side menu nav for that application. Copy down the Application Vanity Domain from the top box.
- Next, navigate to the "OAuth2 Clients" side nav menu and create a Machine-to-Machine (M2M) OAuth2 Client. Copy down the Client ID and Client Secret.
3) Configure the SDK
First, store your credentials securely. For local development, a .env file works well:
[!NOTE] Never commit secrets to source control. Use environment variables or a secrets manager in production.
CLIENT_ID=<your-client-id>
CLIENT_SECRET=<your-client-secret>
APPLICATION_VANITY_DOMAIN=<your-domain.wristband.dev>Then, instantiate the client once at module level with the createWristbandM2MClient() factory function so it is shared across requests:
import { createWristbandM2MClient } from '@wristband/typescript-m2m-auth';
export const wristbandM2MClient = createWristbandM2MClient({
clientId: process.env.CLIENT_ID!,
clientSecret: process.env.CLIENT_SECRET!,
wristbandApplicationVanityDomain: process.env.APPLICATION_VANITY_DOMAIN!,
});Multiple Instances
If you need to communicate with multiple Wristband Applications or use different OAuth2 clients, simply instantiate the client multiple times:
const wristbandM2MClient01 = createWristbandM2MClient({ ... });
const wristbandM2MClient02 = createWristbandM2MClient({ ... });4) Prime the Token Cache on Server Startup
Call getToken() during server startup to pre-load the token cache. This avoids a cold-start delay on the first request.
// Express
import express from 'express';
import { wristbandM2MClient } from './auth';
const app = express();
async function start() {
try {
await wristbandM2MClient.getToken();
} catch (err) {
console.error('[M2M AUTH] Failed to retrieve initial M2M token:', err);
}
app.listen(3000);
}
start();5) Use the Token
Call getToken() before any authenticated downstream request and pass the result as a Bearer token. If a downstream API returns a 401, call clearToken() to force a fresh token on the next call.
async function callProtectedApi() {
const token = await wristbandM2MClient.getToken();
const response = await fetch('https://your-api.example.com/protected', {
headers: { Authorization: `Bearer ${token}` },
});
if (response.status === 401) {
wristbandM2MClient.clearToken();
}
return response.json();
}Token Caching and Refresh
The SDK automatically caches tokens in memory. On each getToken() call, the cached token is returned if still valid. When it expires, a new token is fetched automatically.
Expiration Buffer
A buffer is subtracted from the token's expiration time to ensure the token is refreshed slightly before it actually expires. The default is 60 seconds.
createWristbandM2MClient({
// ...
tokenExpirationBuffer: 120, // Refresh 2 minutes before actual expiration
});Background Refresh
You can optionally enable proactive background refreshing at a fixed interval, independent of request activity:
createWristbandM2MClient({
// ...
backgroundTokenRefreshInterval: 900, // Refresh every 15 minutes
});Background refresh failures are silently swallowed; they will not throw or affect foreground getToken() calls. If the token is still invalid after a failed background refresh, the next getToken() call will fetch a fresh one.
On Node.js, Deno, and Bun, unref() is called on the background timer so it will not prevent the process from exiting cleanly.
[!NOTE] Background refresh is not suitable for serverless or edge runtimes (Cloudflare Workers, AWS Lambda, etc.) where the process does not persist between requests. Avoid setting
backgroundTokenRefreshIntervalin these environments and rely ongetToken()'s built-in lazy refresh instead.
SDK Configuration Options
| Option | Type | Required | Default | Description |
| ------ | ---- | -------- | ------- | ----------- |
| clientId | string | Yes | — | The client ID of the Wristband M2M OAuth2 Client. |
| clientSecret | string | Yes | — | The client secret of the Wristband M2M OAuth2 Client. |
| wristbandApplicationVanityDomain | string | Yes | — | The vanity domain of the Wristband application. |
| tokenExpirationBuffer | number | No | 60 | Seconds to subtract from the token's expiration for early refresh. Must be >= 0. |
| backgroundTokenRefreshInterval | number \| undefined | No | undefined | Seconds between background refresh attempts. undefined disables background refresh. Minimum is 60 seconds when provided. |
API
createWristbandM2MClient(config)
Factory function that creates and returns a new WristbandM2MClient instance.
const client = createWristbandM2MClient({ ... });WristbandM2MClient
getToken()
Returns a valid access token, fetching a new one if the cache is empty or expired. Concurrent calls while a fetch is in flight are coalesced. Only one HTTP request is made regardless of how many callers are waiting. Retries up to 3 times on 5xx errors with 100ms between attempts. Fails immediately on 4xx or network errors.
const token = await client.getToken();[!NOTE] In serverless and edge runtimes (Cloudflare Workers, AWS Lambda, etc.), the in-memory token cache does not persist between requests. Each cold start will incur a token fetch. This is expected behaviour;
getToken()will still work correctly, it just cannot cache across invocations.
clearToken()
Clears the cached token. The next getToken() call will fetch a fresh one. Use this when a downstream API returns a 401.
client.clearToken();Runtime-specific Setup
Node.js (20+)
No additional setup required. Install and use as shown above.
Deno
import { createWristbandM2MClient } from 'npm:@wristband/typescript-m2m-auth';Bun
No additional setup required. Install and use as shown above.
Cloudflare Workers
Instantiate the client outside the request handler to reuse it across requests within the same isolate:
import { createWristbandM2MClient } from '@wristband/typescript-m2m-auth';
const m2mClient = createWristbandM2MClient({
clientId: env.CLIENT_ID,
clientSecret: env.CLIENT_SECRET,
wristbandApplicationVanityDomain: env.VANITY_DOMAIN,
});
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const token = await m2mClient.getToken();
// ...
},
};Related Wristband SDKs
This SDK can work in tandem with the Wristband TypeScript JWT SDK for JWT validation. It handles JWT signature verification, token parsing, and JWKS key management. Refer to that GitHub repository for more information on JWT validation configuration and options.
Wristband Express M2M Demo App
You can check out the Wristband Express M2M demo app to see this SDK in action. Refer to that GitHub repository for more information.
Questions
Reach out to the Wristband team at [email protected] for any questions regarding this SDK.
