I attempt to describe what little I know about authN/authZ and what the future should probably look like.
I know less about these specs than I should, but I have seen the fires created from people (myself included) attempting to implement them.
OAuth2 is a disaster of a specification, and not only because it’s actually a collection of specifications. Here’s how I’ve seen it go wrong.
OAuth2 often (not always) relies on TLS to protect tokens.
By delegating security to the wire instead of the data, OAuth2 relies on the ancient concept of hardened outside and soft center. OAuth2 access tokens are, by design, supposed to allow for one app acting on behalf of another. It’s up to application developers to secure these tokens from any other things that may have shared access to other parts of their application.
OAuth2 says “here’s how to get a token and where you should send it,” but neglects to adequately define how to protect this token.
On the token consumer side, there’s a general list of stuff you should probably look at (token expiry, signatures, etc.), but how are these tokens stored? Very luckily we have RFC6750 which tells us how bearer tokens are used.
It says nothing about their actual format, though the one example it gives returns data as JSON. It takes RFC7519 (accepted years later) to define a JSON web token (JWT).
EIGHT YEARS after RFC6750 was written, we have a DRAFT of how to use JWTs in OAuth2.
Excuse me? Doesn’t any worthwhile OAuth2 implementation use JWTs? What have we been doing this whole time?
Turns out: whatever the heck we wanted!
RFC7519 defines the JWT format and a minimum set of claims that should be in each token. It also stops short of differentiating between an access token and an ID token. Implementers can add any claims want, which is how we ended up with these proprietary, incompatible, customizeable JWT implementations.
Given all of the above, OAuth2 has taken years to figure out best practices, and in general is tricky for developers to get right.
What if I told you OAuth2 is so large in scope it doesn’t just have a section on security threats, it has RFC6819, which is four pages long?
I’m kidding, of course. Four pages only covers the table of contents.
To patch OAuth2 (or at least OAuth2-like token exchange) into something more secure or useable I have seen the following attempts:
Encrypted JWTs: Encryption protects token contents, but JWTs are meant to be signed anyway. If an attacker reads a normal, unencrypted JWT and reads what’s inside, the attacker shouldn’t be able to modify any data that the server would then accept. Encrypted tokens tempt developers to put sensitive data (say, user emails) in the token itself, which is an antipattern (GNAP/OAuth3 prefers an ID for servers to look up instead). Sniffed tokens are still susceptible to decryption, compromised encryption keys require quick key revocation, and keys should be rotated periodically. Coordinating that system is its own endeavor. Lastly, this does nothing to prevent replay attacks by resending a sniffed encrypted token anyway.
jti claim (nonce): This is actually defined as an optional parameter in RFC7519. A nonce means the token consumer should only accept this token once, mitigating replay attacks, however enforcing nonces are quite difficult at scale.
Compression: Token bloat occurs when too many scopes (or other growing pieces of information) are enclosed in a single token. Since tokens are typically sent in HTTP headers (when not being sent in the URI as RFC6750 allows (!!!)), which often have size limits, this is a way of expanding those limits, but ultimately staves off the real issue: token claims shouldn’t have to grow this big.
AuthN as AuthZ: Together with token bloat, we’re sort of coming full circle on separating AutheNtication from AuthoriZation. Once upon a time, we realized that proving who you are is different than proving what you have access to in a given context. I have seen token types that conflate these two concepts: suppose a user is part of a group that has access to a resource. An access token verifier may only care about access claims (scopes), but for tracing purposes we may want to also reference the original group or user that requested this access token. By including ID data, we now have elements of an ID token inside an access token–though it should not be enough to obtain additional tokens. Zero Trust blurs the AuthN/AuthZ line by further asserting identity before allowing access.
In the next part, I’ll summarize what patterns I’ve seen from next-gen auth attempts.
In the next next (last?) part, I’ll summarize what I think should come next for authorization.