15 ms·
Self-Signed JWTs
- beckthompson 1y agoGithub has a cool little article on making JWTs for their API. Very useful! https://docs.github.com/en/apps/creating-github-apps/authenticating-with-a-github-app/generating-a-json-web-token-jwt-for-a-github-app https://docs.github.com/en/apps/creating-github-apps/authent... The JWT website is also super useful https://www.jwt.io/ https://www.jwt.io/
- rvz 1y agoThis article uses "ES256" for the alg, GitHub uses "RS256" as their alg and a very deranged few use "none". The point here is this article is giving the developer lots of rope to hang themselves with the JOSE standard on JWT/K/S and it is a sure way to implement it incorrectly and have lots of security issues. PASETO is a much better alternative to work with: https://paseto.io https://paseto.io with none of the downsides of the JOSE standard.
- danscan 1y agoHaven't heard of PASETO, but I'll check it out. I'd say JOSE is an implementation detail of what I'm advocating for, so very open to alternatives.
- JimDabell 1y agoJWTs and JOSE have a bad reputation for footguns and ignoring modern cryptographic principles. PASETO is the “mostly fixed” version of JWTs, but if you’re looking for something with more features, biscuits are quite interesting: https://www.biscuitsec.org https://www.biscuitsec.org
- jillesvangurp 1y agoYes you can create unsigned JWTs. Don't do that and don't accept any such tokens as valid (which would be the even bigger facepalm worthy mistake). Just do it right (and at this point it is widely documented what the pitfalls are here), comply with the widely used and commonly supported standards, and follow the principle of the least amount of surprise. Which is kind of important in a world where things need to be cross integrated with each other and where JWTs, JOSE, and associated standards like OpenID connect are basically used by world+dog in a way that is perfectly secure and 100% free of these issues. Honestly, it's not that hard. The paradox with Paseto is that if you are smart enough to know what problem it fixes, you shouldn't be having that problem and also be smart enough to know that using "none" as an algorithm is a spectacularly bad idea. You shouldn't need Paseto to fix it if you somehow did anyway. And of course you shouldn't be dealing with the security layer in your product at all if that is at all confusing to you.
- JohnMakin 1y agois the author suggesting allowing the client to set their own claims and using that to auth whatever action they are going to take? I have to be misunderstanding what they are saying - that sounds fraught with risk
- reactordev 1y agoSome engineers forgot the secret/salt part of generating the jwt. Sometimes you can just pack some claims in there and encode it and it works!!
- danscan 1y ago(Author here) The JWT signer should be the authority setting claims, so if your server is the authority and the client is untrusted, the server can provide the client a pre-signed JWT with the claims it needs, and the client can send that along with requests to the API. But this scheme is flexible. You could also have the client send "requested" claims for the server to consider adding if allowed when getting a JWT. You could also reverse-proxy client requests through your server, adding any claims the server allows.
- danscan 1y agoIn some apps, the client may be the signing authority (e.g. it owns the resource it's accessing). In that case, the client can possess the JWK keypair and do its own signing.
- nabwodahs 1y agoThat site is blocked by Fortinet as "pornography."
- snickerdoodle12 1y agoDid you contact Fortinet since you're the one that apparently utilizes them?
- nabwodahs 1y ago[dead]
- danscan 1y agoBummer. Not sure what I can do about that, but I assure you it is not pornography!
- garganzol 1y agoModern day AV software: isCornography = url.contains("corno") || url.contains("body") || url.contains("self"); isVirus = url.contains("virus") || competitorUrlRegex.matches(url);
- nabwodahs 1y ago[dead]
- marifjeren 1y ago> Visit our website. Create an account. Verify your email. Create a project. Add your credit card. Go to settings. Create an API key. Add it to your password manager. Drop it in your .env file. Download our SDK. Import it. Pass your env var in. Never share your API key. Make sure you never commit it to source control. None of this "BS" actually goes away with self-signed JWTs, right? Just replace mentions of "API Key" with public/private key and it's otherwise a similar process I think.
- danscan 1y agoThe things that change are: 1. With self-signed JWTs, you could start consuming APIs with free tiers immediately, without first visiting a site and signing up. (I could see this pattern getting traction as it helps remove friction, especially if you want to be able to ask an LLM to use some API). 2. Compare this scheme to something like the Firebase SDK, where there's a separate server-side "admin" sdk. With self-signed JWTs, you just move privileged op invocations to claims – consuming the API is identical whether from the client or server. 3. The authority model is flexible. As long as the logical owner of the resource being accessed is the one signing JWTs, you're good. A database service I'm working on embeds playgrounds into the docs site that use client-generated JWKs to access client-owned DB instances.
- deleted 1y ago[deleted]
- simsla 1y agoThe problem I see with (1) is that it becomes a little bit too easy to regenerate public keys and circumvent free tier metering.
- actinium226 1y agoI guess that's easily addressed by requiring an account and a public key to access the free tier. Still better than having to get yet another API key.
- woodruffw 1y agoI feel like I’m not understanding the target audience for this post: are there people/companies out there specifically paying other companies to be their key-holding party for JWT issuance purposes? I know about SSO providers of course, but that’s several layers of abstraction up. (Maybe my confusion here is that these JWTs are being described as self-signed, as if there’s a JWK PKI cabal out there, like the bad old days of the Web PKI. There isn’t one that I know of!)
- danscan 1y agoThe key distinction I am getting at is: self-signed as in “signed with a self-issued key pair”, as opposed to using an API key/credential that has been issued to you
- actinium226 1y agoInteresting, so instead of OpenAI giving me an API key, I give them a public key, which they register. Sounds like what we already do with GitHub. I like it.
- esseph 1y agoThis is similar to ssh key auth. (Pubkey, privkey)
- PantaloonFlames 1y agoYes. and it’s easy to do keypair generation in the browser using subtle crypto. That API doesn’t provide jwk generation, but seems like it would be relatively easy to do even without the jose module. And the browser cab “download” (really just save) the keypair using the Blob API. Keys need not leave the browser. An api developer portal could just offer - generate a new keypair? - upload your existing public key? …As a choice. Either way the public key gets registered for your account. The end. Easy.
- lokar 1y agoEven better if they would take a private CA cert.
- johncolanduoni 1y agoThis is actually how GCP has always done service account authentication. A GCP service account key is an asymmetric keypair and Google stores the public key. AWS is somewhat similar, but they use an symmetric HMAC so they store the same secret key you use.
- danscan 1y agoIt's interesting to imagine taking the pubkey as identity concept to its full extents in situations like this, for example if you could create a cloud account, spin up resources, and authorize payment for them all programmatically without having to enter payment details on a form (because your keypair can authorize payment with the whatever payment method you use)
- 1y ago
- jauntywundrkind 1y agoThe model here feels not entirely dissimilar to Passkeys? Both are user provided auth tokens?? [Ed: allegations that the following is inaccurate! Probably checks out? Yes I meant the browser not the domain bound part, that seems solid.] Pity that Passkeys are so constrained in practice by browsers, that using them pretty much requires you trust the cloud providers absolutely with all your critical keys.
- danscan 1y agoYeah, I am sort of a fan of Passkeys in principal, but they are domain bound (you can't use them across domains). I wish there were something built into browsers that offered a scheme where your pubkey = your identity, but in short there are a lot of issues with that
- johncolanduoni 1y agoThey're not constrained that way at all. The communication between browsers and various passkey-holding software and hardware is an open standard. There are open-source apps that can hold and sync passkeys. I don't know why everyone keeps repeating this obvious falsehood.
- danscan 1y agoNot sure which way of constraint you're referring to, but WebAuthn credentials are bound to a domain via Relying Party ID. There's a proposal for cross-domain usage via Related Origins, but that scheme depends on the authority of the relying party, meaning you can't say "I'd like to be represented by the same keypair across this set of unrelated domains"
- johncolanduoni 1y agoI was referring to this: > Pity that Passkeys are so constrained in practice by browsers, that using them pretty much requires you trust the cloud providers absolutely with all your critical keys. Passkeys are not constrained so you have to trust cloud providers or anyone else with all your critical keys. The key is resident in whatever software or hardware you want to use, and anyone can create passkey software or hardware that will work with Chrome etc. I'm talking about (and I'm pretty sure the OP was referring to) the other side of WebAuthn: where the credentials surfaced to JavaScript via WebAuthn actually come from and how the browser relays requests that a challenge is signed.
- maxwellg 1y agoI _love_ JWTs for API authentication - one of the nicest APIs I ever consumed was essentially JSON RPC over JWTs. Unfortunately they represent a huge usability hit over API Keys for the average joe. Involving cryptography to sign a JWT per request makes an API significantly harder to consume with tools like Postman or CURL. You can no longer have nice click-to-copy snippets in your public docs. You either have an SDK ready to go in your customer's language or ecosystem of choice, or you're asking them to write a bunch of scary security-adjacent code just to get to their first successful request. No, I don't have a JWT library recommendation for Erlang, sorry. 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.
- tempest_ 1y agoBack in the day I worked at a place that had HMAC signing on an http endpoint. 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.
- danscan 1y agoEasy to imagine that haha. That’s part of the reason I’d lean on a standard like JOSE and make signing happen automatically for users who prefer to use an SDK
- lll-o-lll 1y ago> Unfortunately they represent a huge usability hit over API Keys for the average joe. Involving cryptography to sign a JWT per request makes an API significantly harder to consume with tools like Postman or CURL. Just generate the JWT using, e.g. https://github.com/mike-engel/jwt-cli 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/authorization/authorization-types/ https://learning.postman.com/docs/sending-requests/authoriza..., although I have not bothered with this personally.
- jacobljohnston 1y agoThis is already something in mainstream authentication applications you host yourself on your own domain. We use Keycloak. I don't know why anyone would install a JavaScript library to do this. It's not that difficult.
- danscan 1y agoFair. I assume you mean asymmetric key cryptography and not JWKs in particular? JOSE is a pretty good library if you need the latter and you’re already working in JS
- jacobljohnston 1y agoWe’re using JWKs.
- danscan 1y agoAh, and just the subtle crypto API to generate keys? Or are you not generating them on the client?
- motorest 1y ago> Fair. I assume you mean asymmetric key cryptography and not JWKs in particular? There's some degree of confusion in your comment. JWKs is a standard to represent cryptographic keys. It is an acronym for JSON Web key set. > JOSE is a pretty good library (...) JOSE is a set of standards that form a framework to securely transfer claims.
- vips7L 1y agoI wish someone would have used keycloak at my place. They decided to write it all by hand instead.
- distalx 1y agoOn the B2B2C section, my mind immediately went to OAuth. For a developer like Bob giving his end users access to a service, wouldn't a standard OAuth flow where his users grant permission to his app would be the more conventional and secure solution? It feels like that model handles key management, delegation, and revocation in a well-established way. What am I missing here that makes this a better fit?
- motorest 1y ago> What am I missing here that makes this a better fit? From a cursory read, the answer is "it doesn't". The blogger puts up a strawman argument to complain about secret management and downloading SDKs, but the blogger ends up presenting as a tradeoff the need to manage public and private keys, key generation at the client side, and not to mention services having to ad-hoc secret verification at each request. This is already a very poor tradeoff, but to this we need to factor in the fact that this is a highly non-standard, ad-hoc auth mechanism. I recall that OAuth1 had a token generation flow that was similar in the way clients could generate requests on the fly with nonces and client keys. It sucked.
- distalx 1y agoSpot on. The burden and complexity of that cryptographic signing on the client is exactly what OAuth2 was created to avoid. Thanks for making that connection.
- tonyhart7 1y agowhat is this drawback???? surely there must be a catch somewhere right??? if its that easy then everyone would love to use this
- bastawhiz 1y agoThis neglects a few pretty fundamental problems. First, it only works for APIs where a user is present. This doesn't work for APIs where your server is the client. Otherwise there's nobody to visit the "payment URL" that the author describes. If you did this on the server, you need to share the keys with all the machines in your fleet (so each machine doesn't get a payment URL when it boots), which feels just like an API key. And if the payment URL could be completed programmatically, it requires the server to provide a value given to you by the API vendor to note your identity as they understand it, which feels exactly like a bearer token. But this is just OAuth in reverse. Instead of authing and getting a token, you generate a key and then auth. Either way you need to store a sensitive value. It's questionable where there's material benefit to not sending a bearer token over the wire (TLS works well). Another problem is key management. Where in the flow do I tell the API what my device is? "This is my Pixel 9 Pro" isn't going to be a thing I'd expect to answer when going through the payment URL. So now I've got a public key registered and I lost my phone. I have to log into this API and tell them "yeah don't trust that key anymore." Which one? I suspect the best ux you can do is to just show the last time a key was used, which is unhelpful for users with multiple devices. The B2B2C note at the bottom is just clown shoes. The servers simply aren't authing to each other (the payment URL has to go to the end user), as I noted above. And if a user needs to auth to every back and API that the app uses, that's kind of wild. Yes, you can do clever things with ZKPs but you can't solve the real UX problems here with more crypto. And that's assuming that all the back end APIs you're using fully support this scheme. The last problem is an issue with invalidating key material. If I log into the vendor and say "My device was imaged while going through customs, don't trust this public key anymore", the server needs to remember that key forever to know that it's banned. You can't just delete the key. Consider: I mark the key as invalidated, then open my phone and use the app. It sends the bad key, which isn't recognized. I go through the payment URL and re-auth. That key is now active again on both my device and the image that was taken going through customs. The only way to avoid this is for every key ever used is remembered until the end of time. If someone tries to use an invalid key, a separate response needs to be sent telling them the key is no longer allowed and to create a new one. Without that, the device can't possibly know if the user didn't auth in the payment URL yet, or if they authed and the key was invalidated (and a new one should be created).