Remix.run Logo
cyberax 3 days ago

> You should let people use your APIs with a long-lived API key.

Sigh... I wish this were not true. It's a shame that no alternatives have emerged so far.

TrueDuality 3 days ago | parent | next [-]

There are other options that allow long-lived access with naturally rotating keys without OAuth and only a tiny amount of complexity increase that can be managed by a bash script. The refresh token/bearer token combo is pretty powerful and has MUCH stronger security properties than a bare API key.

maxwellg 3 days ago | parent | next [-]

Refresh tokens are only really required if a client is accessing an API on behalf of a user. The refresh token tracks the specific user grant, and there needs to be one refresh token per user of the client.

If a client is accessing an API on behalf of itself (which is a more natural fit for an API Key replacement) then we can use client_credentials with either client secret authentication or JWT bearer authentication instead.

TrueDuality 3 days ago | parent [-]

That is a very specific form of refresh token but not the only model. You can just easily have your "API key" be that refresh token. You submit it to an authentication endpoint, get back a new refresh token and a bearer token, and invalidate the previous bearer token if it was still valid. The bearer token will naturally expire and if you're still using it, just use the refresh immediately, if its days or weeks later you can use it then.

There doesn't need to be any OIDC or third party involved to get all the benefits of them. The keys can't be used by multiple simultaneous clients, they naturally expire and rotate over time, and you can easily audit their use (primarily due to the last two principles).

rahkiin 3 days ago | parent | prev | next [-]

If api keys do not need to ve stateless, every api key can become a refresh token with a full permission and validity lookup.

marcosdumay 3 days ago | parent [-]

This.

The separation of a refresh cycle is an optimization done for scale. You don't need to do it if you don't need the scale. (And you need a really huge scale to hit that need.)

0x1ceb00da 3 days ago | parent | prev [-]

> The refresh token/bearer token combo is pretty powerful and has MUCH stronger security properties than a bare API key

I never understood why.

TrueDuality 3 days ago | parent [-]

The quick rundown of refresh token I'm referring to is:

1. Generate your initial refresh token for the user just like you would a random API key. You really don't need to use a JWT, but you could.

2. The client sends the refresh token to an authentication endpoint. This endpoint validates the token, expires the refresh token and any prior bearer tokens issued to it. The client gets back a new refresh token and a bearer token with an expiration window (lets call it five minutes).

3. The client uses the bearer token for all requests to your API until it expires

4. If the client wants to continue using the API, go back to step 2.

The benefits of that minimal version:

Client restriction and user behavior steering. With the bearer tokens expiring quickly, and refresh tokens being one-time use it is infeasible to share a single credential between multiple clients. With easy provisioning, this will get users to generate one credential per client.

Breach containment and blast radius reduction. If your bearer tokens leak (logs being a surprisingly high source for these), they automatically expire when left in backups or deep in the objects of your git repo. If a bearer token is compromised, it's only valid for your expiration window. If a refresh token is compromised and used, the legitimate client will be knocked offline increasing the likelihood of detection. This property also allows you to know if a leaked refresh token was used at all before it was revoked.

Audit and monitoring opportunities. Every refresh creates a logging checkpoint where you can track usage patterns, detect anomalies, and enforce policy changes. This gives you natural rate limiting and abuse detection points.

Most security frameworks (SOC 2, ISO 27001, etc.) prefer time-limited credentials as a basic security control.

Add an expiration time to refresh tokens to naturally clean up access from broken or no longer used clients. Example: Daily backup script. Refresh token's expiration window is 90 days. The backups would have to not run for 90 days before the token was an issue. If it was still needed the effort is low, just provision a new API key. After 90 days of failure you either already needed to perform maintenance on your backup system or you moved to something else without revoking the access keys.

0x1ceb00da 3 days ago | parent [-]

So a refresh token on its own isn't more secure than a simple api key. You need a lot of plumbing and abuse detection analytics around it as well.

TrueDuality 3 days ago | parent [-]

