Securing M2M tokens in B2B SaaS: Storage, rotation, expiry

Kuntal Banerjee
Founding Engineer

Machine-to-machine (M2M) authentication powers core infrastructure in B2B SaaS environments. Whether it’s internal services syncing data, external clients automating tasks, or CI/CD pipelines deploying code, M2M tokens unlock access to secure access to APIs. But with that access comes risk; tokens misused or exposed can lead to system-wide compromise.

This guide breaks down how to store, rotate, and expire M2M tokens securely. It focuses on the OAuth 2.0 Client Credentials Flow, the most common authentication method for app-to-app communication in B2B stacks. You’ll learn what good security looks like, how to avoid common pitfalls, and how to implement token safeguards that scale.

Why OAuth token security matters in M2M integrations

APIs in B2B SaaS products are often accessible to external systems, partner apps, and internal automation. These integrations typically rely on OAuth 2.0 tokens issued via the Client Credentials Grant, no user logs in, no browser interaction happens, just raw app-to-app trust.

That trust hinges entirely on token security.

OAuth tokens are not just access passes; they're the keys to production systems managed by an authorization server. If a token falls into the wrong hands, attackers can impersonate legitimate services, extract customer data, or trigger destructive workflows using an authorization token..

Real-world breaches highlight the risk:

  • GitHub secret leaks: Developers accidentally commit client secrets or access tokens to public repositories
  • Long-lived token abuse: Tokens without expiration stay valid indefinitely, silently allowing access
  • CI/CD exfiltration: Build logs and environment dumps expose tokens used in automation pipelines

These aren’t edge cases; they’re recurring incidents that attackers actively scan for. With machine-to-machine authentication, no user alerts you when something feels off. Systems talk to each other without human involvement, so it’s on you to ensure those conversations happen securely.

Token types: Access, refresh, and client secrets

The OAuth 2.0 Client Credentials flow simplifies authentication for M2M systems, but it still involves a few moving parts. Knowing the role of each token type helps clarify what needs to be protected and how.

Access tokens: Short-lived and scoped

Access tokens represent authorization to access a resource. In M2M flows, these tokens are issued when a client app authenticates using its credentials (client ID and secret).

  • Lifetime: Typically short, often between 5 and 60 minutes
  • Scope: Limited to what the app is allowed to do (read-only, write access, etc.)
  • Usage: Sent with API requests as a Bearer token in the Authorization header

Because they grant access directly, access tokens must not be cached in plain text, exposed in logs, or embedded in static files.

Refresh tokens: Rarely used in M2M

Refresh tokens are designed to let clients request new access tokens without re-authenticating. They're commonly issued in user-based flows like Authorization Code Grant, but not in machine-to-machine setups where a user token is not involved. The client can request tokens from the authorization server to gain access to resources on the resource server.

Most providers do not issue refresh tokens in client credentials flows.

  • Why: M2M clients can authenticate themselves repeatedly, no user friction to avoid
  • Exceptions: Some identity providers (e.g., custom OAuth setups or specific B2B platforms) may allow issuing refresh tokens even in M2M scenarios, but this is not standard behavior
  • Example: Auth0 explicitly disables refresh tokens in Client Credentials Grant by default

If your provider supports refresh tokens for M2M clients, apply stricter storage and rotation policies, since refresh tokens typically last much longer than access tokens.

Client secrets: The root of trust

Client secrets are the app’s identity, akin to a password for an OAuth client. They are used to authenticate the client when requesting tokens via the token endpoint.

  • Storage: Must be encrypted at rest and protected from unauthorized access
  • Rotation: Rotate regularly (e.g., every 90 days), and always when credentials are suspected to be exposed
  • Audit: Track when and where secrets are used to detect anomalies

Secure token generation in M2M Flows

Machine-to-machine token generation relies on the Client Credentials Flow, a straightforward OAuth 2.0 grant that doesn't involve user interaction. While it's simple to implement, security starts right at the point where tokens are requested.

Step 1: Requesting a token via Client Credentials

To get a token, the client sends a POST authentication request to the OAuth provider’s token endpoint, authenticating with its client ID and secret as part of the authorization protocol.

Example (cURL):

