RC RANDOM CHAOS

OAuth converts consent into standing permission

OAuth 2.0 issues a token, not an identity. It verifies authority once at consent and honours the reference thereafter, without ever revalidating the grant.

· 10 min read
OAuth converts consent into standing permission

OAuth 2.0 issues a token. That is the whole of its function, and it is worth stating plainly because the function is so often mistaken for something larger. Under RFC 6749, the authorisation server receives a request from a registered client, checks that request against a set of pre-configured parameters, and returns an access token. It does not ask who is holding the token. It does not ask whether the client requesting access is the same client, in the same condition, with the same intent, as the one that was registered. It resolves a permission and it emits a credential. The resource server that later receives that token treats its presence as sufficient. A valid bearer token is, by design, its own proof of authorisation. Possession is the argument. Nothing behind the possession is interrogated at the moment of use.

This is not a flaw in a particular deployment. It is the documented behaviour of the protocol. The authorisation grant flow was built to solve a specific problem, which was to let one application act on a resource owner’s behalf without that application ever holding the resource owner’s password. The mechanism that solves that problem is delegation. A scope is granted once, a token is issued, and from that point the client operates with the authority the grant conferred. The system is optimised for exactly this: to convert a one-time act of consent into a durable, reusable statement of permission that can be presented again and again without the original decision being revisited. When a client presents an access token to a resource server, or exchanges a refresh token for a new access token at the token endpoint, the server is not assessing legitimacy. It is confirming that a token exists and that it maps to a scope. Those are different questions, and the protocol answers only the second.

So the object at the centre of an OAuth 2.0 flow is not an identity and it is not a verification. It is a reference. The token points back to a decision made at some earlier moment, under conditions that are no longer being examined. The authorisation server’s certainty is real, but it is certainty about the token’s provenance, not about the state of the world the token now operates in. Identity layers built on top, OpenID Connect issuing an ID token alongside the access token, do not change this underneath. They add a claim about who authenticated at sign-in. They do not add a re-check at the point where the delegated authority is actually spent. The system verifies once and delegates thereafter, and it does so because that is precisely what it was constructed to do.

The assumption sitting under that construction is that trust, once granted, holds. When a resource owner consents to a scope, the model treats that consent as a stable fact about the relationship between the resource owner, the client, and the resource. The grant is written down in the form of a token, and the token is then trusted as a faithful proxy for the grant. This is where the substitution happens. The reality being represented is a human decision made at a particular instant with a particular understanding of what the client was and what it would do. The proxy is a string presented at an API endpoint. The protocol assumes those two things remain equivalent for as long as the token lives, and refresh tokens are engineered specifically to make that life long. A refresh token exists so that the delegated authority need not be re-established. It is the assumption of persistence given a concrete form.

The assumption is also one of transferability. The grant was made to a client, an entity abstracted down to a client_id and a client_secret, and the token carries the authority of that grant to wherever it is presented. But the token does not know which process is holding it, whether that process is the one the resource owner had in mind, or whether the credential now sits in a location the resource owner never anticipated. The model assumes that whoever holds the token stands in the position the grant intended. Trust attaches to the reference and travels with it. This is deliberate. Bearer semantics, defined in RFC 6750, state outright that any party in possession of the token can use it. The specification is honest about what it is doing. It delegates authority to a string and asks nothing further of the party that presents it.

And the assumption is one of continued validity. Nothing in the base flow requires the authorisation server to reconsider a grant while the token remains within its lifetime. The consent was valid when it was given, and the system carries that validity forward unchanged. There is no built-in moment at which the grant is measured against the present. The scope that was reasonable at consent is the scope that is honoured now. The client that was trustworthy at registration is the client that is trusted now. Time does not enter the evaluation, because at the moment of use there is no evaluation into which time could enter. The token is either within its window or it is not, and within that window the past decision is simply the present authority.

What changed is none of these mechanics. The protocol behaves today as it was specified to behave. What changed is the standing of the assumption the mechanics rest on. A grant is a statement about a relationship at a point in time, and points in time do not persist. The client that received the scope can be updated, re-hosted, acquired, or quietly repurposed after the grant was made, and the token issued to it continues to resolve to the same authority regardless. The consent was accurate when the resource owner gave it. It describes a world that has since moved. The token does not move with it. It holds the shape of the original decision and presents that shape at every subsequent use, and the resource server, honouring the token, honours a description of conditions that may no longer obtain.

