Safeguard
Application Security

OAuth vs API Keys

OAuth and API keys aren't interchangeable: one is a static, long-lived credential, the other a scoped, expiring token. Here's how to choose, backed by real breaches.

James
Principal Security Architect
7 min read

In April 2022, attackers used stolen OAuth tokens issued to Heroku and Travis CI to pull private source code from dozens of GitHub organizations, including npm — without ever touching a password. Nine months later, in January 2023, a compromised CircleCI engineer's laptop exposed customer API keys and tokens across CI/CD pipelines, forcing GitHub, AWS, and other vendors into mass credential rotation. Both incidents trace back to the same question every security team eventually has to answer: should this integration authenticate with an API key or an OAuth token? The two look interchangeable in a config file — both are strings pasted into an Authorization header — but they carry very different blast radii, revocation paths, and failure modes once they leak. This post breaks down the practical differences between OAuth and API keys, using real breach data, so you can make the right call the next time a vendor integration or internal service needs authenticated access.

What's the core difference between OAuth and API keys?

An API key is a single static credential that authenticates as a fixed identity with whatever permissions it was assigned at creation, while OAuth is a delegation protocol that issues short-lived, scoped access tokens on behalf of a user or service. When you generate an API key in a dashboard, you get one opaque string — often valid indefinitely — that grants the same access every time it's presented, until someone manually revokes it. OAuth 2.0, by contrast, runs a token exchange: a client authenticates once, receives an access token that typically expires in 1 hour to 24 hours, and uses a separate refresh token to get a new one without re-authenticating. Access tokens can also be scoped down to specific actions (repo:read vs. repo:write, for example), so a compromised token doesn't automatically hand over full account control. That distinction — static and broad vs. ephemeral and scoped — is the entire reason OAuth exists, and it's why most API providers with real security programs (GitHub, Google, Salesforce, Stripe) push developers toward OAuth for anything touching user data.

Why do API keys create more supply chain risk than OAuth tokens?

API keys create more risk because they're long-lived, frequently over-scoped, and rarely rotated, so a single key sitting in a GitHub repo, CI log, or Slack message can grant standing access for months or years after it leaks. GitGuardian's 2024 State of Secrets Sprawl report found more than 12.8 million new secrets — including API keys, tokens, and credentials — exposed on public GitHub repositories in 2023, a 28% increase over 2022, and the same research has repeatedly found that a large share of leaked secrets remain valid long after they're first exposed because nobody notices or rotates them. That's the supply chain risk in a nutshell: a key hard-coded into a build script or checked into .env doesn't just expose one developer's account, it exposes whatever downstream system trusts that key, and it keeps working until someone actively kills it. OAuth tokens shrink that exposure window from "indefinite" to "hours," which is precisely why credential-stuffing and secret-scanning campaigns disproportionately target static API keys over access tokens.

Can OAuth tokens be stolen too?

Yes — OAuth tokens are still bearer tokens, and the April 2022 GitHub incident proves that stealing one gives an attacker the same practical access as stealing a password, just with a shorter shelf life. In that breach, an attacker exfiltrated OAuth tokens that GitHub had issued to the Heroku and Travis CI integrations, then used those tokens to authenticate as those apps and download private repository contents from a number of organizations, including npm's. GitHub, Heroku, and Travis CI had to revoke the affected tokens and force-reauthorize every connected app. The lesson isn't that OAuth is unbreakable — it's that OAuth limits how long a stolen credential stays useful and how much it can do, since a properly scoped token can't reach resources outside its granted permissions, and an expired one is worthless without a valid refresh flow. A leaked API key with admin scope and no expiration has no equivalent safety net.

When should you actually use an API key instead of OAuth?

API keys still make sense for machine-to-machine automation, internal service-to-service calls, and simple integrations where there's no human user to delegate on behalf of and no need for fine-grained, revocable scopes — think a monitoring agent pushing metrics to Datadog, or a CI job pulling artifacts from an internal registry. Building a full OAuth client-credentials flow for a single internal cron job that hits one endpoint is often unnecessary overhead. The trade-off only becomes dangerous when teams default to API keys out of habit for use cases that actually involve third-party access, user data, or broad permissions — which is exactly what happened at Twitter/X in early 2023, when researchers demonstrated that overly permissive, long-lived API keys attached to abandoned or forgotten developer apps remained functional for years, a pattern that shows up across nearly every major API provider's bug bounty history. If a human user, a third-party vendor, or sensitive data is involved, OAuth's scoping and expiration are worth the setup cost.

How do you migrate a service from API keys to OAuth without breaking production?

Migrate incrementally by running both authentication methods in parallel, issuing OAuth clients with scopes that mirror your current key permissions, then deprecating the keys only after telemetry confirms zero traffic on the old credential. Concretely: stand up an OAuth 2.0 authorization server (or use a managed identity provider like Auth0, Okta, or AWS Cognito), map each existing API key's permission set to an equivalent OAuth scope, and add a Deprecation header or logging hook on the legacy key endpoint so you can see exactly which clients still depend on it. Give integrators a fixed sunset date — 90 days is a common window used by providers like Slack and Stripe when they've deprecated legacy tokens — and track adoption weekly rather than assuming a broadcast email is enough. The riskiest part of this migration isn't the protocol change, it's the silent dependency: a partner integration or internal script nobody remembers writing that will break the moment the key stops working. That's a discovery problem as much as an authentication one.

How Safeguard Helps

That discovery problem — knowing which of your leaked or long-lived credentials are actually reachable and exploitable versus dormant and harmless — is exactly where Safeguard's reachability analysis earns its keep, tracing whether a flagged API key or OAuth client secret is wired into a code path that's actually invoked in production rather than flagging every match a scanner finds. Griffin AI correlates those findings with your SBOM (generated automatically or ingested from existing tooling) to show which services, dependencies, and third-party integrations a given credential touches, so a security team can prioritize the OAuth migration for the internet-facing payment integration over the internal cron job. When Safeguard confirms a static API key should be scoped down, rotated, or replaced with an OAuth flow, it can open an auto-fix pull request with the change already drafted, cutting the remediation loop from a backlog ticket to a reviewable diff. For teams juggling hundreds of vendor integrations, that combination of reachability context and automated remediation is what turns "we know we have a secrets sprawl problem" into a shrinking number on a dashboard.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.