Enterprise SaaS APIs don't agree on authentication. One CRM uses OAuth2 with refresh token rotation. The next ERP uses API key in a custom header. A logistics platform uses JWT with a short expiry and a separate JWKS endpoint for validation. If you're building integrations against multiple enterprise systems, you need an auth layer that handles all three patterns correctly — without branching logic in your application code.
This article documents the three dominant auth patterns in enterprise SaaS APIs, their failure modes, and what a correct abstraction layer looks like. We're not covering SAML or LDAP — those are identity federation protocols for user authentication, not API authorization. We're covering what you'll encounter in machine-to-machine integration work.
OAuth2 in Depth
OAuth2 is the most common auth pattern in modern enterprise SaaS APIs. The authorization code flow is used for user-delegated access (your integration acts on behalf of a specific user). The client credentials flow is used for server-to-server access (your integration acts as itself, with permissions defined by the application).
For enterprise API integrations, client credentials is almost always the right choice. You're not acting as a user; you're syncing data between systems on behalf of your customer. Client credentials flow is simpler: POST your client ID and secret to the token endpoint, receive an access token with an expiry, use the token until it expires, then repeat.
The failure mode that breaks most integrations is token expiry handling. Access tokens typically expire in 3600 seconds (one hour). If your batch job runs for 70 minutes, the token expires mid-job. The correct implementation checks token expiry before each request and refreshes proactively if expiry is within a buffer window (typically 60 seconds). Most hand-rolled implementations either check expiry only at job start (fails mid-job) or refresh on every 401 response (works but adds latency on every token expiry event).
// Correct proactive refresh pattern
async function getValidToken(ctx) {
if (Date.now() > ctx.token.expires_at - 60_000) {
ctx.token = await refreshToken(ctx.credentials);
}
return ctx.token.access_token;
}
The second failure mode is refresh token rotation. Some APIs rotate the refresh token on every use — the refresh request returns a new access token AND a new refresh token, and the old refresh token is invalidated. If your token store doesn't persist the new refresh token (a race condition in concurrent refresh requests is common), you permanently lose access and must re-authorize.
API Key Auth
API key auth is simpler than OAuth2 and appropriate for server-to-server integrations where the API doesn't support OAuth2 client credentials, or where the API owner issues you a static key for your application. The key is typically passed in a request header: X-API-Key: your_key, Authorization: ApiKey your_key, or occasionally as a query parameter (avoid this in production — query parameters appear in server logs).
The failure modes with API key auth are operational rather than protocol-level. Keys don't expire automatically, but they can be rotated by the API owner (for security hygiene or after a detected leak). Your integration needs a way to update the key in your secrets store and have it picked up without redeployment. Hardcoding keys in application code is the obvious mistake; storing them in environment variables with a documented rotation procedure is the minimum standard.
Rate limiting with API key auth is per-key, not per-user. If you're using the same key across multiple integration clients, they share the same rate limit bucket. This is usually fine for single-tenant integrations but breaks for multi-tenant platforms where multiple customers might trigger parallel requests through the same API key.
JWT Authentication
Some enterprise APIs issue short-lived JWTs that your client generates and signs with a private key, rather than exchanging credentials with a token endpoint. The client creates a JWT with the required claims (issuer, subject, audience, expiry), signs it with an RSA or ECDSA private key, and passes it as a Bearer token. The API validates the JWT against your registered public key without a network round-trip to a token server.
JWT auth is common in logistics and supply chain APIs that prioritize low-latency token validation at high request volumes. The main implementation complexity is key management: generating the key pair, registering the public key with the API provider, securely storing the private key, and rotating it when needed. JWT expiry is typically short (5–15 minutes), so you're generating new tokens frequently.
Which to Choose
If the API supports OAuth2 client credentials, use it. It's the most standardized and gives you the clearest revocation path. If the API only offers API key, use it with a secrets manager (AWS Secrets Manager, Vault, or similar) for rotation support. If the API requires JWT, budget extra time for key management setup.
Building an Auth Abstraction
The correct abstraction layer exposes a single interface regardless of the underlying auth pattern:
// Your application code shouldn't know which auth pattern is in use
const token = await authProvider.getValidToken();
// The auth provider implementation is connector-specific
// but the interface is uniform
This is exactly what the Devloom SDK does. dlx.auth.oauth2(), dlx.auth.apiKey(), and dlx.auth.bearer() all return the same AuthConfig interface. The client reads the token via the same interface regardless of how it's issued or refreshed. Your application code doesn't branch on auth type.