curl -X POST https://auth.example.com/oauth/token \ -H "Content-Type: application/x-www-form-urlencoded" \ -d "grant_type=client_credentials" \ -d "client_id=${CLIENT_ID}" \ -d "client_secret=${CLIENT_SECRET}" \ -d "scope=read:data"

This request must always go over HTTPS to prevent credentials and tokens from being intercepted.

Step 2: Token generation in code

Here’s a minimal example in Node.js using axios:

const axios = require('axios'); async function getAccessToken() {  const response = await axios.post('https://auth.example.com/oauth/token', new URLSearchParams({    grant_type: 'client_credentials', client_id: process.env.CLIENT_ID, client_secret: process.env.CLIENT_SECRET, scope: 'read:data'  }));  return response.data.access_token; }


Important
: Never hardcode your client_id or client_secret in code.
Use environment variables, configuration managers, or secret vaults (e.g., HashiCorp Vault, AWS Secrets Manager) to inject them securely at runtime.

Step 3: Apply granular and time-bound access

Secure token generation isn’t just about fetching tokens; it’s about limiting their blast radius:

  • Restrict scopes: Don’t ask for broad access. Define narrow scopes tied to specific APIs or actions
  • Use short expiration windows: Configure the token lifetime via expires_in, 5 to 15 minutes is ideal for most M2M use cases
  • Monitor token usage: Set up alerts for unusual patterns (e.g., tokens used outside expected timeframes or IP ranges)

Step 4: Use asymmetric client authentication (Where supported)

Some OAuth providers support private key JWT authentication for clients. This approach replaces client secrets with signed JWTs, allowing:

  • No shared secrets
  • Better auditability
  • Support for rotation via key pairs

This adds a layer of cryptographic assurance and avoids transmitting sensitive secrets altogether.

Example: Private key JWT authentication can be configured in providers like Auth0, Azure AD, and Okta.

Storing M2M tokens and Client Credentials securely

M2M tokens and credentials often grant privileged access to internal APIs and systems. Improper storage can open the door to unauthorized access, data leaks, or infrastructure compromise. Securing them isn’t optional; it’s foundational.

What not to do

Avoid these common mistakes that expose sensitive credentials:

  • Never hardcode secrets in source files (even temporarily)
  • Never commit tokens or secrets to version control (e.g., Git)
  • Never log credentials or access tokens, even during debugging

Once committed or logged, secrets are hard to revoke and easy to exfiltrate, especially in public or shared repositories.

Where and how to store secrets safely

Use dedicated secrets management solutions to securely store and access client credentials (client_id, client_secret) and private keys, enforcing a strong credential rotation policy. This ensures that secure communication is maintained throughout the client application lifecycle.

Provider
Recommended For
HashiCorp Vault
Enterprise-grade, dynamic secrets, policies
AWS Secrets Manager
AWS-native apps, IAM integration
GCP Secret Manager
Lightweight, great for GCP environments
Azure Key Vault
Role-based access with Azure services

Automating token rotation and expiry handling

Static secrets and long-lived tokens are a ticking time bomb in B2B systems. If compromised, they give attackers prolonged access to APIs and services. Automating token rotation shrinks this exposure window and ensures credentials don’t become liabilities.

Why rotation matters

  • Mitigates leaked secrets: If credentials are exposed (logs, repos, misconfigurations), regular rotation renders them useless quickly.
  • Reduces blast radius: Frequent rotation limits how long a stolen secret remains valid.
  • Complies with security policies: Many enterprise environments enforce secret life cycles by default.

Token rotation options

Rotate Client Credentials via provider APIs

Most identity providers (e.g., Auth0, Azure AD, AWS Cognito) support rotating client secrets through their API or admin interface.

  • Generate a new secret
  • Update the secret in your secrets manager
  • Revoke the old one after confirming the transition

Use automation to update the secret across systems (e.g., CI/CD, Vault, apps).

Use CI/CD for time-bound credentials

Set time-bound secrets or use tools like Terraform to periodically trigger secret updates in a secure and trackable way.

Automating token expiry handling (in code)

M2M tokens typically expire in minutes. Your service should gracefully handle token expiry and re-authenticate as needed.

Example: Token caching with expiry awareness (Node.js)

