Not that an API couldn't support both API Keys and JWT based authentication, but one is a very established and well understood pattern and one is not. Lowest common denominator API designs are hard to shake.
Not that an API couldn't support both API Keys and JWT based authentication, but one is a very established and well understood pattern and one is not. Lowest common denominator API designs are hard to shake.
That's interesting - why do it this way rather than including a "reusable" signed JWT with the request, like an API token? Why sign the whole request? What does that give you?
Also what made that API so nice? Was this a significant part of it?
Supposedly bearer tokens should be ephemeral, which means either short-lived (say single-digit minutes) or one-time use.
This was supposed to be the way bearer tokens were supposed to be used.
> What does that give you?
Security.
The desirable properties for tokens is that they have some means of verifying their integrity, that they are being sent by the authorized party, and that they are being consumed by the authorized recipient.
A "reusable" bearer JWT with a particular audience satisfies all three - as long as the channel and the software are properly protected from inspection/exfiltration. Native clients are often considered properly protected (even when they open themselves up to supply chain attacks by throwing unaudited third party libraries in); browser clients are a little less trustworthy due to web extensions and poor adoption of technologies like CSP.
A proof of possession JWT (or auxiliary mechanisms like DPoP) will also satisfy all three properties - as long as your client won't let its private key be exfiltrated.
It is when you can't have all three properties that you start looking at other risk mitigations, such as making a credential one time use (e.g. first-use-wins) when you can't trust it won't be known to attackers once sent, or limiting validity times under the assumption that the process of getting a new token is more secure.
Generally an extremely short lifetime is part of one-time-use/first-use-wins, because that policy requires the target resource to be stateful. Persisting every token ever received would be costly and have a latency impact. Policy compliance is an issue as well - it is far easier to just allow those tokens to be used multiple times, and non-compliance will only be discovered through negative testing. Five minutes is a common value here, and some software will reject a lifetime of over an hour because of the cost of enforcing the single use policy.
I haven't seen recommendations for single-digit minute times for re-issuance of a multi-use bearer token though (such as for ongoing API access). Once you consider going below 10 minutes of validity there, you really want to reevaluate whatever your infrastructure requirements were that previously ruled out proof-of-possession (or whether your perceived level of risk-adversity is accurately represented in your budget)
Those are desirable properties.
But session hijacking is a known problem. You have to expect a scenario where an attacker fishes one of your tokens and uses it on your behalf to access your things.
To mitigate that attack vector, either you use single-user tokens or short-lived tokens.
Also, clients are already expected to go through authentication flows to request tokens with specific sets of claims and/or scopes to perform specific requests.
Single-use tokens were expected to be the happy flow of bearer token schemes such as JWTs. That's how you eliminate a series of attack vectors.
> Generally an extremely short lifetime is part of one-time-use/first-use-wins, because that policy requires the target resource to be stateful.
Not quite.
Single-use tokens are stateful because resource servers need to track a list of revoked tokens. But "stateful" only means that you have to periodically refresh a list of IDs.
Short-lived tokens are stateless. A JWT features "issued at" time, "not before" time, and "expiration" time. Each JWT already specifies the time window when resource servers should deem it valid.
> Persisting every token ever received would be costly and have a latency impact.
No need. As per the JWT's RFC, JWTs support the JWT ID property. You only need to store the JWT ID of a revoked token, not the whole token. Also, you only need to store it during the time window it's valid.
> Policy compliance is an issue as well - it is far easier to just allow those tokens to be used multiple times, and non-compliance will only be discovered through negative testing.
I think "easier" is the only argument, and it's mostly about laziness.
Authentication flows already support emitting both access tokens and refresh tokens, and generating new tokens is a matter of sending a request with a refresh token.
Ironically, the "easy" argument boils down arguing in favor of making it easy to pull session hijacking attacks. That's what developers do when they fish tokens from some source and send them around.
> I haven't seen recommendations for single-digit minute times for re-issuance of a multi-use bearer token though (such as for ongoing API access). Once you consider going below 10 minutes of validity there, you really want to reevaluate whatever your infrastructure requirements were that previously ruled out proof-of-possession (or whether your perceived level of risk-adversity is accurately represented in your budget)
This personal belief is not grounded in reality.
It's absurd to argue that clients having to do a request each 5 minutes is something that requires you to "reevaluate your infrastructure requirements". You're able to maintain infrastructure that handles all requests from clients, but you draw the line in sending a refresh request each minute or so?
It's also absurd to argue about proof-of-possession and other nonsense. The token validation process is the same: is the token signed? Can you confirm the signature is valid? Is the token expired/revoked? This is something your resource servers already do at each request. There is no extra requirements.
You're effectively talking about an attacker breaking https aren't you? Unless you can detail another way to get at a user's token. I'm curious to hear about it.
No. There are many ways to fish bearer tokens. Encryption in transit only addresses some of them.
Just Google for session hijacking attacks. There's a wealth of information on the topic. It's a regular entry in OWASP top 10.
OWASP's page lists 3 more examples which it seems you omitted for some reason.
50% of the support issues were because people could not properly sign requests and it caused me to learn how to make http in all sorts of crap to help support them.
Just generate the JWT using, e.g. https://github.com/mike-engel/jwt-cli ? It’s different, and a little harder the first time, but not any kind of ongoing burden.
You can even get Postman to generate them for you: https://learning.postman.com/docs/sending-requests/authoriza..., although I have not bothered with this personally.
Even the very foundational libraries needed to create/sign/handle JWTs in many programming languages are kind of clunky. And I think subconsciously as developers when we encounter clunky (ie high accidental complexity) libraries/apis we sense that the overall project is kind of amateurish, or will take some trial and error to set up properly. Sometimes that's no big deal, but with auth you can't afford to risk your company or product on someone's side project.
For example, in Go, there is really only one major jwt implementation in use [0] and it's the side project of some guy with a day job working on protobufs [1,2]. Also, with all due respect to the contributors because it's a good library considering the level of support it has, it is just not easy to use or get started with.
Part of the problem is also that the JWT specification [3,4] is a bad mix of overly prescriptive and permissive regarding "claims". I actually think it needs to be replaced with something better because it's a serious problem: it adds a bunch of unnecessary fluff to deal with special claims like "email_verified" when that use case could easily just be treated like any other application-specific jwt payload data, AND it then adds a bunch of complexity because almost everything is optional.
Then of course there's the giant problem of handling your own private keys and identity/security infrastructure + all the associated risks. Nothing mature makes that easy, so everybody naturally prefers to delegate it to auth providers. But that tends to make it hard to fully leverage the underlying jwts (eg with custom claims) and might force you into an authorization model/impl that's less flexible than what JWTs actually support, because now you have to use your auth provider's apis.
I think there really needs to be some kind of all-in-one library or tool for developers to operate their own hmac/jwks/authn/authz safely with good enough defaults that typical use cases require ~no configuration. And maybe either a jwtv2 or second spec that strips out the junk from the jwt spec and prescribes basic conventions for authorization. That's actually the only realistic path to fully leveraging jwt for identity and authz, because you couldn't build something like that on top of auth providers' APIs since they're too restricted/disparate (plus the providers are incentivized to sneakily lock you in to their APIs and identity/authz).
Anyway, this is a project I've been toying with for about a year now and we have some funding/time at my company to start tackling it as an open source project. Hit me up if you're interested.
[0] https://github.com/golang-jwt/jwt
[1] https://github.com/golang-jwt
[3] https://datatracker.ietf.org/doc/html/rfc7519#section-4.1