The system does not register this because it was never built to. Registering it would require the authorisation server to re-evaluate a grant it has already resolved, to treat each presentation of a token as a fresh question rather than a lookup against a prior answer. That is not what the flow does. The flow inherits trust from a past state and applies it to the present without inspecting the distance between them. A refresh token exchanged months after the original consent produces a new access token with the same untroubled certainty as one exchanged minutes later. The token endpoint is not asking whether the grant still reflects reality. It is confirming that the grant was once made and has not been explicitly revoked. Absence of revocation is read as continued validity, and those are not the same condition. One is an active statement about the present. The other is silence about the past.

This is the quiet shift. The delegation model did not degrade and no capability was added to it. What degraded was the correspondence between the token and the thing it stands for. In the moment of consent, the proxy and the reality were aligned, and the system’s trust in the proxy was trust in the reality. Over the life of the token that alignment is assumed rather than maintained, and the assumption is load-bearing. The authority the system extends is exactly as current as the last time anyone checked, and in the OAuth 2.0 authorisation grant flow the last time anyone checked was the moment the grant was first made. Everything after that is the system spending a decision it has stopped examining.

The failure is not an event. It is the resource server performing its function correctly. When a request arrives carrying a bearer token, the resource server reads the token, confirms it is well-formed, within its lifetime, and mapped to a scope that permits the requested operation, and it performs the operation. Under RFC 6750 that sequence is complete. Nothing in it examines the party presenting the token. The check runs against the reference, not against the state of the world the reference now sits in.

What has happened is that the identity of the source has quietly replaced the integrity of the content. The authorisation server issued the token, and the resource server honours the token because of where it originated, not because of anything it independently confirms about the request in front of it. The provenance of the credential stands in for the legitimacy of its use. A token exfiltrated from a compromised client, lifted from a log that recorded it, or replayed from an intercepted exchange presents identically to a token used by the party the grant intended. The resource server cannot distinguish between them, because the protocol never gave it that question to answer. Presence is the proof, and presence is all the resource server can observe.

This is why the attacker does not need to break anything. The observable behaviour of a stolen token in use and the observable behaviour of a legitimate token in use are the same behaviour. A refresh token presented at the token endpoint returns a fresh access token whether the presenter is the registered client or a process that came to hold the refresh token by other means. The token endpoint confirms the grant exists and has not been revoked, and it issues. The system is not deceived. It executes exactly the function RFC 6749 specifies, against exactly the reference it was built to honour. What reads as a bypass from outside is, from the system’s position, a correct resolution. There is no failed check, because there was no check to fail. The validation the situation would require was never part of the flow. The lookup that is part of the flow succeeds.

The pattern beneath OAuth 2.0 is execution based on reference rather than verification. A system resolves a costly question once, the question of whether an authority is legitimate, records the answer in a portable token, and thereafter honours the token in place of re-asking the question. The token is cheaper to check than the reality it stands for, and the architecture exists to make that substitution durable. Every system that issues a credential in order to avoid repeating an expensive verification inherits this shape. The credential is a reference to a decision, and the presentation of the reference is treated as equivalent to the decision itself.

The same mechanism operates in DNS resolution. When a recursive resolver answers a query, it does not, for the life of the record’s TTL, return to the authoritative nameserver to confirm the answer still holds. It serves the cached record. That record is a reference to an authoritative answer given at some earlier second, and within the TTL window the reference is honoured as though it were the present truth. The resolver is not verifying the mapping. It is confirming that a cached mapping exists and has not expired. Absence of expiry is read as continued validity, in the same way absence of revocation is read as continued validity at the OAuth token endpoint. A record that has gone stale or been poisoned within its TTL resolves with the same confidence as a fresh one, because the resolver is answering the only question it is built to answer: does a valid reference exist.

Two different protocols, RFC 6749 and the DNS caching model, produce one behaviour. Both convert an expensive act of verification into a cheap act of reference, and both then run on the reference for a defined window without revisiting the thing referenced. The window is where correspondence decays. The authority can change, the record can go stale, the client can be repurposed, and the reference holds its original shape regardless, because holding its shape is what a reference is for. The economy is the point. A system that re-verified at every use would be a system that had never delegated, and delegation is the feature, not the defect. The pattern is not a weakness bolted onto these systems. It is the mechanism by which they function at all.

OAuth 2.0 resolves the question of authority once, at the moment of consent, and it does not resolve it again. Every use after that is a lookup, not a judgement. The token is not the authority. It is the memory of an authority that was true when it was recorded.

A grant describes a relationship at an instant. The instant passes. The token does not. It carries the shape of a decision forward into conditions the decision never saw, and the resource server, honouring the token, honours the past as if it were the present.

This is not a defect in the implementation. It is the design behaving as specified. The system verifies once and trusts thereafter. The control exists. The outcome does not.

Share

Keep Reading

Stay in the loop

New writing delivered when it's ready. No schedule, no spam.