let cachedToken = null; let expiryTimestamp = 0; async function getToken() { const now = Date.now(); if (!cachedToken || now >= expiryTimestamp) {    const res = await fetchToken(); // call to get new token cachedToken = res.access_token; expiryTimestamp = now + res.expires_in * 1000 - 5000; // add buffer  }  return cachedToken; }

Example: Retry on expired token

async function makeApiRequest() {  try { const token = await getToken(); return await axios.get("https://api.example.com/data", { headers: { Authorization: `Bearer ${token}` },    });  } catch (err) { if (err.response?.status === 401) { cachedToken = null; // force refresh      return makeApiRequest(); // retry    }    throw err;  } }

Automate credential rotation with Cron or Serverless

You can run secret rotation workflows on a schedule using GitHub Actions, Lambda, or Terraform:

Sample Cron Job (Linux)

# Rotate secrets every 6 hours 0 */6 * * * /usr/local/bin/rotate-m2m-credentials.sh >> /var/log/rotation.log

GitHub actions workflow

name: Rotate Client Secret on: schedule: - cron: '0 */12 * * *' jobs: rotate-secret: runs-on: ubuntu-latest steps: - name: Fetch new secret from IdP run: | NEW_SECRET=$(curl -X POST https://idp.example.com/rotate) echo "New secret: $NEW_SECRET" - name: Store in Vault run: | vault kv put secret/client-creds client_secret=$NEW_SECRET

For Terraform-based infra, use terraform-provider-vault with rotate_secret capabilities.

Real-world code example: Secure M2M flow (Node.js)

Below is a production-ready Node.js example demonstrating a secure Machine-to-Machine (M2M) token fetch using the client credentials grant. It includes token caching, expiry checks, and reading secrets from AWS Secrets Manager.

Setup assumptions

  • You're using AWS Secrets Manager to store client_id and client_secret
  • Your identity provider supports the standard OAuth 2.0 token endpoint
  • You’ve set the environment variable AWS_REGION

Dependencies

npm install axios @aws-sdk/client-secrets-manager

Secure token client (tokenClient.js)

const axios = require('axios'); const { SecretsManagerClient, GetSecretValueCommand } = require('@aws-sdk/client-secrets-manager'); const secretsClient = new SecretsManagerClient({ region: process.env.AWS_REGION }); let cachedToken = null; let expiryTimestamp = 0; async function getClientCredentials() { const command = new GetSecretValueCommand({ SecretId: 'm2m-client-credentials' }); const response = await secretsClient.send(command); const { client_id, client_secret, token_url } = JSON.parse(response.SecretString); return { client_id, client_secret, token_url }; } async function fetchAccessToken() { const { client_id, client_secret, token_url } = await getClientCredentials(); const res = await axios.post( token_url, new URLSearchParams({ grant_type: 'client_credentials', client_id, client_secret, }), { headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, timeout: 5000, httpsAgent: new (require('https').Agent)({ rejectUnauthorized: true }), } ); cachedToken = res.data.access_token; expiryTimestamp = Date.now() + res.data.expires_in * 1000 - 5000; return cachedToken; } async function getAccessToken() { if (!cachedToken || Date.now() >= expiryTimestamp) { return await fetchAccessToken(); } return cachedToken; } module.exports = { getAccessToken };

Usage example

const { getAccessToken } = require('./tokenClient'); async function callProtectedAPI() { const token = await getAccessToken(); const response = await axios.get('https://api.your-service.com/secure-data', { headers: { Authorization: `Bearer ${token}` }, }); console.log(response.data); } callProtectedAPI().catch(console.error);

Token security checklist for B2B SaaS

Securing M2M flows is about reducing the attack surface and enforcing best practices consistently. Use the checklist below to audit your integration setup:

Area
Best Practice
Storage
Store client credentials in a secure vault (e.g., AWS Secrets Manager, Vault, Azure Key Vault). Do not persist access tokens in vaults; cache in memory.
Transport
Always use HTTPS for token exchange and secret retrieval. Never accept plaintext tokens or credentials.
Token Scope
Limit token scopes to only what's necessary for the machine integration. Avoid wide or catch-all scopes.
TTL Enforcement
Set short expires_in durations (5–15 minutes). Avoid infinite or long-lived access tokens.
Rotation
Rotate client secrets regularly using IdP APIs and update them in vaults with automation.
Misuse Detection
Log all token issuance and use. Monitor for unusual patterns or unexpected token grants. Set up alerts.
Access Restrictions
Restrict token access to known IP ranges or enforce mutual TLS (mTLS) for critical endpoints.

This checklist applies to both platform and tenant-level M2M integrations in B2B SaaS. Locking down these patterns early prevents breaches and builds enterprise trust.

Tools that help secure M2M OAuth tokens

Tool
Purpose
Notes
HashiCorp Vault
Secrets storage for client credentials
Strong RBAC, dynamic secrets
AWS Secrets Manager
Secure storage and rotation of credentials
IAM-integrated, supports rotation workflows
GCP Secret Manager
Credential storage
GCP-native IAM control
Keycloak
Self-hosted OAuth2 provider
Supports client credentials and rotation
Postman/curl
Testing token endpoints
Avoid saving tokens in shared environments

Here’s where each tool shines:

  • HashiCorp Vault Ideal for teams that need fine-grained access control and dynamic secret generation. It's cloud-agnostic and fits well in hybrid or multi-cloud environments. Use it when you want to centralize secrets outside your cloud provider.
  • AWS Secrets Manager Best suited for teams fully operating within AWS. Seamlessly integrates with IAM roles and supports automated rotation using Lambda functions.
  • GCP Secret Manager Lightweight and native for GCP-based stacks. Simplifies credential management for GCP workloads and supports granular IAM policies.
  • Keycloak A great choice if you're hosting your own identity infrastructure. Offers full control over OAuth clients, flows, and token lifecycles. Perfect for custom or on-premises use cases.
  • Postman/curl
    Useful for testing and validating token endpoints. Stick to local environments for usage and avoid storing sensitive data in shared or cloud-synced collections.

Conclusion

Machine-to-machine (M2M) communication underpins much of modern B2B SaaS. Whether you're connecting backend microservices or enabling third-party API access, OAuth tokens are the gatekeepers that ensure secure access to your platform. Their security isn't optional; it's fundamental.

To keep your system resilient and trustworthy:

  • Store only client credentials in vaults, never persist access tokens
  • Use short-lived access tokens, and rotate them on strict schedules.
  • Log and monitor usage, track how tokens are used, and flag anomalies.
  • Automating everything, from token generation to secret rotation, reduces the chances of human error.

Next, audit your current OAuth flows:

  • Are you securely storing client credentials and access tokens?
  • Are you using short-lived tokens and rotating secrets regularly?
  • Do you have proper access controls and scopes in place?

If the answer is no, start there. Strengthen the foundation before scaling the stack.

FAQs

Why are short-lived tokens better than long-lived ones in M2M auth?

Short-lived tokens limit the window of exposure if leaked. Even if compromised, they quickly expire, minimizing potential damage. Long-lived tokens, on the other hand, offer attackers more time to exploit access, violating the principles of the authorization protocol.

What’s the difference between storing tokens vs. secrets?

Secrets (like client_id and client_secret) are static credentials and should be stored securely in vaults. Tokens are dynamic, time-bound, and should be cached in memory. Persisting tokens introduce unnecessary risk.

Can I use refresh tokens in Client Credentials Flow?

Generally, no. The OAuth 2.0 spec doesn't include refresh tokens in the Client Credentials flow. Some providers may offer extensions, but it's not standard,  and it often defeats the purpose of short-lived tokens.

Can I reuse the same M2M access token across multiple services?

Yes, if all services trust the same identity provider and token audience. But be careful: overuse can increase blast radius. It’s better to scope tokens per service where possible.

Why doesn’t the Client Credentials Flow support refresh tokens?

Because M2M clients can authenticate directly using their credentials, there’s no need to "refresh" tokens like in user flows. Instead, they request a new access token on demand. This design keeps the flow simple and secure.

No items found.
On this page
Share this article
Start scaling
into enterprise

Acquire enterprise customers with zero upfront cost

Every feature unlocked. No hidden fees.
Start Free
$0
/ month
3 FREE SSO/SCIM connections
Built-in multi-tenancy and organizations
SAML, OIDC based SSO
SCIM provisioning for users, groups
Unlimited users
Unlimited social logins
M2M authentication

Securing M2M tokens in B2B SaaS: Storage, rotation, expiry

Kuntal Banerjee

Machine-to-machine (M2M) authentication powers core infrastructure in B2B SaaS environments. Whether it’s internal services syncing data, external clients automating tasks, or CI/CD pipelines deploying code, M2M tokens unlock access to secure access to APIs. But with that access comes risk; tokens misused or exposed can lead to system-wide compromise.

This guide breaks down how to store, rotate, and expire M2M tokens securely. It focuses on the OAuth 2.0 Client Credentials Flow, the most common authentication method for app-to-app communication in B2B stacks. You’ll learn what good security looks like, how to avoid common pitfalls, and how to implement token safeguards that scale.

Why OAuth token security matters in M2M integrations

APIs in B2B SaaS products are often accessible to external systems, partner apps, and internal automation. These integrations typically rely on OAuth 2.0 tokens issued via the Client Credentials Grant, no user logs in, no browser interaction happens, just raw app-to-app trust.

That trust hinges entirely on token security.

OAuth tokens are not just access passes; they're the keys to production systems managed by an authorization server. If a token falls into the wrong hands, attackers can impersonate legitimate services, extract customer data, or trigger destructive workflows using an authorization token..

Real-world breaches highlight the risk:

  • GitHub secret leaks: Developers accidentally commit client secrets or access tokens to public repositories
  • Long-lived token abuse: Tokens without expiration stay valid indefinitely, silently allowing access
  • CI/CD exfiltration: Build logs and environment dumps expose tokens used in automation pipelines

These aren’t edge cases; they’re recurring incidents that attackers actively scan for. With machine-to-machine authentication, no user alerts you when something feels off. Systems talk to each other without human involvement, so it’s on you to ensure those conversations happen securely.

Token types: Access, refresh, and client secrets

The OAuth 2.0 Client Credentials flow simplifies authentication for M2M systems, but it still involves a few moving parts. Knowing the role of each token type helps clarify what needs to be protected and how.

Access tokens: Short-lived and scoped

Access tokens represent authorization to access a resource. In M2M flows, these tokens are issued when a client app authenticates using its credentials (client ID and secret).

  • Lifetime: Typically short, often between 5 and 60 minutes
  • Scope: Limited to what the app is allowed to do (read-only, write access, etc.)
  • Usage: Sent with API requests as a Bearer token in the Authorization header

Because they grant access directly, access tokens must not be cached in plain text, exposed in logs, or embedded in static files.

Refresh tokens: Rarely used in M2M

Refresh tokens are designed to let clients request new access tokens without re-authenticating. They're commonly issued in user-based flows like Authorization Code Grant, but not in machine-to-machine setups where a user token is not involved. The client can request tokens from the authorization server to gain access to resources on the resource server.

Most providers do not issue refresh tokens in client credentials flows.

  • Why: M2M clients can authenticate themselves repeatedly, no user friction to avoid
  • Exceptions: Some identity providers (e.g., custom OAuth setups or specific B2B platforms) may allow issuing refresh tokens even in M2M scenarios, but this is not standard behavior
  • Example: Auth0 explicitly disables refresh tokens in Client Credentials Grant by default

If your provider supports refresh tokens for M2M clients, apply stricter storage and rotation policies, since refresh tokens typically last much longer than access tokens.

Client secrets: The root of trust

Client secrets are the app’s identity, akin to a password for an OAuth client. They are used to authenticate the client when requesting tokens via the token endpoint.

  • Storage: Must be encrypted at rest and protected from unauthorized access
  • Rotation: Rotate regularly (e.g., every 90 days), and always when credentials are suspected to be exposed
  • Audit: Track when and where secrets are used to detect anomalies

Secure token generation in M2M Flows

Machine-to-machine token generation relies on the Client Credentials Flow, a straightforward OAuth 2.0 grant that doesn't involve user interaction. While it's simple to implement, security starts right at the point where tokens are requested.

Step 1: Requesting a token via Client Credentials

To get a token, the client sends a POST authentication request to the OAuth provider’s token endpoint, authenticating with its client ID and secret as part of the authorization protocol.

Example (cURL):

curl -X POST https://auth.example.com/oauth/token \ -H "Content-Type: application/x-www-form-urlencoded" \ -d "grant_type=client_credentials" \ -d "client_id=${CLIENT_ID}" \ -d "client_secret=${CLIENT_SECRET}" \ -d "scope=read:data"

This request must always go over HTTPS to prevent credentials and tokens from being intercepted.

Step 2: Token generation in code

Here’s a minimal example in Node.js using axios:

const axios = require('axios'); async function getAccessToken() {  const response = await axios.post('https://auth.example.com/oauth/token', new URLSearchParams({    grant_type: 'client_credentials', client_id: process.env.CLIENT_ID, client_secret: process.env.CLIENT_SECRET, scope: 'read:data'  }));  return response.data.access_token; }


Important
: Never hardcode your client_id or client_secret in code.
Use environment variables, configuration managers, or secret vaults (e.g., HashiCorp Vault, AWS Secrets Manager) to inject them securely at runtime.

Step 3: Apply granular and time-bound access

Secure token generation isn’t just about fetching tokens; it’s about limiting their blast radius:

  • Restrict scopes: Don’t ask for broad access. Define narrow scopes tied to specific APIs or actions
  • Use short expiration windows: Configure the token lifetime via expires_in, 5 to 15 minutes is ideal for most M2M use cases
  • Monitor token usage: Set up alerts for unusual patterns (e.g., tokens used outside expected timeframes or IP ranges)

Step 4: Use asymmetric client authentication (Where supported)

Some OAuth providers support private key JWT authentication for clients. This approach replaces client secrets with signed JWTs, allowing:

  • No shared secrets
  • Better auditability
  • Support for rotation via key pairs

This adds a layer of cryptographic assurance and avoids transmitting sensitive secrets altogether.

Example: Private key JWT authentication can be configured in providers like Auth0, Azure AD, and Okta.

Storing M2M tokens and Client Credentials securely

M2M tokens and credentials often grant privileged access to internal APIs and systems. Improper storage can open the door to unauthorized access, data leaks, or infrastructure compromise. Securing them isn’t optional; it’s foundational.

What not to do

Avoid these common mistakes that expose sensitive credentials:

  • Never hardcode secrets in source files (even temporarily)
  • Never commit tokens or secrets to version control (e.g., Git)
  • Never log credentials or access tokens, even during debugging

Once committed or logged, secrets are hard to revoke and easy to exfiltrate, especially in public or shared repositories.

Where and how to store secrets safely

Use dedicated secrets management solutions to securely store and access client credentials (client_id, client_secret) and private keys, enforcing a strong credential rotation policy. This ensures that secure communication is maintained throughout the client application lifecycle.

Provider
Recommended For
HashiCorp Vault
Enterprise-grade, dynamic secrets, policies
AWS Secrets Manager
AWS-native apps, IAM integration
GCP Secret Manager
Lightweight, great for GCP environments
Azure Key Vault
Role-based access with Azure services

Automating token rotation and expiry handling

Static secrets and long-lived tokens are a ticking time bomb in B2B systems. If compromised, they give attackers prolonged access to APIs and services. Automating token rotation shrinks this exposure window and ensures credentials don’t become liabilities.

Why rotation matters

  • Mitigates leaked secrets: If credentials are exposed (logs, repos, misconfigurations), regular rotation renders them useless quickly.
  • Reduces blast radius: Frequent rotation limits how long a stolen secret remains valid.
  • Complies with security policies: Many enterprise environments enforce secret life cycles by default.

Token rotation options

Rotate Client Credentials via provider APIs

Most identity providers (e.g., Auth0, Azure AD, AWS Cognito) support rotating client secrets through their API or admin interface.

  • Generate a new secret
  • Update the secret in your secrets manager
  • Revoke the old one after confirming the transition

Use automation to update the secret across systems (e.g., CI/CD, Vault, apps).

Use CI/CD for time-bound credentials

Set time-bound secrets or use tools like Terraform to periodically trigger secret updates in a secure and trackable way.

Automating token expiry handling (in code)

M2M tokens typically expire in minutes. Your service should gracefully handle token expiry and re-authenticate as needed.

Example: Token caching with expiry awareness (Node.js)

let cachedToken = null; let expiryTimestamp = 0; async function getToken() { const now = Date.now(); if (!cachedToken || now >= expiryTimestamp) {    const res = await fetchToken(); // call to get new token cachedToken = res.access_token; expiryTimestamp = now + res.expires_in * 1000 - 5000; // add buffer  }  return cachedToken; }

Example: Retry on expired token

async function makeApiRequest() {  try { const token = await getToken(); return await axios.get("https://api.example.com/data", { headers: { Authorization: `Bearer ${token}` },    });  } catch (err) { if (err.response?.status === 401) { cachedToken = null; // force refresh      return makeApiRequest(); // retry    }    throw err;  } }

Automate credential rotation with Cron or Serverless

You can run secret rotation workflows on a schedule using GitHub Actions, Lambda, or Terraform:

Sample Cron Job (Linux)

# Rotate secrets every 6 hours 0 */6 * * * /usr/local/bin/rotate-m2m-credentials.sh >> /var/log/rotation.log

GitHub actions workflow

name: Rotate Client Secret on: schedule: - cron: '0 */12 * * *' jobs: rotate-secret: runs-on: ubuntu-latest steps: - name: Fetch new secret from IdP run: | NEW_SECRET=$(curl -X POST https://idp.example.com/rotate) echo "New secret: $NEW_SECRET" - name: Store in Vault run: | vault kv put secret/client-creds client_secret=$NEW_SECRET

For Terraform-based infra, use terraform-provider-vault with rotate_secret capabilities.

Real-world code example: Secure M2M flow (Node.js)

Below is a production-ready Node.js example demonstrating a secure Machine-to-Machine (M2M) token fetch using the client credentials grant. It includes token caching, expiry checks, and reading secrets from AWS Secrets Manager.

Setup assumptions

  • You're using AWS Secrets Manager to store client_id and client_secret
  • Your identity provider supports the standard OAuth 2.0 token endpoint
  • You’ve set the environment variable AWS_REGION

Dependencies

npm install axios @aws-sdk/client-secrets-manager

Secure token client (tokenClient.js)

const axios = require('axios'); const { SecretsManagerClient, GetSecretValueCommand } = require('@aws-sdk/client-secrets-manager'); const secretsClient = new SecretsManagerClient({ region: process.env.AWS_REGION }); let cachedToken = null; let expiryTimestamp = 0; async function getClientCredentials() { const command = new GetSecretValueCommand({ SecretId: 'm2m-client-credentials' }); const response = await secretsClient.send(command); const { client_id, client_secret, token_url } = JSON.parse(response.SecretString); return { client_id, client_secret, token_url }; } async function fetchAccessToken() { const { client_id, client_secret, token_url } = await getClientCredentials(); const res = await axios.post( token_url, new URLSearchParams({ grant_type: 'client_credentials', client_id, client_secret, }), { headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, timeout: 5000, httpsAgent: new (require('https').Agent)({ rejectUnauthorized: true }), } ); cachedToken = res.data.access_token; expiryTimestamp = Date.now() + res.data.expires_in * 1000 - 5000; return cachedToken; } async function getAccessToken() { if (!cachedToken || Date.now() >= expiryTimestamp) { return await fetchAccessToken(); } return cachedToken; } module.exports = { getAccessToken };

Usage example

const { getAccessToken } = require('./tokenClient'); async function callProtectedAPI() { const token = await getAccessToken(); const response = await axios.get('https://api.your-service.com/secure-data', { headers: { Authorization: `Bearer ${token}` }, }); console.log(response.data); } callProtectedAPI().catch(console.error);

Token security checklist for B2B SaaS

Securing M2M flows is about reducing the attack surface and enforcing best practices consistently. Use the checklist below to audit your integration setup:

Area
Best Practice
Storage
Store client credentials in a secure vault (e.g., AWS Secrets Manager, Vault, Azure Key Vault). Do not persist access tokens in vaults; cache in memory.
Transport
Always use HTTPS for token exchange and secret retrieval. Never accept plaintext tokens or credentials.
Token Scope
Limit token scopes to only what's necessary for the machine integration. Avoid wide or catch-all scopes.
TTL Enforcement
Set short expires_in durations (5–15 minutes). Avoid infinite or long-lived access tokens.
Rotation
Rotate client secrets regularly using IdP APIs and update them in vaults with automation.
Misuse Detection
Log all token issuance and use. Monitor for unusual patterns or unexpected token grants. Set up alerts.
Access Restrictions
Restrict token access to known IP ranges or enforce mutual TLS (mTLS) for critical endpoints.

This checklist applies to both platform and tenant-level M2M integrations in B2B SaaS. Locking down these patterns early prevents breaches and builds enterprise trust.

Tools that help secure M2M OAuth tokens

Tool
Purpose
Notes
HashiCorp Vault
Secrets storage for client credentials
Strong RBAC, dynamic secrets
AWS Secrets Manager
Secure storage and rotation of credentials
IAM-integrated, supports rotation workflows
GCP Secret Manager
Credential storage
GCP-native IAM control
Keycloak
Self-hosted OAuth2 provider
Supports client credentials and rotation
Postman/curl
Testing token endpoints
Avoid saving tokens in shared environments

Here’s where each tool shines:

  • HashiCorp Vault Ideal for teams that need fine-grained access control and dynamic secret generation. It's cloud-agnostic and fits well in hybrid or multi-cloud environments. Use it when you want to centralize secrets outside your cloud provider.
  • AWS Secrets Manager Best suited for teams fully operating within AWS. Seamlessly integrates with IAM roles and supports automated rotation using Lambda functions.
  • GCP Secret Manager Lightweight and native for GCP-based stacks. Simplifies credential management for GCP workloads and supports granular IAM policies.
  • Keycloak A great choice if you're hosting your own identity infrastructure. Offers full control over OAuth clients, flows, and token lifecycles. Perfect for custom or on-premises use cases.
  • Postman/curl
    Useful for testing and validating token endpoints. Stick to local environments for usage and avoid storing sensitive data in shared or cloud-synced collections.

Conclusion

Machine-to-machine (M2M) communication underpins much of modern B2B SaaS. Whether you're connecting backend microservices or enabling third-party API access, OAuth tokens are the gatekeepers that ensure secure access to your platform. Their security isn't optional; it's fundamental.

To keep your system resilient and trustworthy:

  • Store only client credentials in vaults, never persist access tokens
  • Use short-lived access tokens, and rotate them on strict schedules.
  • Log and monitor usage, track how tokens are used, and flag anomalies.
  • Automating everything, from token generation to secret rotation, reduces the chances of human error.

Next, audit your current OAuth flows:

  • Are you securely storing client credentials and access tokens?
  • Are you using short-lived tokens and rotating secrets regularly?
  • Do you have proper access controls and scopes in place?

If the answer is no, start there. Strengthen the foundation before scaling the stack.

FAQs

Why are short-lived tokens better than long-lived ones in M2M auth?

Short-lived tokens limit the window of exposure if leaked. Even if compromised, they quickly expire, minimizing potential damage. Long-lived tokens, on the other hand, offer attackers more time to exploit access, violating the principles of the authorization protocol.

What’s the difference between storing tokens vs. secrets?

Secrets (like client_id and client_secret) are static credentials and should be stored securely in vaults. Tokens are dynamic, time-bound, and should be cached in memory. Persisting tokens introduce unnecessary risk.

Can I use refresh tokens in Client Credentials Flow?

Generally, no. The OAuth 2.0 spec doesn't include refresh tokens in the Client Credentials flow. Some providers may offer extensions, but it's not standard,  and it often defeats the purpose of short-lived tokens.

Can I reuse the same M2M access token across multiple services?

Yes, if all services trust the same identity provider and token audience. But be careful: overuse can increase blast radius. It’s better to scope tokens per service where possible.

Why doesn’t the Client Credentials Flow support refresh tokens?

Because M2M clients can authenticate directly using their credentials, there’s no need to "refresh" tokens like in user flows. Instead, they request a new access token on demand. This design keeps the flow simple and secure.

No items found.
Ship Enterprise Auth in days