Live data from Hacker News

Everything I know about good API design

seangoedecke.com

81–90 of 168 posts

Re: Everything I know about good API design

#81
post #2

> 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.

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.

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

I never understood why.

Re: Everything I know about good API design

#82
post #62

Earlier quoted context omitted.

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.

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 th…

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.

Re: Everything I know about good API design

#83
post #18

Earlier quoted context omitted.

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.

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.

> 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.

Re: Everything I know about good API design

#84

Earlier quoted context omitted.

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.

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

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.

Re: Everything I know about good API design

#86

Earlier quoted context omitted.

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

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 bear…

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.

Re: Everything I know about good API design

#87

Earlier quoted context omitted.

From what I understand, statically linking in GNU's libc.a without releasing source code is a violation of LGPL. Which would break maybe 95% of companies out there running proprietary software on Linux. musl libc has a more permissive licence, but I hear it performs worse than GNU libc. One can hope for LLVM libc[1] so the entire toolchain would become Clang/LLVM, from the compiler driver to the C/C++ standard librar…

The Windows redistributables are so annoying as a user. I remember countless times applications used to ask me to visit the official Microsoft page for downloading them, and it was quite hard to find the right buttons to press to get the thing. Felt like offloading the burden to the users.

Many installers do it right and don't require the user to do it themselves.

Re: Everything I know about good API design

#88

The reminder to "never break userspace" is good, but people never bring up the other half of that statement: "we can and will break kernel APIs without warning". It illustrates that the reminder isn't "never change an API in a way that breaks someone", it's the more nuanced "declare what's stable, and never break those".

Even if the kernel doesn't break userspace, GNU libc does, all the time , so the net effect is that Linux userspace is broken regardless of the kernel maintainers' efforts. Put simply, programs and libraries compiled on/for newer libc are ABI-incompatible or straight-up do not run on older libc, so everything needs to be upgraded in lockstep. It is a bit ironic and a little funny that Windows solved this problem a co…

You're describing 2 completely different things there.

If your program is built to require myfavoritelibrary version 1.9, and you try to run it against myfavoritelibrary 1.0, no shit it doesn't work. Glibc is no different than any other in this regard.

If your program is built to require myfavoritelibrary version 1.0, and you try to run it on myfavoritelibrary 1.9 ... glibc's binary compatibility story has been very good since the release of 2.2 or so, way back in 2000. (I know from documentation that there were a lot of 2.0 -> 2.1 breakages, some of which might've actually been fixed in 2.1.x point releases, so I'm saying 2.2 to be safe)

It's not quite as perfect as Linux's "we do not break userland" but it's pretty darn close; I would have to hunt down changelogs to find something that actually broke without explicitly relying on "do not rely on this" APIs. Source compatibility is a different story, since deprecated APIs can be removed from the public headers but still present in the binary.

... actually, even Linux has unapologetically broken its promise pretty badly in the past at various times. The 2.4 to 2.6 transition in particular was nasty. I'm also aware of at least one common syscall that broke in a very nasty way in some early versions; you can't just use ENOSYS to detect it but have to set up extra registers in a particular way to induce failure for incompatible versions (but only on some architectures; good luck with your testing!)

---

There's nothing stopping you from installing and using the latest glibc and libgcc at runtime, though you'll have to work around your distro's happy path. Just be careful if you're building against them since you probably don't want to add extra dependencies for everything you build.

By contrast, I have statically-linked binaries from ~2006 that simply do not work anymore, because something in the filesystem has changed and their version of libc can't be fixed the way the dynamically-linked version has.

Re: Everything I know about good API design

#89

Earlier quoted context omitted.

I believe it’s pretty common to e.g. call libraries’ and frameworks’ user- (developer-) facing interface an API, like in “Python’s logging library has a weird-looking API”, so I don’t think API had eroded to mean only networked ones.

I never understood why libraries also had the word API. From my understanding a library is a set of functions specific to a certain domain, such as a statistics library, for example. Then why would you need the word API? You already know it’s a library. For end points it’s a bit different. You don’t know what are they or user facing or programmer facing. I wonder if someone has a good take on this. I’m curious to lea…

The API of a library is what a recipe is to food.

Re: Everything I know about good API design

#90
post #52
post #38

Earlier quoted context omitted.

It's a problem downstream. Integrators weren't forced to include a version number for v1, so the rework overhead to use v2 will be higher than if it was present in your scheme to begin.

This here, it's way easier to grep a file for /v1/ and show all the api endpoints then ensure you haven't missed something.

grep for /* and omit /v2 ?
Post reply on HN