Making Beautiful API Keys
58 points by savannahkc 6 hours ago | 94 comments
  • theamk 6 hours ago |
    One of the best things you can do to your API key is to give it a fixed prefix. Makes it very easy to tell that you have the right string, to detect accidental secret leakage, etc...

    IMHO this makes key much more beautiful than any internal structure.

    • adriancooney 6 hours ago |
      Agreed. I first seen it at Stripe (along with prefixing every ID). Whoever at Stripe (or where ever it was invented) needs a good pat on that back. It's adoption has been a huge for DX generally.
    • notpushkin 6 hours ago |
      I’ve made a library for that! https://codeberg.org/prettyid
      • dewey 6 hours ago |
        Why should anyone vendor a dependency in a critical functionality for three lines of code (https://codeberg.org/prettyid/js/src/branch/master/lib/index...)?
        • HideousKojima 6 hours ago |
          That's the JavaScript/Node ecosystem in a nutshell. See the LeftPad fiasco and the mere existence of the IsOdd and IsEven packages for more poor judgement.
        • notpushkin 6 hours ago |
          To be honest that’s because I didn’t flesh out JS counterpart yet. My plan was to have something similar to the Python version: https://codeberg.org/prettyid/python

          Of course, if you like the idea but don’t want to add another dependency, you can copy-paste the three-liner as it is! (And I’m sure it’s not even copyrightable right now :-)

        • fusspawn 6 hours ago |
          multiple dependencies,

          Also requires two other packages to run its 3 lines of code.

          "dependencies": { "@scure/base": "^1.1.7", "uuidv7": "^1.0.1" }

          • kqr 5 hours ago |
            ...but you'll have those dependencies anyway!
        • verdverm 4 hours ago |
          only if I can leftpad it first?
      • xdennis 5 hours ago |
        There's a similar proposal called TypeID: https://github.com/jetify-com/typeid

        E.g.: user_2x4y6z8a0b1c2d3e4f5g6h7j8k

    • jjice 6 hours ago |
      I'm a big fan of prefixed keys as well. If I could go back in time, I would've made my current company's API keys prefixed. Sometimes you'll chat with a customer and just notice the same isn't right and realize the key is all wrong. It's a lot easier if it starts with `company_XXXXX`. Also denoting environment (live vs test in Stripe) with a prefix is actually critical in my eyes.

      I also like prefixed resource IDs. Stripe is the first one that comes to mind, but I've run into it multiple times where a customer is describing an issue and it turns out the ID they're trying to lookup is for a different resource (often similar). You don't get those accumulated hours of support time back...

    • moebrowne 6 hours ago |
      Agreed. I like what GitHub did with their API tokens, not only adding prefixes but also a checksum.

      https://github.blog/engineering/platform-security/behind-git...

    • dspillett 6 hours ago |
      I would agree with that.

      Also, I don't really want API keys and such to be generally pretty. If things that have no business being end-user facing are ugly then they are less likely to be allowed to accidentally become end-user facing.

      If being pretty or otherwise user-friendly is a priority, then I'd go with trying to make them readable/pronounceable rather than shorter, even if that actually makes them longer. There are numerous projects out there¹²³ for doing just that. You could even use the 256-word example³ with multiple small dictionaries, and give people a choice from various possibilities that map to the same number.

      ----

      [1] https://github.com/Debdut/uuid-readable

      [2] https://github.com/Martichou/uuid-readable-rs

      [3] https://github.com/anton-bot/guid-in-words

    • yread 5 hours ago |
      I like adding a suffix as well: (optional) human readable expiration date
    • progforlyfe 5 hours ago |
      actual functional features in API keys! How dare you! aesthetics over all!!
  • mtmail 6 hours ago |
    "encodes UUIDs to a readable Key format via the Base32-Crockford codec and also decodes them back to UUIDs."

    Example: "d1756360-5da0-40df-9926-a76abff5601d" => "38QARV0-1ET0G6Z-2CJD9VA-2ZZAR0X"

    I think now you risk having 0 vs O or I vs 1 readability issues. [edit: good news, I was wrong]

    • notpushkin 6 hours ago |
      Base32-Crockford doesn’t have 0 and 1 for this exact reason!

      (Seems uuidkey authors have decided to remove O and I instead, but the effect is the same)

      EDIT: I’ve looked it up and I was wrong! Crockford alphabet does use all digits (0–9), but doesn’t have O, I or L. When decoding, O is mapped back to 0 and both I and L are mapped to 1. Sorry for the confusion!

      • copperlight 6 hours ago |
        https://www.crockford.com/base32.html

        > We chose a symbol set of 10 digits and 22 letters. We exclude 4 of the 26 letters: I L O U.

        I had never heard of Base32 Crockford before. The whole rationale is clever.

      • mrweasel 5 hours ago |
        Given that API keys are likely to simply be copy-pasted, I don't honestly think this matters all that much.

        If you're have the risk to users confusing 0 and O, then you can't use either. Your users aren't going to know that you're running Base32-Crockford and that they'll only encounter 0 and 1, never O, I or L.

        We did a "password" generator, for people who made a purchase, but didn't want an account. To view an order they'd then need to enter a code, found in their confirmation email. Those codes where really short, 8 or 10 characters, no 0,1,I,O,L,U,V and all upper case. If the user entered the code in lower case, we'd automatically upper case it. You'd never use these as a real password, but for a temporary order tracking page they pretty much removed all of the input mistakes people could make.

    • zdc1 6 hours ago |
      I feel like they really missed the mark with "Beautiful" as their priority. If I were to make a UUID to API key encoding/serde library I'd definitely prioritise things like human readable characters / phrases (e.g. BIP-39 style with hyphens), and as jjice said, a fixed prefix.

      Edit: okay, good to know were at least covered for 1 vs I issues.

  • PaulHoule 6 hours ago |
    I invented (more or less) Crockford Base32 back in 2001 which I called "Base32t" (t for Tapir, because it was part of the "Tapir User Management System") for encoding password reset tokens and such. (Minus those squicky check symbols... Right out of the mind that left us the seductive but slightly flawed JSON [1])

    I used T.U.M. for a number of sites including one that was in the Alexa top 2000, even though it was open source it got no pickup from anyone else. The standard at the time was to pick up some software like PHPNuke which did a lot of things badly as opposed to my Yahoo-inspired approach of "pick the best of breed software and plug them into a common user management system".

    The idea didn't get any traction until 2013 when things like this popped up like mushrooms. Seemed the missing features were "vendor lock-in", "somebody else owns your user database", "they might shut down, get bought by Google or kick you out of the free tier."

    [1] I've seen it enough that I'd expect higher uptake if you inject small flaws into a specification like that.

  • anticorporate 6 hours ago |
    I love the throwback reference to the Diablo II CD key. There are some CD keys that will be forever etched in my brain, no matter how many PINs I struggle to remember. I suspect a good number of you know far too much of a certain string that starts with FCKGW.
    • tommica 6 hours ago |
      • kqr 6 hours ago |
      • mossTechnician 5 hours ago |
        This topic looked interesting, so I hunted down a better frontend for that full Medium article[0]. The writing is unsatisfying, doesn't really say anything, and by the last paragraph ("The tale of the product key... is a continuing demonstration of the never-ending struggle that takes place between the technological sector and the unyielding world of hackers") I was convinced the author was just posting a ChatGPT response. Which is sad, because I'd have liked to see the backstory for something apparently shrouded in mystery. But since there appears to be little explanation to how that key was leaked before Windows itself went on sale, I'll just enjoy the memory of the cultural phenomenon[1][2].

        [0] https://freedium.cfd/https://medium.com/@cristian.nedelcu/fc...

        [1] https://news.ycombinator.com/item?id=19298196

        [2] https://marco.org/2007/06/18/wow-fckgw-has-its-own-wikipedia...

    • mrweasel 6 hours ago |
      That reference to CD keys is nice and when I look at their example vs. the Diablo II CD key I think they fall a bit short of creating something the looks nice. It's simply down to size, the example key they generate is just to long, to the point where I don't see the value.

      It's a cute idea, but I really don't like the extra level of indirection, especially as I feel like there's nothing gained. The base32 encoded key is no more beautiful that the uuid7. I think it's to easy for someone to look at this and go uuid7().upper() and assume that's the same thing, if they just look at the key.

  • voidUpdate 6 hours ago |
    It may just be personal preference but I think I'd find it more beautiful if there were 5 characters per block, instead of 7. It has a better rhythm to say it in my head
  • robertclaus 6 hours ago |
    This type of key editing always makes me nervous. I know how uuids behave. I'm not a security expert, but I'm 99% sure the formatting steps here don't increase the chance of key collisions or security implications significantly. Is that 1% risk worth it?
    • ramchip 6 hours ago |
      There's a 1:1 mapping (a bijection) between uuid and uuidkeys, therefore there are as many possible uuidkeys as there are uuids, and the risk of collision is the same.
    • jalk 6 hours ago |
      I'm 99.9999% sure that your 1% risk is incorrect, given they are just reformatting UUIDv7
    • Merad 5 hours ago |
      Base 32 is just an encoding, so it doesn't modify the underlying data of the uuid, it presents the data in a different display format. It's the same as how the number 10 can be represented as decimal 10, binary 0b1010, octal 0o12, hex 0xA, etc.
    • jaccola 5 hours ago |
      I agree - the reformatted IDs are shorter than the originals, so by the pigeonhole principle you are increasing the chance of collision.

      I doubt this matters in reality for this case, but the number of comments stating "there is no difference" or something to this effect shows how any added step can easily be misunderstood and could (in the worst case) introduce a fatal security flaw.

  • lovasoa 6 hours ago |
    Dashes in API keys are really annoying. Double-clicking doesn’t select the full key, which just adds extra hassle. It would be much better if they used a continuous string without any separators. Makes copying and pasting way easier, and doesn't affect security at all.
    • noman-land 6 hours ago |
      Double click and drag is your friend.
      • OptionOfT 6 hours ago |
        Side note: this seems to work differently on Mac. I had to wait a little bit before the drag function would drag and not change the selection.
      • verdverm 4 hours ago |
        you're my hero today
    • boredumb 6 hours ago |
      I really don't care about the aesthetics of a string but this is 100% my issue when interacting with UUIDs.
    • majkinetor 6 hours ago |
      or make it optional and/or allow other symbols such as _
    • xirdstl 6 hours ago |
      They realized that and were pleased with themselves.

      > The dashes do remove easy double-click copying, but we think this a fine trade off for readability. We don't want users copying and pasting them everywhere, in fact we want them to be handled with care. Ideally, users copy each key exactly once - when they generate the key from our dashboard - so we added a copy button to our UI to solve that case

      Don't even think about copying/pasting that key, you rube!

      • alt227 5 hours ago |
        This statement makes me never want to use their product.
        • inexcf 5 hours ago |
          I agree. They seem to focus on completely useless stuff that actively makes it worse to use.
          • alt227 5 hours ago |
            Not only that, they are making the biggest mistake a company can do by telling their customers that what they are doing is wrong and that the company knows better than they do.
    • dietr1ch 6 hours ago |
      This is the only annoyance with UUIDs, I won't be typing it because its 5 characters shorter. Separators are nice because I can try to visually compare Ids, but it needs to be a visual separator that won't break a `word`/`WORD` (whatever boundary that is by default on most terminals and browsers) select by double clicking.

      I want to just double click and copy, dragging is annoying.

    • kennu 2 hours ago |
      Whenever I have created API keys for a product, this is the #1 feature I want: easy copy-pasteability. Just letters and numbers, no special characters that break double-click-selecting the key.
  • error404x 6 hours ago |
    This is so amazing, thanks for sharing!

    I usually create an API key like this: `sk_${randomUUID().replace(/-/g, '')}`.

  • philo23 6 hours ago |
    A bit off topic, but on the copying issue with dashes, you can wrap the whole key in a span styled with “user-select: all” to improve the copying experience. Well, on the web at least!
  • stitched2gethr 6 hours ago |
    I guess this is an unpopular opinion given the existing comments, but I don't get it. They spent time and energy on this instead of writing features or fixing bugs and I don't think it matters at all. The only interaction I have with my API keys is copying them from one place to another. If I need to manually identify a key then putting the first or last few chars, whatever they are, in my working memory is more than sufficient.
    • a12k 6 hours ago |
      I totally agree. This looks like a pretty new startup too. I would be livid if my team had spent so much time on this instead of building capabilities to get the startup traction.
      • alt227 6 hours ago |
        It looks like they wanted a reason to make a shiny blog post to get attention for their startup. Judging by the fact its on the front page of HN and we are all commenting on it, looks like they succeeded.
      • hombre_fatal 6 hours ago |
        Yall are so dramatic.

        It's a trivial library to write. 99% of it is just deciding what you want the output to look like + writing the blog post.

        • great_wubwub 5 hours ago |
          Yeah, the code is easy but navel-gazing and agonizing over the prettiest way to replace a long jumble of dash-separated lowercase characters with a slightly shorter jumble of dash-separated uppercase characters is an insane thing to spend any time on at all. Convince me this has any commercial value and isn't just bikeshedding.
          • hombre_fatal 5 hours ago |
            This is just what it looks like to care about the details, though certainly a more trivial case than most.

            The resistance to it is part of why most engineer-types like HNers can't build UX. They think it's all agonizing and navel-gazing, so they don't deliberate over anything. And they don't practice it, so when they see someone making deliberate UX decisions, even trivial ones, they think it must have taken a lot of time.

            Good UX comes from a chain of trivial-looking decisions in isolation and a culture of caring about it.

            • alt227 5 hours ago |
              > This is just what it looks like to care about the details

              This isnt caring about the details, this is going over the top on something completely unneccessary just to get attention for it.

        • xdennis 5 hours ago |
          Not trying to be mean, but the fact that they spend that much time on ids is an indication that the team could have misplaced priorities in other regards as well.
    • alt227 6 hours ago |
      Agreed. I cant see why anyone thinks this was worth spending time on rather than just using the standards which everybody else does.

      Nobody types out api keys so there is no need to make them friendly to say or remember. After you have copied and pasted them once into your db, you are never going to seee or use that string again!

      • wink 5 hours ago |
        Totally worth spending a single day at some hackathon or slack time event... but not more. And it looks like a lot more.
        • thiht 5 hours ago |
          Does it? We once discussed making our ids (UUIDs) prettier, came to the same conclusion of using some base64 variant in 5 minutes, and came across base32 10 minutes later, and the final implementation is pretty trivial.

          This is a 1 day job at most to get something polished (assuming there’s no migration/retrocompatibility constraints)

    • shortrounddev2 6 hours ago |
      idk why but it's something product people obsess over. I added a table in the database for our platform that used autoincrementing IDs and my boss (the CEO) sent it back to me asking to use nanoids simply because they look nicer
    • felideon 5 hours ago |
      Their customers are developers, their product is a dev tool. No different than any other company obsessing over great UX. Stripe won because of the developer experience.
      • alt227 5 hours ago |
        Tangent, but same with Quickbooks Online. Seems most accountants and bookkeepers I have spoken to hate it, however it has a really useable api and simple callback system which makes it super attractive to developers.

        No surprise then that it is the worlds most used accounting software, even though most of its users hate it.

    • Retr0id 5 hours ago |
      All things in moderation. I think they're correct to classify their API keys as part of their user interface. It's less like deciding the colour of the bike shed, and more like deciding whether the metal sides of the new iPhone should have a brushed or polished finish. Sure, it doesn't affect anything practical, but it may well influence how the user feels about the product. Maybe you deny having feelings about APIs but I think you'd be lying ;)

      All that said, I'm not sure the juice was worth the squeeze here - I don't think their new API keys look any more beautiful than a standard UUID. I like that they cared all the same, though.

    • fbuitron 5 hours ago |
      Sometimes someone caring about details is what makes everything better.

      Nobody is going to choose their product just because their API Keys.

      But they are generating a halo effect. Is like going to a restaurant with outstanding bathrooms. If they put a lot of care in that, you immediately assume they do the same in all the other aspects of their product/service.

      • alt227 5 hours ago |
        IMO formatting an API key string is not equal to an outstanding bathroom. Its more akin to a valve you put under the floor of the bathroom. You put it there once to make things work and you will never see it again.

        Will it make you feel better to know that you have the shinier valve which looks prettier? Maybe, but nobody else will ever know and it will work just the same as the valve everyone else has in their bathroom which cost 10% of the price.

    • dewey 5 hours ago |
      Sometimes spending engineering time for content marketing is worth more than implementing some feature that's not needed for the early adopters.
      • verdverm 4 hours ago |
        content marketing can demonstrate orgs you don't want to work for and can therein be counterproductive
    • ripped_britches 5 hours ago |
      I’d say, besides writing this blog post, it probably took someone about a couple of hours to look up the available options and come to a decision. If that’s true, well worth the extra thought for their customers. It takes lots of painstaking focus like that to make a great product. But yes, if they spent weeks on this it would be too much.
    • kimi 5 hours ago |
      Agreed. I'll copy and paste it anyway. It's not that instead of an UUID you have "FluffCat47" that is easy to type and remember, it's a freaking UUID as well (ok, 31 characters vs 36 - saves a huuge 15% of your fingertips...)
    • rw_panic0_0 5 hours ago |
      yeah it's an unpopular opinion
  • progbits 6 hours ago |
    This is missing a checksum which helps secret scanning avoid false positives in other high entropy strings.

    See for example: https://docs.github.com/en/code-security/secret-scanning/sec...

  • a12k 6 hours ago |
    This is the very definition of bikeshedding. It should be included as a reference in any definition of that term.
    • bkummel 5 hours ago |
      +1
  • remram 6 hours ago |
    I use URLs as API keys, so they are self-descriptive (links to a page that tells you what it is/what service it's for) and self-revocable (there's a button, no need to post it to a GitHub repo to have them revoke it for you with their secret scanner [1]).

    I bring this up a lot [2] but I do think there is value in being able to tell if something is a secret and tell where to go to revoke it if found. Most current API keys use some sort of prefix at least (AWS, SendGrid, GitHub, etc).

    [1]: https://docs.github.com/en/code-security/secret-scanning/int...

    [2]: https://news.ycombinator.com/item?id=28296864

  • fusspawn 6 hours ago |
    I really dont see the benefit of these. its just guids with extra steps. its not any easier to read than guids.

    its an alphanumeric random string in both systems.

    yes theres is kinda symetrical. but im not going to find it easier to communicate/remember say:

    38QARV0-1ET0G6Z-2CJD9VA-2ZZAR0X

    any easier than i am

    d1756360-5da0-40df-9926-a76abff5601d

    both are long random strings. both are awkward to have to read over say a phone call.

    what am I missing here?

  • gioazzi 6 hours ago |
    I love using Crockford's Base32 to encode "invite codes" or any ID that you may want to share/write down/spell out: don't have to worry about mixing up O-s and zeroes - or care about casing.

    I try to make it obvious in UI by automatically converting to uppercase, replacing O->0 etc, and adding a dash in the middle.

  • smashed 6 hours ago |
    I don't quite understand the need for a timestamp. This only reduces entropy? You wouldn't think of using the current date in a password prefix for example.

    Aren't you going to track the keys in a database, where you can keep the tenant id and creation time, scope of the key and any other significant metadata anyway?

    A static prefix + checksum, maybe a version number so you can future-proof the system sounds like best practice. For example `ASKEY1-(128bit random base32 encoded)-(chksum)`.

  • tzury 6 hours ago |
    API keys and UUIDs serve fundamentally different purposes, even though they may look similar as random strings:

    1. API keys are security credentials: - They are meant to be secret and revocable - They often encode metadata about permissions and identity - Compromised API keys must be invalidated and replaced - They function like passwords for authentication/authorization

    2. UUIDs are identifiers: - They are designed to be globally unique but not secret - They contain no inherent permissions or privileges - There's no security risk if others know a UUID - They function like serial numbers for identification

    To use an analogy: An API key is like the key to your house (needs to be kept secret, grants access, can be changed if compromised), while a UUID is like your house's street address (can be public, just identifies the location, doesn't grant any access by itself).

    Thinking they're equivalent is like saying your house key and address are the same thing just because they're both strings of characters. This misconception could lead to serious security vulnerabilities if API keys are treated with the same casualness as UUIDs.

    PS, we all liked this site, right? https://everyuuid.com/

  • bArray 6 hours ago |
    > The dashes do remove easy double-click copying, but we think this a fine trade off for readability. We don't want users copying and pasting them everywhere, in fact we want them to be handled with care. Ideally, users copy each key exactly once - when they generate the key from our dashboard - so we added a copy button to our UI to solve that case.

    Or, just ignore the dashes? It's not that difficult, we have the technology.

  • pksunkara 5 hours ago |
    This ulid Postgres extension doesn't have any performance issues compared to native uuidv7 in Postgres. https://github.com/pksunkara/pgx_ulid. Just wanted to bring it up since it looks like the author didn't come across it when looking for ulid generators in postgres.

    Disclaimer: I built it.

  • benterix 5 hours ago |
    The author mentions readability - if they care about it, they should eliminate all ambiguous characters like 0Oo1l.
    • moebrowne 5 hours ago |
      They use Crockford Base32 encoding which does exactly that.
      • benterix 5 hours ago |
        I don't think so since the sample key they present in the big image contains many of these. The caption is "An example API key generated by github.com/gofrs/uuid and encoded with github.com/agentstation/uuidkey." - so something is wrong.
        • alt227 5 hours ago |
          You need to look harder at the encoding standard. After generation all o are mapped to 0 and all l are mapped to 1 so yes there are 0 and 1 but there are never o and l.
          • hdjjhhvvhga 4 hours ago |
            Ah, so you need to know that first, otherwise it's still ambiguous to people reading them.
    • stby 5 hours ago |
      Base 32 does that for them, it excludes the lower case letters and I L O U.
  • cynicalsecurity 5 hours ago |
    This is an absolute useless feature. It's actually even dangerous, because the "beauty" of API keys makes absolutely no sense. Only security matters.
    • rw_panic0_0 4 hours ago |
      bro your username speaks for itself lol
  • balls187 5 hours ago |
    This reminds me of every time I heard a complaint that a url is “ugly”
  • skerit 5 hours ago |
    What's up with these comments? Let these folks format their keys the way they want.
  • alt227 5 hours ago |
    > Overall, developers like to use UUIDs - they just don't like how they look.

    I have never ever heard a developer even mention the way api keys look before.

  • xdennis 5 hours ago |
    > uppercase letters and numbers for "blocky" aesthetics and readability

    I'm not sure if this is meant to be read as "uppercase (letters and numbers)", but it is effectively what he's referring to.

    Lowercase digits do exist[1], but there's no Unicode encoding for them and fonts typically have to choose to support one or the other.

    [1]: https://en.wikipedia.org/wiki/Text_figures

  • bkummel 5 hours ago |
    I'm not sure whether the result is more readable than a UUID. With a UUID you at least know that it can only be [0-9A-F]. Besides, who on earth _types_ API keys? You should copy/paste them and store them in a password manager. So they don't _need_ to be readable.