September 29, 2026
An API Key and an OAuth Token Differ by One Field, and That Field Decides Who Kills the Credential
An API key identifies the project or application behind a request. An OAuth access token identifies a principal and carries a scope and an…

By Ghost Accounts
6 min read
An API key identifies the project or application behind a request. An OAuth access token identifies a principal and carries a scope and an expiry. Google Cloud's own documentation says a key does not authenticate a principal, so IAM cannot authorize the caller. That missing subject is why keys outlive their owners and tokens expire on their own.
What an API key actually identifies
Picture a key called demo-2021 in one of your cloud projects. Someone minted it the afternoon before a customer demo, pasted it into a header, watched the request return 200, and moved on. Nothing about that key records who did this, what it was for, or when it should stop working. It is a row in a lookup table, and the row is the whole credential.
That is not a criticism of the platform; it is the design. Google Cloud's API key documentation is unusually direct about it: a standard key does not authenticate a principal. It associates the request with a project so the platform can meter quota and bill the right account. Because there is no principal, IAM has nothing to evaluate, and the platform cannot decide whether the caller is allowed to do what it is asking.
Read that back slowly. Attribution, authorization and expiry are all things a server does to a subject. A key has no subject. Restrictions can be layered on afterwards, and they matter, but they narrow where the string works rather than telling you who is holding it. Everything that follows about lifetimes, blast radius and cleanup flows from that one absent field.
How an OAuth access token gets its clock
An OAuth access token is never handed to you in a console. It is the result of a negotiation. The service proves who it is to an authorisation server, using a client ID and secret in the client credentials grant, a signed assertion, or a workload identity the cloud platform vouches for. Only after that does the server mint a token, and it mints it small.
RFC 6749 defines the pieces that make the token different from a key. A subject names the identity it was issued to. A scope lists the operations it covers, and nothing else. An expiry sets how long the resource server will honour it, typically minutes to an hour. Every request is checked against that expiry, so the credential ages out whether or not anyone is watching.
The second half of the flow is what makes it operable. Alongside the short-lived access token, the client holds a refresh token or a platform identity that can request a fresh one. So the long-lived thing your code keeps is proof of identity, and the thing that actually opens the door is always young. For the demo-2021 key there is no such split. The string that proves identity and the string that opens the door are the same string, and it never ages.
Both credentials are bearer tokens, so the clock is the only control
Teams that move from keys to OAuth often assume cryptography now protects the credential in flight. It does not, and RFC 6750 says so in its definition: a bearer token can be used by any party in possession of it, with no need to prove possession of a cryptographic key. Copy the token, replay the token, and the resource server treats you as the legitimate client.
That puts the API key and the access token in the same family. Both are opaque strings that grant access to whoever presents them. The upgrade OAuth delivers is not secrecy. It is time. A stolen access token is a live problem until its expiry, often under an hour. A stolen demo-2021 key is a live problem until a person notices it, which may be never.
There is a way to shrink even that window. Sender-constrained tokens bind the credential to a client certificate or a proof key, so a copied token is worthless without the private material that goes with it. Mutual TLS binding and DPoP both work this way, and provider support is spreading. Until your identity provider offers one, the expiry is the control you actually have, so treat every design decision about credentials as a decision about how long a leak stays useful.
Attribution, lifetime, scope and revocation: where the four differences bite
Attribution is the first casualty. A request carrying an access token lets your logs record the subject, so you know which workload, pipeline or agent made the call. A request carrying a key records the key and whatever label someone typed in a hurry. Access review, the process that asks whether an identity should still hold a permission, cannot run against a string with no owner, and after an incident the first hours go to discovering what the key was even for.
Lifetime and scope follow the same pattern. A token expires with no ticket and no maintenance window. A key expires when someone decides it should, and deleting a key that might still be wired into a running system is the task that never leaves the bottom of the backlog. A token requests the scope the job needs; a key inherits everything the account behind it can do, which is usually far more than the one endpoint it was created to reach.
Revocation is where the theory turns into headlines. In its November 2023 disclosure, Cloudflare reported that an attacker entered a self-hosted Atlassian environment using one service token and three service account credentials exposed in a third-party breach the previous month and never rotated. Dropbox reported in April 2024 that a compromised production service account in the Dropbox Sign environment exposed customer API keys and OAuth tokens, which the company then rotated and invalidated. At population scale, GitGuardian counted 23.77 million new hardcoded secrets in public GitHub commits during 2024, roughly a quarter more than the year before. A token in that pile expired before the scan finished. A key in that pile is still a door.
When an API key is still the right choice
None of this argues for ripping every key out this quarter, and a plan that tries will not survive your engineers. The Google Cloud documentation describes exactly where a key belongs: public APIs called from browsers or mobile apps, where the job is to meter a client for quota and billing rather than to authorize it. A developer's first five minutes with your API is another good fit; an OAuth handshake there loses more people than it protects. The same documentation warns against using keys in production for APIs that create or manage cloud resources.
If a key must stay, make it smaller. Google offers two families of restrictions. Application restrictions limit where the key works: IP addresses or CIDR ranges, HTTP referrers with wildcards, Android package names with a SHA-1 fingerprint, or iOS bundle IDs. API restrictions limit which services the key can reach, and the console and command line now require them. The documentation is blunt that unrestricted keys are insecure.
Be honest about what restrictions buy. An IP allowlist stops a stranger calling from elsewhere and does nothing about a build agent already inside the range. A referrer check stops casual browser reuse and folds to anyone willing to set a header. Restrictions shrink the surface; they still do not give the key an owner or a clock.
Retiring the keys you already have
Start with an inventory, but do not sort it by age. Sort it by blast radius: what the key can reach if someone else is holding it. A read-only key on a public dataset can wait. A key with write access to customer records goes to the top. demo-2021 turns out to hold Editor on a project that now includes a production bucket, so it leads the list.
Second, prove use before you delete. Most platforms expose a last-used timestamp or an access log. If a high-blast-radius key has been silent for ninety days, it is a deletion candidate, not a mystery. Google's rotation flow creates a replacement with identical restrictions, lets you migrate callers, then deletes the original, and a deleted key can be restored for thirty days if something breaks. That safety net removes the usual excuse for leaving the old key alive during the overlap.
Third, replace with something that expires: a client credentials grant, a federated workload identity, or a short-lived token the platform already issues. Fourth, give every surviving key an owner recorded somewhere a human reads. demo-2021 ends as a workload identity bound to the one service that still needed it, the original key silent for a quarter and then deleted.
Then fix the intake, or the inventory refills behind you. A key request should be a form that asks who owns it, what it may reach and when it stops. A request that cannot answer all three gets a short-lived credential instead.
This article expands on our video "What Is the Difference Between an API Key and an OAuth Token?" — watch it here.