Almost every one of those benefits _doesn't_ require anything else. You need one more API endpoint to exchange refresh tokens for bearer token (over a simple static API key) and you get those benefits.

pixelatedindex 3 days ago | parent | prev [-]

To add on, are they talking about access tokens or refresh tokens? It can’t be just one token, because then when it expires you have to update it manually from a portal or go through the same auth process, neither of which is good.

And what time frame is “long-lived”? IME access tokens almost always have a lifetime of one week and refresh tokens anywhere from 6 months to a year.

cyberax 3 days ago | parent | next [-]

If you're using APIs from third parties, the most typical authentication method is a static key that you stick in the "Authorization" HTTP header.

OAuth flows are not at all common for server-to-server communications.

In my perfect world, I would replace API keys with certificates and use mutual TLS for authentication.

zzo38computer 3 days ago | parent | next [-]

I would also use mutual TLS; in addition to improved security (on both sides), it also allows for many additional possibilities, such as partial delegation of authorization, etc. (If not everyone wants it, it can be made an option rather than mandatory.)

pixelatedindex 3 days ago | parent | prev | next [-]

IME, OAuth flows are pretty common in S2S communication. Usually these tend to be client credential based flows where you request a token exactly like you said (static key in Authorization), rather than authorized grant flows which requires a login action.

cyberax 3 days ago | parent [-]

Yeah, but then there's not that much difference, is there? You can technically move the generation of the access tokens to a separate secure environment, but this drastically increases the complexity and introduces a lot of interesting failure scenarios.

pixelatedindex 3 days ago | parent [-]

I mean… is adding an OAuth layer in 2025 adding that much complexity? If you’re scripting then there’s usually some package native to the language, if you’re using postman you’ll need to generate your authn URL (or do username/passwords for client ID/secret).

If you have sensitive resources they’ll be blocked behind some authz anyway. An exception I’ve seen is access to a sandbox env, those are easily generated at the press of a button.

cyberax 3 days ago | parent [-]

No, I'm just saying that an OAuth layer isn't really adding much benefit when you either use an API key to obtain the refresh token or the refresh token itself becomes a long-term secret, not much better than an API key.

Some way to break out of the "shared secret" model is needed. Mutual TLS is one way that is at least getting some traction.

JambalayaJimbo 2 days ago | parent [-]

Refresh tokens aren’t necessarily long lived, you can force the client to exchange for another refresh token.

nostrebored 3 days ago | parent | prev [-]

In your perfect world, are you primarily the producer or consumer of the API?

I hate mTLS APIs because they often mean I need to change how my services are bundled and deployed. But to your point, if everything were mTLS I wouldn’t care.

cyberax 3 days ago | parent [-]

> In your perfect world, are you primarily the producer or consumer of the API?

Both, really. mTLS deployment is the sticking point, but it's slowly getting better. AWS load balancers now support it, they terminate the TLS connection, validate the certificate, and stick it into an HTTP header. Google Cloud Platform and CloudFlare also support it.

smj-edison 3 days ago | parent | prev | next [-]

> Every integration with your API begins life as a simple script, and using an API key is the easiest way to get a simple script working. You want to make it as easy as possible for engineers to get started.

> ...You’re building it for a very wide cross-section of people, many of whom are not comfortable writing or reading code. If your API requires users to do anything difficult - like performing an OAuth handshake - many of those users will struggle.

Sounds like they're talking about onboarding specifically. I actually really like this idea, because I've certainly had my fair share of difficulty just trying to get the dang thing to work.

Security wise perhaps not the best, but mitigations like staging only or rate limiting seem sufficient to me.

pixelatedindex 3 days ago | parent [-]

True, I have enjoyed using integrations where you can generate a token from the portal for your app to make the requests. One thing that’s difficult in this scenario is authorization - what resources does this token have access to can be kind of murky.

rahkiin 3 days ago | parent | prev [-]

Inthink they are talking about refresh token or Api Keys like PATs. Some value you pass in a header and it just works. No token flow. And the key is valid for months and can be revoked