[HN Gopher] Should I use JWTs for authentication tokens?
       ___________________________________________________________________
        
       Should I use JWTs for authentication tokens?
        
       Author : pantalaimon
       Score  : 540 points
       Date   : 2024-05-27 15:31 UTC (1 days ago)
        
 (HTM) web link (blog.ploetzli.ch)
 (TXT) w3m dump (blog.ploetzli.ch)
        
       | zie wrote:
       | Not to mention it's ridiculously easy to make JWT's insecure.
        
         | toenail wrote:
         | It's ridiculously easy to make any computer insecure.
        
           | finack wrote:
           | Some computers make it harder than others.
        
         | EthanHeilman wrote:
         | Can you provide some examples here using modern JWT libraries?
         | I'm not saying you are wrong or right, but this comment as it
         | is written doesn't add much to the discussion.
        
           | CiPHPerCoder wrote:
           | https://github.com/firebase/php-jwt/issues/351
        
             | EthanHeilman wrote:
             | Thanks for providing this, it is interesting vulnerability
             | to read about.
             | 
             | In terms of JWT vs other ways of doing this, is there any
             | evidence that JWTs are more vulnerable that other
             | approaches? Clearly there are vulnerabilities is other
             | approaches as well.
             | 
             | I buy the statement that bearer authentication JWTs are
             | much worse than proof of possession JWTs, but are bearer
             | authentication JWTs worse than other bearer authentication
             | approaches? What data would you need to argue that position
        
               | CiPHPerCoder wrote:
               | > In terms of JWT vs other ways of doing this, is there
               | any evidence that JWTs are more vulnerable that other
               | approaches? Clearly there are vulnerabilities is other
               | approaches as well.
               | 
               | Contrast JWTs with PASETO implementations when you make
               | that sort of analysis.
               | 
               | i.e., pick any that support v3/v4 and try to attack them
               | the same way that JWT implementations have been
               | vulnerable, or worse ways: https://paseto.io
        
               | EthanHeilman wrote:
               | Thanks for sharing this. I do a lot of work in this area
               | and I had not come across PASETO before. It is an
               | exciting project.
               | 
               | The nonce is especially nice because it makes the token
               | high entropy enough that if only the signature leaks an
               | attacker can't brute force the full token. This isn't
               | always true in OIDC JWTs.
        
         | bebop wrote:
         | I have not seen an authorization server that makes it easy to
         | configure no signing algorithm or even one that might be
         | considered insecure. Most of the client authentication
         | providers I have used (I.e frameworks) have also forced a
         | secure algorithm, usually starting with rsa 256. So while
         | technically you can use a no algorithm signer, I have never
         | seen this happen.
        
           | CiPHPerCoder wrote:
           | The vulnerability is usually in verifiers rather than
           | signers.
           | 
           | See, for example:
           | 
           | https://github.com/firebase/php-jwt/issues/351
        
       | ramesh31 wrote:
       | So then why does Auth0 use JWTs for everything..?
        
         | zaphar wrote:
         | Because their business is auth as a service and because their
         | database is not your database and they don't want you hitting
         | their database directly.
         | 
         | A similar question to the articles is whether you actually need
         | Auth0 or not. You might but then you are offloading all the
         | issues of JWT's to Auth0 so in theory, probably not in
         | practice, you don't have to worry about those.
         | 
         | For many apps however. You probably don't really need Auth0.
        
           | FerdSlav wrote:
           | As someone that is just attempting to get a better
           | understanding of this aspect of a tech stack, when you say
           | many apps don't need Auth0 what is the actual alternative for
           | the whole user authentication story?
        
             | zaphar wrote:
             | Most web frameworks come with out of the box authentication
             | plugins that will use your own database. It's plenty good
             | enough and you still don't have to roll your own. Adding an
             | external service adds complexity that you may not want to
             | pay for later.
        
             | angoragoats wrote:
             | Submit a username and password via a form over https. Your
             | backend hashes the password, and checks it against the
             | stored (hashed) password in your database. If it matches,
             | the user provided the correct password. Create a session
             | token (random string is fine) and return it to the user via
             | a cookie in the reply. Store the session token in your
             | database, such that you can map it to the authenticated
             | user. Then on each subsequent request, look up the session
             | token and you have your logged in user.
             | 
             | This is how apps were doing it for literally decades,
             | before JWT was invented. And most web frameworks will do
             | all of this for you.
        
               | swiftcoder wrote:
               | > the stored (hashed) password in your database
               | 
               | Hashed _and_ salted
               | 
               | I hope
        
               | angoragoats wrote:
               | Technically yes, of course, but if you're using an
               | algorithm that requires you to manually worry about
               | salting then you're probably doing it wrong. Hence,
               | "hashed" with an algorithm like bcrypt or scrypt is good
               | enough.
        
               | zaphar wrote:
               | There are some subtleties here that are important to
               | know[0]. Which is why I generally advise people to use
               | the framework provided code for this.
               | 
               | 0: Such as use a secure hash like sha256 or blake2
               | instead of md5.
        
               | angoragoats wrote:
               | Yes, though I would recommend something that is resistant
               | to timing attacks such as bcrypt. And this was why I
               | mentioned frameworks in my last sentence. My intention
               | wasn't to give a comprehensive soup to nuts guide for
               | what to do; instead i was giving a high level overview.
        
               | andoando wrote:
               | Auth0 provides a lot more goodies than this though.
               | Password reset, organizations, multiple login flows, etc
        
               | angoragoats wrote:
               | Yes, and most authentication plugins for web frameworks
               | provide the same things.
        
               | koliber wrote:
               | If you write a B2C app, there is a good chance that you
               | might not need Auth0 and the functionality of the
               | authentication, authorization, and account management
               | tools in your framework suffice. If you plan on selling
               | B2B you might need to support SAML and other enterprise
               | federated login mechanisms. There, the scales tip in my
               | book and I would go with Auth0. It's expensive to support
               | SAML in-house.
        
               | angoragoats wrote:
               | There are plenty of ready made SAML libraries out there
               | that should work with whatever web framework you like to
               | use.
        
               | koliber wrote:
               | There are plenty of libraries. But supporting a
               | production SAML service provider takes a lot of work.
               | 
               | I've done this before. The first library I used had a
               | horrible security issue that remained unfixed. We
               | switched to one that seemed to be secure. Implementing
               | SAML is non-trivial. Adding automated testing is also not
               | something that required more senior people on our team.
               | Getting engineers to understand how SAML work takes
               | effort.
               | 
               | Also, about one out of five SAML IdP's are unconventional
               | in some way. They are a royal pain to support.
               | 
               | The support burden of SAML is much higher than expected.
               | Paying for Auth0 is cheaper than the engineering cost of
               | supporting SAML, even with one of the existing libraries
               | you refer to.
        
           | CuriouslyC wrote:
           | Need? Maybe not. But it's a time saver out of the gate.
        
           | swiftcoder wrote:
           | > You probably don't really need Auth0
           | 
           | And there's a good chance you really don't want to pay for
           | Auth0. Their Enterprise tier becomes very _expensive_ as your
           | MAU starts to grow
        
           | ramesh31 wrote:
           | >but then you are offloading all the issues of JWT's to Auth0
           | so in theory, probably not in practice, you don't have to
           | worry about those.
           | 
           | Yeah, exactly. I get to offload everything to Auth0 instead
           | of maintaining a cryptographer on our payroll to reimplement
           | a solved problem. Hand rolled OSS authz/n may work for
           | individual projects, but there's no other reasonable solution
           | in a modern enterprise environment with SSO.
        
             | finack wrote:
             | You don't need a cryptographer on payroll to install and
             | configure an SSO library. Maybe your developers aren't
             | skilled enough to do that, but it is not a challenge for a
             | proper senior developer.
        
         | rendall wrote:
         | Well, I imagine, if we're taking the opinion of the article as
         | fact, Auth0 must be able to scale to enterprise levels, i.e.
         | have so many long-tail accounts that it approaches
         | Facebook/Google scale.
        
         | augunrik wrote:
         | JWTs are best practice for OAuth as it can transport claims.
         | It's up to your application if you continue to use it after the
         | initial flow. You are fine to convert it, but most apps don't
         | as it's easier.
        
         | koliber wrote:
         | JWT is a great way to authenticate the person who is trying to
         | log into your app. Once authenticated, you are free to exchange
         | the JWT token for a sessionid.
        
           | angoragoats wrote:
           | The whole point of the article was that JWT is unnecessary if
           | you're just using it to get a session token. Why not just cut
           | out the middleman?
        
       | endisneigh wrote:
       | Jwts are fine, just use them properly.
        
         | pwpwp wrote:
         | Maybe address any of the issues raised in the post?
        
         | croes wrote:
         | C/C++ is just fine, just use it properly.
        
           | shiandow wrote:
           | Rolling your own crypto is fine, just do it properly.
        
       | gizzlon wrote:
       | You _can_ implement a blocklist of all the revoked JWT and
       | publish it to all servers. The list should be small, because only
       | otherwise valid tokens need to be included. It becomes so much
       | more complicated than a simple check-the-db setup though.
       | 
       | I don't think I would start with JWT if I did this again.
        
         | esafak wrote:
         | what would you do?
        
           | gizzlon wrote:
           | Sessions, like it's 2005 :P See above.
        
         | brabel wrote:
         | You're talking about tokens in general, not just JWT. The only
         | alternative I know to tokens is to query the DB every time (or
         | perhaps use a cache to make the lookup less often, but then you
         | also have to find a way to invalidate the cache - back to
         | square one?).
        
           | gizzlon wrote:
           | > You're talking about tokens in general, not just JWT. Yes,
           | all stateless tokens. But I have never seen an in-house token
           | system that was not using JWT's.
           | 
           | Yes, query the DB or some sort of storage every time. It
           | sounded so clean and nice and fast to just check JWTs without
           | any network calls. But it ended up very messy and
           | complicated. Might still be worth it in some cases, of
           | course, but I would start my next project with random
           | sessions stored in a db or redis or memcache or .. something
           | :)
           | 
           | You can actually do crazy stuff with your sessions as well,
           | to avoid a normal db lookup. But in practice all services I
           | have worked on would/did not suffer noticeably for a fast DB
           | lookup.
        
         | cryptonector wrote:
         | Having to list the tokens to revoke is a problem in several
         | ways. Obviously publishing the tokens themselves allows
         | attackers to use them before relying parties get the revocation
         | notice, so publish a token hash instead. But even publishing a
         | token hash is annoying because you then have to record all the
         | claims + token hashes issued, and that's a failure point.
         | Instead you'd want to revoke _subjects_ (`sub`), but since
         | tokens don't have to have those... (In enterprise systems
         | though your tokens should name subjects.)
        
       | weinzierl wrote:
       | Using JWT from Keycloak just to obtain a session cookie. Is this
       | a common pattern or a smell?
        
         | doctorpangloss wrote:
         | It's a good idea. The article conflates authentication with
         | authorization. Your application can authorize many different
         | ways, most do. You can use your session for authorization -
         | your application can decide what a person can and can't do. But
         | facts about the user's identity never change, like their
         | uniquely generated ID in your database, and that's what gets
         | stored in Keycloak's `sub` field, so it's fine to use that for
         | trading a token for a session cookie. Their password _does_
         | change but that 's Keycloak's job, is to turn passwords into
         | authentication tokens.
         | 
         | The JWT always stores facts about the principal (aka who or
         | what is doing something), and those don't really make sense to
         | revoke or whatever anyway. Stuff that will never change over
         | time. JWTs can optionally store something like a `role` or
         | similar fact that may change over time, specific to your
         | application. Those facts can be used to decide what you can do
         | in your application, that's authorization. We could talk about
         | when and how that should be done, but it would be too nuanced
         | for these evergreen JWT blogposts.
        
           | tptacek wrote:
           | The article isn't talking about authz at all.
        
         | eYrKEC2 wrote:
         | Taking complete systems off the shelf and using them smells a
         | lot like money. If/when your app requires enterprise
         | integration, you'll be fanatically happy that you chose
         | Keycloak over having to implement okta... then ldap... then..
         | Saml... then.. Kerberos... then ldap again with custom
         | mappers..
        
       | davedx wrote:
       | Okay I'll bite...
       | 
       | This post doesn't seem to address microservice architectures at
       | all? For me, this is the primary reason to use JWT's -- so you
       | can pass authentication ("claims", or whatever you want to call
       | them) through your chain of microservice service-to-service
       | calls. If you don't have microservices then there's much less
       | reason to use JWT's.
       | 
       | I'm not saying the article is a strawman exactly, but it does
       | seem to miss the primary use case of JWT's. At least, the way
       | I've used them in anger.
       | 
       | Also, the "JWT's can be insecure if you use the wrong library or
       | configure them incorrectly" argument, while having _some_ points,
       | seems to me more of an argument that you should really do due
       | diligence on any libraries you use for security. The better JWT
       | libraries are not insecure by default.
       | 
       | I wouldn't use JWT's if I were making a monolith, but there are
       | lots of companies who (for better or worse) use microservices.
        
         | doctorpangloss wrote:
         | > so you can pass authentication ("claims", or whatever you
         | want to call them) through your chain of microservice service-
         | to-service calls.
         | 
         | This is a misconception about so called zero trust. You can't
         | "just" pass the same token to someone else. They can use it to
         | impersonate or misuse the token later. While you are going to
         | say that "my microservices will not impersonate users because
         | to each other, they are all _trusted_ ," you have run directly
         | into the difference between trusted and zero trust.
        
           | EthanHeilman wrote:
           | > This is a misconception about so called zero trust. You
           | can't "just" pass the same token to someone else. They can
           | use it to impersonate or misuse the token later.
           | 
           | Put another way, JWTs used as bearer tokens have vulnerable
           | to intra-audience replay attacks.
           | 
           | While this is true for many zero trust architectures, but you
           | don't have to build zero trust architectures this way. Simply
           | have the token commit to a public key of a signing key held
           | by the identity, then you can do Proof-of-Possession and
           | remove these replay attacks. This is the direction zero trust
           | is headed. For instance AWS is slowly moving toward this with
           | sigV4A. Most zero trust solutions aren't there yet.
        
             | doctorpangloss wrote:
             | Well a lot of value in application architectures like this
             | is, I want to give something access to my Google Calendar
             | forever, to schedule tasks and read stuff, expressly
             | without user intervention. Most people want token exchange
             | - that an all-powerful user token gets exchanged for a
             | token with the privileges specific to the service that
             | holds onto it. I don't really want Google or Apple or
             | whoever has, for idiosyncratic reasons, possession of a
             | private key, to sign every request I make to Google
             | Calendar, because they will inevitably revoke it sooner for
             | obnoxious business reasons than any good security reason.
             | And if I give a signing key to the service doing this deed
             | for me, it's kind of redundant to an ordinary exchanged
             | JWT.
             | 
             | Really the ergonomics are why this hasn't been adopted more
             | readily. I wonder why it's possible to have OpenTelemetry
             | inject a header into thousands of different APIs and
             | services for dozens of programming languages, more or less
             | flawlessly. But if I wanted to do this at process
             | boundaries, and the content of my header was the result of
             | a stateless function of the current value of the header
             | (aka token exchange + destination service): you are shit
             | out of luck. Ultimately platform providers like Google,
             | Apple and Meta lose their power when people do this, so I
             | feel like the most sophisticated and cranky agitators are
             | more or less right that the user experience is subordinate
             | to the S&P top 10's bottom line, not real security
             | concerns.
        
               | EthanHeilman wrote:
               | The first case sounds more like a case for OAuth which
               | doesn't have to use JWTs or digital signatures.
               | 
               | > I don't really want Google or Apple or whoever has, for
               | idiosyncratic reasons, possession of a private key, to
               | sign every request I make to Google Calendar, because
               | they will inevitably revoke it sooner for obnoxious
               | business reasons than any good security reason.
               | 
               | Can you provide more context on this? I would assume
               | asymmetric signing keys are less likely to be revoked
               | than say an HMAC key since an HMAC key must be securely
               | stored at both the client and server whereas you can just
               | put a asymmetric signing key in an HSM at the client and
               | be done with it.
        
               | radicalbyte wrote:
               | Which we have in modern computers and mobiles (the EU
               | identity wallet concept is built around them).
        
               | EthanHeilman wrote:
               | I thought the EU wallet was using JWTs that attest to
               | public key. You don't use them as bearer tokens, you use
               | them as certificates and then do verifiable credential
               | presentation via proof of possession and SD-JWTs.
        
             | marcosdumay wrote:
             | Bearer tokens are vulnerable to man-in-the-middle
             | impersonation.
             | 
             | It's right in the name.
             | 
             | Anyway, zero trust architecture are wildly overrated and
             | used in way more places than they should. But the entire
             | thread is correct in that you can't build them with bearer
             | tokens.
        
               | EthanHeilman wrote:
               | Man-in-the-middle impersonation is not the biggest threat
               | because TLS 1.3 does a decent job of protecting the token
               | in transit. The biggest issue is the endpoints:
               | 
               | 1. The client that holds the token can't use an HSM or
               | SSM to protect the token because they need to transmit
               | it. Thus a compromise of the client via an XSS or
               | Malware, results in the token leaking.
               | 
               | 2. The server that receives the token, might be
               | compromised and they can replay the token to other
               | servers or leak it accidentally e.g., with a log file or
               | to an analytics service.
               | 
               | Both of these problems go away if you uses OpenPubkey or
               | Verifiable Credentials with JWTs. The JWT is now a public
               | value, and the client holds a signing key.
               | 
               | 1. The client can protect the signing key with an HSM or
               | SSM (modern web browsers grant javascript access to a
               | SSM).
               | 
               | 2. The server only receives the JWT (now a public value)
               | and a signature specific to that server. They don't have
               | any secrets to protect.
               | 
               | > But the entire thread is correct in that you can't
               | build them with bearer tokens.
               | 
               | You can and people do, but it is far better to use proof
               | of possession JWTs than bearer JWTs. Even better to use
               | JWS instead of JWTs so you can make use of multiple JWS
               | signers (a JWT is a type of JWS, but a JWS with more than
               | one signer can not be a JWT).
        
               | ec109685 wrote:
               | User X's web browser calls Server A which makes a web
               | service request to Server B that needs to authenticate
               | that user X is making the call.
               | 
               | What types of tokens do you suggest in each case?
        
               | EthanHeilman wrote:
               | To pitch my own project, OpenPubkey[0], it is designed
               | for exactly this use case. OpenPubkey let's you add a
               | public key to an ID Token (JWT) without needing any
               | change at the IDP.
               | 
               | 1. Alice generates an ephemeral key pair (if she is using
               | a browser she can generate the key pair as a "non-
               | extractable key"[1]).
               | 
               | 2. Alice gets ID Token issued by Google that commits to
               | their public key,
               | 
               | 3. Alice signs her API request data to Service A and
               | sends her ID Token to Service A.
               | 
               | 4. Service A checks the ID Token (JWT) is issued by
               | Google and that the identity (alice@gmail.com) is
               | authorized to make this API call, then it extracts
               | Alice's public key from the ID Token and verifies the
               | signature on the data in the API call. Then it passes the
               | signed data to Service B.
               | 
               | 5. Service B verifies everything again including that the
               | data is validly signed by Alice. Service B could then
               | write this data and its cryptographic prominence into the
               | database.
               | 
               | Technically OpenPubkey uses a JWS, but it is a JWS
               | composed of a JWT (ID Token) with additional signatures.
               | OpenPubkey signed messages, like the ones passed via the
               | API are also JWS.
               | 
               | I'm working on a system where each service in the path
               | adds their signatures to the signed message so you can
               | cryptographically enforce that messages must pass through
               | particular services and then check that at during the
               | database write or read. Using signature aggregation, you
               | don't get a linear increase in verification cost as the
               | number of signatures increase. It doesn't seem to add
               | much overhead to service meshes since they are already
               | standing up and tearing down mTLS tunnels.
               | 
               | The main question to me is how much autonomy do you want
               | to give to your services. There are cases in which you
               | want services to query each other without those services
               | having to prove that the call originated from a specific
               | authorized user.
               | 
               | [0]: https://github.com/openpubkey/openpubkey
               | 
               | [1]: https://developer.mozilla.org/en-
               | US/docs/Web/API/CryptoKey/e...
        
           | davedx wrote:
           | If someone nefarious steals your <secret something> then yes
           | you have problems, this isn't anything unique to JWTs
        
             | ummonk wrote:
             | It makes a pretty big difference whether you have to send
             | the secret down the wire to prove you possess it (e.g. a
             | JWT, unlike say a private key).
        
           | DaiPlusPlus wrote:
           | The audience and scope claims exist to address that problem.
           | Provided that RPs reject JWTs issued for other audiences than
           | themselves there's no security weakness here.
           | 
           | This is why JWTs are used in OIDC (e.g. "Sign-in with
           | Google": any website can use it, and it doesn't make Google's
           | own security weaker.
           | 
           | I'll concede that small, but important, details like these
           | are not readily understood by those following some tutorial
           | off some coding-camp content-farm (or worse: using a shared-
           | secret for signing tokens instead of asymmetric cryptography,
           | ugh) - and that's also where we see the vulnerabilities.
           | OAuth2+OIDC is very hard to grok.
        
             | rwdf wrote:
             | It's also why we use DPoP for serious stuff.
        
               | dadadad100 wrote:
               | I hadn't heard of DPoP until this mention. Please tell us
               | more. Google tells me it is Demonstrating Proof of
               | Possession, but is it supported by any products?
        
               | unscaled wrote:
               | DPoP described in RFC9449 - you can see from the RFC
               | number it's quite new. I don't think there's wide support
               | for it, but at least Okta supports it[1] and I think
               | Auth0 are also working on adding DPoP.
               | 
               | Is it good? I'm not a fan. To use DPoP safely (without
               | replay attacks), you need to add server-side nonces
               | ("nonce") and client-generated nonces ("jti", great and
               | definitely not confusing terminology there).
               | 
               | You need to make sure client-generated nonces are only
               | used once, which requires setting up... wait for it... A
               | database! And if you'll be using DPoP in a distributed
               | manner, with access tokens then, well, a database shared
               | across all services. And this is not an easy-to-scale
               | read-oriented database like you'd have to use for
               | stateful tokens. No, this is a database that requires an
               | equal number of reads and writes (assuming you're not
               | under a DDoS attack): for each DPoP validation, you'd
               | need to read the nonce and then add it to the database.
               | You'd also need to implement some sort of TTL mechanism
               | to prevent the database from growing forever and
               | implement strong rate limitation across all services to
               | prevent very easy DDoS.
               | 
               | It seems like the main driving motivation behind DPoP is
               | to mitigate the cost of refresh tokens being exfiltrated
               | from public clients using XSS attacks, but I believe it
               | is too cumbersome to be used _securely_ as a general
               | mechanism for safe token delegation that prevents  "pass-
               | the-token" attacks.
               | 
               | [1] https://developer.okta.com/docs/guides/dpop/nonoktare
               | sources...
        
               | jmdc wrote:
               | I agree that DPoP - especially the nonce - is quite
               | complex, but I don't think it's as bad as you make out.
               | 
               | Proof tokens can only be used for a narrow window of time
               | (seconds to minutes), so you just need a cache of
               | recently seen token identifiers (jtis) to do replay
               | detection. And proof tokens are bound to an endpoint with
               | the htm and htu claims. They can't be used across
               | services, so I don't see a need for that replay cache to
               | be shared across all services.
        
               | jmdc wrote:
               | DPoP is an OAuth extension that defends against token
               | replay by sender constraining tokens. It is a new-ish
               | spec, but support is pretty widespread already. It's used
               | in a lot of European banking that has pretty strict
               | security requirements, and it's supported by some of the
               | big cloud identity providers as well as the OAuth
               | framework I work on, IdentityServer. We have sample code
               | and docs etc on our blog:
               | https://blog.duendesoftware.com/posts/20230504_dpop/
        
               | rwdf wrote:
               | It's a new proposed standard. Where I work (in healthcare
               | in Europe) we have it as a requirement for any new APIs
               | we offer public access to. We have our own auth service,
               | but looks like Okta already offers DPoP.
        
             | chipdart wrote:
             | > The audience and scope claims exist to address that
             | problem. Provided that RPs reject JWTs issued for other
             | audiences than themselves there's no security weakness
             | here.
             | 
             | My interpretation is that the audience and scope claims, as
             | other features like nonce, are in place to prevent tokens
             | from being intercepted and misused, not to facilitate
             | passing tokens around.
        
               | Aeolun wrote:
               | Don't see how those prevent tokens from being misused?
               | They just prevent anyone from issuing tokens as you. Not
               | by themselves, but if you implement your server
               | correctly.
        
               | chipdart wrote:
               | > Don't see how those prevent tokens from being misused?
               | 
               | The purpose of a nonce is to explicitly prevent the token
               | from being reused.
               | 
               | The purpose of the other claims is to prevent them from
               | being accepted (and used) in calls to other services.
               | 
               | If you implement your server correctly, each instance of
               | each service is a principal which goes through auth flows
               | independently and uses its own tokens.
               | 
               | There is no token sharing.
        
             | ramchip wrote:
             | > (or worse: using a shared-secret for signing tokens
             | instead of asymmetric cryptography, ugh)
             | 
             | What's so terrible about that? Several security engineers I
             | trust favor designs with symmetric crypto, e.g. Fly.io
             | https://fly.io/blog/macaroons-escalated-quickly/ and
             | Facebook https://eprint.iacr.org/2018/413.pdf
        
               | ak217 wrote:
               | It limits your ability to compartmentalize your
               | infrastructure, establish security perimeters, and
               | provide defense-in-depth against vulnerabilities in your
               | dependencies.
        
             | locallost wrote:
             | Large companies have fallen into this trap [1]. So you are
             | right that aud addresses the problem, but it's widespread
             | enough to question if it really affects just coding camp
             | content farms. Hard to grok is probably possibly in some
             | way a design flaw.
             | 
             | [1] https://salt.security/blog/oh-auth-abusing-oauth-to-
             | take-ove...
        
         | EthanHeilman wrote:
         | > If you don't have microservices then there's much less reason
         | to use JWT's.
         | 
         | Fair point. This post assumes a single database which opaque
         | tokens can be mapped to. That said, a lot of smaller webapps
         | are and should be monoliths.
        
         | johnnyAghands wrote:
         | Yeah I agree, but I think this post is for those cases where
         | this design might be inappropriate, mainly monoliths with
         | single dbs.
         | 
         | I disagree with the whole "you're not Google/FB"/"over arbitary
         | RPS" logic though. If the design makes sense then it makes
         | sense, end of story. Just understand it.. lol
        
         | wg0 wrote:
         | Side question, anyone knows where the phrase "used in anger"
         | comes from? I know it means using something in production but
         | where does it come from?
         | 
         | Is it about battlefield and such?
        
           | louthy wrote:
           | It's military based isn't it? First time you use a weapon "in
           | anger" is to test its effectiveness in real world conflict
        
           | mceachen wrote:
           | I've always heard it as a shorter alternative to "holy hell
           | this tool is horrible but I'm unaware of an alternative, or
           | cannot apply an alternative solution."
        
             | dagss wrote:
             | Are you sure? I have never interpreted it this way and it
             | is the first way I hear this interpretation.
             | 
             | My understanding: To use something "for real" on an actual
             | project, not just toy around with it. (What you use can be
             | good or bad, expression doesn't say)
        
               | Zecc wrote:
               | My interpretation has always been the same as mceachen's,
               | but other comments in this sub-thread have thought me I
               | am wrong and your understanding is correct. Today I
               | learned.
               | 
               | For what it's worth[0]:
               | 
               | > If you do something in anger, you do it in a real or
               | important situation as it is intended to be done, rather
               | than just learning or hearing about it
               | 
               | [0]
               | https://dictionary.cambridge.org/dictionary/english/in-
               | anger
        
             | 7sidedmarble wrote:
             | No, it's a military term. Firing a cannon in anger for
             | instance means firing at the enemy, rather than in
             | training.
        
           | latentsea wrote:
           | I don't think it necessarily means using it in production but
           | rather using it on some non-trivial capacity that exposes you
           | to it's various complexities and nuances such that you have
           | more than just a surface level understanding. That probably
           | coincides with using things in production a lot of the time,
           | but that's not strictly necessary.
        
           | dilyevsky wrote:
           | Yes it's an old britishism - comes from "shots fired in
           | anger" (as opposed to training)
        
         | Ozzie_osman wrote:
         | Even with microservices, you still have the invalidation
         | problem. I guess you could use non-Jwt for external auth and
         | jwt between the services, but then you lose the benefit of
         | standardization (and still don't get full zero-trust). Or you
         | could standardize on jwt, but then, invalidation problem again.
        
           | jupp0r wrote:
           | It's pretty rare in practice to be able to make authz
           | decisions solely based on the information in JWT claims.
           | Space in HTTP headers is limited and any moderately complex
           | system will have a separate authz concept anyways that can be
           | used to check for token invalidation.
        
             | hyperadvanced wrote:
             | Exactly. Learned this the hard way. JWT is good for "this
             | token is legit and has XYZ role or group", and letting it
             | go to the next layer. The next layer should do some
             | addition checking that token has legit claims on modifying
             | a resource or taking other actions, however that might be.
        
           | davedx wrote:
           | Depends on your business. Most b2b companies probably don't
           | need to care about invalidation, at least not in the startup
           | phase. For B2C it's going to be more important. But ask
           | yourself "why do I need to pre-emptively invalidate tokens?"
        
         | thayne wrote:
         | Same applies to non-micro service architectures.
        
         | ak217 wrote:
         | > doesn't seem to address microservice architectures at all
         | 
         | Or just, you know, service architectures. Most microservice
         | architectures I've seen go way too far down the route of
         | breaking up services and their infrastructure to an impractical
         | level. But all you need in order to make JWTs really useful is
         | two federated services. This happens all the time, often in the
         | course of some partnership, acquisition, or just an
         | organizational structure meant to decouple 2+ teams from each
         | other.
         | 
         | Based on their mention of a single framework connecting to a
         | single database, OP seems to have never moved past the point of
         | developing a single service. Which is fine! It makes things
         | simpler for sure, and you can get very far with that. But they
         | are then dispensing advice about things they don't seem to know
         | much about.
        
       | EthanHeilman wrote:
       | > By just using a "normal" opaque session token and storing it in
       | the database, the same way Google does with the refresh token,
       | and dropping all jwt authentication token nonsense.
       | 
       | Not only is this true, but most actual deployments of JWTs just
       | have you swap a JWT (ID Token) for a opaque session token.
       | 
       | That said, I really like having a JWT signed by an IDP which
       | states the user's identity because if designed correctly you only
       | need to trust one party IDP. For instance Google (the IDP) is the
       | ideal party to identify a gmail email address since you already
       | have to trust them for this. I created OpenPubkey to leverage
       | JWTs, while minimizing and in some cases removing trust.
       | 
       | OpenPubkey[0, 1] let's you turn JWTs bearer tokens into
       | certificates. This lets you use digital signatures with ephemeral
       | secrets.
       | 
       | [0]: https://github.com/openpubkey/openpubkey [1]:
       | https://eprint.iacr.org/2023/296
        
         | Aeolun wrote:
         | Aren't JWT bearer tokens certificates already? Only the issuing
         | server has the private keys, and the public keys are used to
         | validate that server signed them?
        
           | magicalhippo wrote:
           | This is the other way around. It allows the user (token
           | holder) to sign messages "using" the ID token.
           | 
           | To be able to sign a message you not only need the ID token
           | but also the private/signing key, and the corresponding
           | public key is bound to the ID token (using the nonce field).
           | 
           | Thus you can prove that not only did Google say you are you,
           | but you possess the signing key associated with the ID token
           | that says so. Thus I can be sure someone else didn't just
           | steal your token in flight or from a log file for example.
        
           | EthanHeilman wrote:
           | Certificates use a signature to bind an identity to a public
           | key.
           | 
           | JWT bearer tokens use a signature to issue an identity, but
           | that don't include the public key of that identity. The
           | issuer has a public key, but the issuee does not.
           | 
           | There are plenty of JWTs that are certificates:
           | 
           | * proof-of-possession JWTs,
           | 
           | * self-issued JWTs, etc...
        
         | magicalhippo wrote:
         | > OpenPubkey let's you turn JWTs bearer tokens into
         | certificates.
         | 
         | This looks really awesome, thanks for sharing.
        
       | tobystic wrote:
       | Isn't JWT plaintext? Just remember your security controls
       | 
       | https://owasp.org/www-chapter-vancouver/assets/presentations...
        
         | ozim wrote:
         | I still can put JWT in http only secure cookie.
        
       | jameshart wrote:
       | Browser sessions are not the only authentication scenario.
       | 
       | > absolutely no one who is not Google/Facebook needs to put up
       | with the ensuing tradeoffs. If you process less than 10k requests
       | per second, you're not Google nor are you Facebook
       | 
       | What's the magic property that flips when you pass 10K requests
       | per second? Are we sure it's at 10K requests per second, not 8K?
       | or 5K? In general, at that kind of scale I'd think JWTs would
       | become less appealing - AWS operates on IAM for example.
       | 
       | And why are Google and Facebook the best examples of companies
       | who are operating at scale? There are different kinds of scale
       | than just 'ad auctions per second'. I would imagine the access
       | management concerns of, say, JP Morgan Chase are at least as
       | complex and challenging to scale as those of Facebook.
        
         | EthanHeilman wrote:
         | I once operated a very low usage webservice that used JWT for
         | auth. We got hit with a DDoS and it was trivial to mitigate by
         | using AWS API gateway to drop HTTP requests that didn't contain
         | a valid JWT for the IDPs we supported.
         | 
         | Making authentication only require a signature verification at
         | the edge (JWT) vs authentication middleware that needs to do a
         | DB read (opaque), can be a life saver even if you have 10
         | requests a second most of the time.
        
           | etothepii wrote:
           | This is a great point. 10 requests per second is likely to be
           | sufficient scale that you are noticeable to people that might
           | want to attack you. The ability to validate the key before
           | doing anything with it could be a huge time (and resource)
           | saver on AWS.
        
             | logifail wrote:
             | > The ability to validate the key before doing anything
             | with it [..]
             | 
             | Q: What about the endpoint that's issuing the tokens?
        
               | EthanHeilman wrote:
               | In OpenID Connect the endpoint is issuing the tokens is
               | run by Google, Microsoft or some other company that is
               | too big to fail (or rather if it fails everything goes
               | down).
               | 
               | If you are issuing the tokens yourself, you can build a
               | simple horizontally scaling identity service that only
               | does authentication and token issuance. With refresh
               | tokens, if that service goes down it only prevents users
               | not already signed in from signing in. Generally users
               | stay signed into to webapps for weeks at a time, so you
               | have massively reduced the impact: rather than 100% of
               | your users not being able to do anything on your site,
               | now 0.5% of users are impacted.
        
         | jupp0r wrote:
         | The notion that you have Google/Facebook scale problems at 10k
         | requests per second (vs 10s of millions of requests per second)
         | is a pretty funny claim in its own right.
        
       | conradludgate wrote:
       | A big problem not addressed when not using a signature based
       | authorization scheme is that you need to hit your database for
       | every access attempt. This makes you much more susceptible to
       | ddos attacks.
       | 
       | You need to be able to turn away malicious users as fast as
       | possible. If you take the time to check a database first, that's
       | a precious resource they can consume.
       | 
       | "Add a cache!", you might say? What if they use random client id
       | and client secret for every request, how do you cache against
       | that?
        
         | candiddevmike wrote:
         | The client ID should be a primary key or some other indexed
         | value, making those database hits fairly cheap. You'd also
         | typically have some kind of rate limiting.
        
         | Y-bar wrote:
         | We use a rate-limiting rule in the existing firewall on the
         | /auth endpoint. Our default is on five failed attempts in a
         | five minute window gets you a one hour ban.
        
           | dclowd9901 wrote:
           | How do you fingerprint the requests? IP address?
        
             | tebbers wrote:
             | The problem I had with IP address fingerprints is that we
             | have a large number of customers all behind one hospital's
             | single IP address.
        
             | Y-bar wrote:
             | Two ways, default key is [IP address + User ID] we also
             | have a fallback with a higher limit on [IP address] only
             | when we expect lots of attempts from e.g. a VPN.
        
           | moduspol wrote:
           | The issue from parent comment is that it needs to be on every
           | _access_ request, not every _login_ request.
        
         | dncornholio wrote:
         | How do you think people make applications if every database
         | call is suspectible to ddos?
        
           | EthanHeilman wrote:
           | 1. Authenticate using signed JWTs at the edge via something
           | like AWS API Gateway. 2. If the attacker is smart enough to
           | use valid JWTs from IDPs. Find the JWTs that match the DDoS
           | attack and ban those identities at the edge. This rate limits
           | the attacker to how quickly they can generate new accounts on
           | say gmail or azure. 3. If the attacker is can generate new
           | accounts fast enough, add a bloom filter to the edge of
           | accounts you have seen before the attack started.
           | 
           | At some point the attacker either gives us or just switches
           | to the brute force of flooding the pipes with so much traffic
           | that the AS doing the filtering goes down. At that point it
           | is now someone else's problem. They might start de-peering
           | the ASes generating the traffic.
        
         | tptacek wrote:
         | The article talks about when and how requests hit the database
         | in _both_ schemes, extensively.
        
       | 5cott0 wrote:
       | fine with me as long as you don't pronounce it "jay double-u tee"
        
         | chrisweekly wrote:
         | ok I'll bite; how do you pronounce it?
        
           | selectnull wrote:
           | jot
           | 
           | https://datatracker.ietf.org/doc/html/rfc7519#section-1
        
             | chrisweekly wrote:
             | Awesome, thank you!
             | 
             | > The suggested pronunciation of JWT is the same as the
             | English word "jot".
             | 
             | 1 syllable instead of 5 FTW
        
       | rendall wrote:
       | While I could agree with the sentiment "consider using opaque
       | auth tokens instead of JWTs in most use cases" I can't get behind
       | the opinion-presented-as-factual-statement tone of the article.
       | There are valid use cases to offload authentication to the client
       | rather than verifying an opaque token with every request. One
       | advantage is that community support for JWTs is large, but home-
       | grown, ad-hoc opaque token solutions not so much. Another is that
       | any claim at all can be stored in the JWT: IP address, user name,
       | opaque token, whatever makes your app secure. While these same
       | claims _could_ be stored in a database, now you have extra
       | overhead and maintenance dealing with them.
       | 
       | I would like to see an open-source project from this author that
       | uses his proposed solution.
        
         | angoragoats wrote:
         | IMHO the stateful opaque token approach is simple enough that
         | it can (and often does) get baked into whatever
         | language/framework you're using to write your app. In addition,
         | the very nature of session tokens is such that the logic for
         | what the token actually means/represents lives in your app, on
         | the server.
         | 
         | So, that may be why we don't see more "opaque session token"
         | standards/libraries out there as an alternative to JWTs.
         | 
         | But if you want an existing example, Devise for Rails [1] has
         | been around a while.
         | 
         | [1] https://github.com/heartcombo/devise
        
         | finack wrote:
         | What? Session tokens have been around for like 30 years now,
         | and every web framework worth using has a rock-solid
         | implementation; you haven't had to do it yourself since Full
         | House was putting out new episodes.
        
           | rendall wrote:
           | Binding a session to client details like IP addresses and
           | user agents requires enough fiddling that it's no longer the
           | no brainer to use session tokens over JWTs that the article
           | makes out, is my point.
        
             | angoragoats wrote:
             | By "binding a session to client details like IP addresses
             | and user agents" do you mean restricting the session so
             | that only the original IP/UA can make use of the session
             | token?
             | 
             | If so, most robust session-based authentication libraries
             | can already store this data for you (e.g. Devise's
             | `trackable` module). Actually locking down the session so
             | it can't be used by different IPs/UAs would require custom
             | code on the server, whether you're using JWTs or session
             | tokens.
             | 
             | So JWTs provide zero advantage here, unless I'm
             | misunderstanding something.
        
       | bebop wrote:
       | I would add two pros of jwts (I guess oauth 2 and oidc more
       | specifically)
       | 
       | 1. It standardizes your auth system. While sessions auth is
       | mostly implemented in the same way across systems, learning oauth
       | and oidc gives you a standard across the industry.
       | 
       | 2. Jwts give an easy path to make "front end" applications and
       | api authentication work in the same way. This in theory reduces
       | your security surface area as all of your authnz code can be
       | shared across your offerings.
        
         | andoando wrote:
         | 3. Easy to implement. 4. Dont need to hit db. 5. Can store some
         | information with the claim which is very convenient
        
           | pests wrote:
           | How do you revoke tokens if a one gets leaked without hitting
           | a db? How long are your users vulnerable to attack?
        
             | moomoo11 wrote:
             | Use low TTL and put rate limits and other mechanisms in
             | place?
        
               | jjice wrote:
               | I think you're right, but it seems like you get into a
               | tricky territory that'll never be great (as everything
               | with security has compromised). Too long is an issue for
               | attacks, but convenient for users. Too short and you have
               | to do an initial re-auth over and over again, partially
               | defeating the benefits.
               | 
               | Even if the TTL is short, there are plenty of ways to
               | compromise a token and use it immediately in an automated
               | system.
               | 
               | If you're using JWTs, I'd lean shorter TTLs and embrace
               | this as a potential concern. Not sure what the best re-
               | auth frequency is though. I'd be really interested to see
               | other's thoughts on that.
        
               | moomoo11 wrote:
               | But the token is used over SSL and the only way to get it
               | afaik is to hijack the client device or somehow hijack
               | the server. The first scenario is pretty rare and the
               | second is pretty easy to avoid. I don't think that's
               | really an edge case that's concerning for 99% of
               | applications.
        
               | pests wrote:
               | > the only way to get it afaik is to hijack the client
               | device or somehow hijack the server.
               | 
               | Yet we have millions of passwords in dumps across the
               | internet. Maybe hijacking the client or server is more
               | common than thought?
        
               | moomoo11 wrote:
               | I think you're conflating a few things.
               | 
               | Passwords being leaked is due to noobs and idiots in
               | charge of systems, and probably not using an actual auth
               | provider.
               | 
               | It really isn't that hard.
               | 
               | 1. Send secure information over SSL. If some noob-tier
               | dev decides that's too advanced for them, congrats I
               | guess for being stupid.
               | 
               | 2. Store passwords with hashing and salting. If you use
               | an auth provider, like auth0 or firebase even, they will
               | do this for you because they're not noobs. A noob-tier
               | dev who stores plaintext passwords in a database with
               | insecure connection and postgres:postgres is again stupid
               | af.
               | 
               | Most of those leaked passwords are because of this. And
               | don't think some Fortune 500 is not stupid. They
               | outsource their development to Accenture or Detoilette,
               | or any of the other outsourcers where they pay some
               | "Software Architect" $8/hr to secure banking information
               | (while charging the client $150/hr). I'm not throwing
               | shade, but these companies are cheap af and throw bodies
               | at the problem instead of experience and brains. I have
               | direct experience with this, so I know how bad it is.
               | 
               | Don't confuse people being stupid (very common, 90% of
               | any population) with a failure in a technology that
               | others who don't understand it claim makes it inferior or
               | something.
               | 
               | The internet and all the technology you use today is
               | glued together by the <10% of people who actually
               | understand computers, networking, and problem solving.
               | 
               | Just because a noob-dev uses session tokens doesn't mean
               | it will be any secure if they fail those 2 points I
               | mentioned. Those are so deadass simple to mitigate,
               | anyone who purposefully skips on them should be named and
               | shamed, and receive a 10 year cooldown from being a
               | software developer.
        
             | bebop wrote:
             | Yes, this is the trade off. If you are working in an
             | industry where you need to be highly sensitive for data
             | access even for short periods of times then oauth/oidc/jwts
             | are probably not for you. If you really need an emergency
             | escape hatch you can always rotate your singing keys and
             | jwks and invalidate all of your tokens and force everyone
             | to sign back in.
        
             | andoando wrote:
             | Good point.
             | 
             | If a short session time isnt good enough, you can use a
             | simple key store to check for revoked tokens. Youll be
             | hitting a db but its somewhat better since its just a very
             | small db of revoked tokens.
             | 
             | Its hard for me to imagine though with like a 30 min or
             | even few hour long token, under what circumstances you'd
             | actually revoke tokens. If your db got leaked, you can
             | rotate the key and invalidate all tokens. Otherwise, itd
             | have to be something like you have some post login fraud
             | detection in place. Cause jwt or not, if a user just signed
             | in and a hacker got their auth token, what are you going to
             | do? Sure you need to check the db to revoke it, but the
             | problem is how would you know the tokens been compromised?
        
               | mrkeen wrote:
               | > If a short session time isnt good enough, you can use a
               | simple key store to check for revoked tokens.
               | 
               | It's not bad solution per se, but it does negate JWT's
               | main value proposition, which is to not need such a
               | store.
        
             | xorcist wrote:
             | Another way to put it: "Do you want a (functional) logout
             | button?"
        
               | tasuki wrote:
               | That's not another way to put it. You can have a logout
               | button which makes the client forget the jwt token.
        
               | pan69 wrote:
               | But if the jwt was leaked before the client forgot it,
               | the jwt itself is still valid and can continued to be
               | used by an attacker.
        
               | stkdump wrote:
               | I wonder if an extension to the concept of jwt that
               | extends the cryptographics chain down into some hardware
               | component such as a TPM or secure enclave is the right
               | answer. Basically the payload of the token could contain
               | a pubkey for checking a signature on the request payload.
               | The logout button would then have two local effects on
               | the client side: delete the token and tell the hardware
               | component to forget the private key.
        
               | xorcist wrote:
               | Well, hence (functional). Making the client forget a
               | token sounds trivial. But there's a long tail of clients
               | out there, and many cases where things might get more
               | complicated.
               | 
               | Maybe I'm damaged from working in a regulated industry,
               | but a (possibly malicious) client who went through the
               | logout process and could prove someone else reused the
               | token after logout might have a case. Or another of a
               | myriad of unknown possibilities.
               | 
               | It's all very unnecessary. There's a reason we all used
               | to invalidate trust server side. It's just so much easier
               | to reason about.
        
               | sweetjuly wrote:
               | Why would you need to revoke on logout? Forgetting seems
               | to be enough in all cases except maybe SSO revocation
               | because in all other cases you can (and indeed often
               | must) trust the client to protect the credential.
        
               | stavros wrote:
               | Because logging out is also supposed to invalidate the
               | token so it can't be reused by anyone who may have stolen
               | it.
               | 
               | This thread is really making me despair. If you don't see
               | a problem with JWTs, you aren't experienced enough to use
               | JWTs.
        
               | sweetjuly wrote:
               | I think you need to take this a step further and really
               | define your threat model instead of being despaired :)
               | 
               | If an attacker is able to steal a victim's cookie
               | database, their system (or at the very least, their
               | browser) is already deeply compromised. It is very likely
               | that an attacker with such capabilities could prevent
               | your website from ever sending the logout request
               | (install a browser extension which blocks it, inject into
               | the render process to silently drop it, modify the cached
               | JavaScript on disk to inject code into the site, etc.).
               | The logout functionality only works insofar as you trust
               | the client, and in any circumstances where the client's
               | cookies could be stolen you really can't trust the
               | client. So logout revocation is not really a meaningful
               | security boundary.
        
               | johtso wrote:
               | How about the scenario of a stolen device that's logged
               | into the service. The victim logs in on another computer
               | to try and reset their password and lock the thief out of
               | the compromised account.
               | 
               | This can't be done without revocation.
        
               | sweetjuly wrote:
               | This is a different feature than a simple logout button
               | though (something along the lines of "sign me out
               | everywhere"), and many (smaller) websites don't actually
               | support this. As far as I can tell, the very website
               | we're on doesn't support it :) The parallel use case is
               | SSO invalidation where you may need to immediately revoke
               | access to a service after an employee is terminated.
               | 
               | You would need revocation in this case. Implementing this
               | is easier when you already have database backed tokens,
               | but unless you intend to support these features JWT is a
               | totally reasonable engineering decision.
        
               | thomasfromcdnjs wrote:
               | I feel ya.
               | 
               | You have to store invalidated tokens anywhere they might
               | pass through a service, which means you have to persist
               | them for as long you can predict that there expiry will
               | last. Simply putting them in a memory database isn't 100%
               | if that db gets flushed, and then you might start storing
               | them in a disk database, which at that point, you might
               | as well have just read the db in the first place using
               | cookie auth.
               | 
               | In microservices, you generally have to put an
               | invalidated JWT cache between every service, or
               | compromised JWT's are just floating around your intranet.
               | 
               | I've worked at a plethora of places who have JWT's who
               | have no invalidation strategy what so ever, the majority
               | of developers think that when you log out and the user
               | has forgotten the JWT then you are all good......
        
               | mgraczyk wrote:
               | You can do what Google and everyone else does, which is
               | store the revoked tokens. At scale this is easy to do
               | efficiently and rarely requires a network request since
               | the number of revoked unexpired tokens is small.
        
               | andoando wrote:
               | How does infrequentcy of revoked tokens reduce requests?
               | Dont you have to check every token to see if its revoked?
               | 
               | Or Do all the server instances store a copy of all
               | revoked tokens in memory/local db?
        
               | mgraczyk wrote:
               | All the servers can store a copy or a bloom filter
               | because the number of revoked tokens is small and doesn't
               | change often
        
             | beeboobaa3 wrote:
             | Store reset_time per user. Use a message queue (or postgres
             | notify) to _push_ changes to this value to your apps. Check
             | the user 's token was created after the reset_time when
             | validating it.
             | 
             | You would be required to keep a Map<UserId, Timestamp> in
             | memory, potentially with TTL. Most systems can handle this
             | easily for their expected user load. If not, you should
             | have the engineering capacity to figure it out ;)
             | 
             | Logout button sets reset_time to now, as does revoking
             | tokens. This would only allow you to revoke all tokens for
             | one user at the same time, but this tends to be fine, since
             | JWTs should be short-lived anyway and apps should deal with
             | the expectation of them being expired/revoked.
        
               | nosefrog wrote:
               | And hope your service hasn't been restarted so it doesn't
               | lose the in-memory revocation list?
        
               | beeboobaa3 wrote:
               | Just populate the cache when you need it? You will need a
               | database round trip for the first request per user per
               | application restart, if they haven't reset since. I
               | assumed this was obvious.
        
               | nosefrog wrote:
               | Oh, I didn't realize that the design also has a database
               | of revocations. In that case, you can just query that
               | directly :P
        
               | beeboobaa3 wrote:
               | You'll want to store your user credentials that they
               | traded for a JWT somewhere. The point of using JWTs is
               | that most of your requests don't have to hit this
               | database.
        
             | zdragnar wrote:
             | You can use a separate DB that acts more like a cache for
             | revocations- usually something where you can set a time to
             | live on the row equal to the duration of the token itself.
             | 
             | That keeps your application DB free for application load,
             | while keeping your identity validation logic nice and
             | snappy.
             | 
             | Of course, adding infrastructure may be intimidating, but
             | most applications that face any real load are going to be
             | using redis or something similar anyway at some place in
             | the stack.
        
               | akerl_ wrote:
               | If I have to run a separate DB to check for revocations,
               | why not skip JWTs and just use that separate DB for auth
               | directly.
        
               | andoando wrote:
               | Not an issue for most cases but a cache of revoked tokens
               | is going to be much smaller than a db of all users
               | tokens.
        
               | zdragnar wrote:
               | The advantage of redis or similar kv DBs / caches comes
               | in being lighter and faster than a full second database,
               | mostly.
               | 
               | The secondary advantage is you don't need to deal with
               | cookie storage, sticky sessions or anything else along
               | those lines.
               | 
               | If you're manually hand crafting a server, go for it. If
               | you're treating them like cattle not pets, going
               | stateless with a bearer token tends to be easier.
        
             | 4star3star wrote:
             | How do you find out a single token got leaked within the
             | token lifetime? If it's that easy to detect a leaked token,
             | why aren't you stopping the leak in the first place?
        
             | 015a wrote:
             | Then you can hit the db.
             | 
             | I don't understand this argument against JWTs; if
             | instantaneous session revocation is important for your use-
             | case, versus JWTs more typical 5 minute or 60 minute
             | expiration, there is nothing about JWTs which makes them
             | poor candidates for going to the database as you would a
             | session token. And, you get all the other benefits.
             | 
             | One example where this can matter: I've seen JWTs used in
             | defense-in-depth scenarios where you've got an API gateway
             | that does the initial JWT validation, including a round-
             | trip to the database to check for revocation, but then had
             | microservices behind the gateway only check the signature.
             | Traditional session keys would require a database roundtrip
             | for every validation, which could number in the dozens for
             | a single API request.
        
         | treve wrote:
         | OAuth2 has no dependency on JWT, and _most_ authentication
         | cases don't need OIDC, OAuth2 is enough.
        
         | tracker1 wrote:
         | I'd add that it's easier to abstract your software service from
         | authentication. It's allowed me to write bridge signing from
         | AzureAD, Okta etc, to a supported application deployed to
         | differing environments on client hardware and readily
         | integrating to different SSO systems.
        
       | adeptima wrote:
       | JWT is not a protocol but kind of a message format from my
       | perspective.
       | 
       | https://www.rfc-editor.org/rfc/rfc7519
       | 
       | It can be signed with HMAC SHA-256 algorithm: {"typ":"JWT",
       | "alg":"HS256"}
       | 
       | Ripping off JWT from surrounding context is a road to hell.
       | 
       | It's worth to study JWT in context of OIDC (OpenID Connect) IDP
       | providers.
       | 
       | You will quickly bump into buzzwords like client (RP), server
       | (OP), PKCE, Token Exchange, mTLS and all kind of Implicit. Hybrid
       | flows.
       | 
       | My biggest regret I didn't go through JWT related RFCs and OpenID
       | Connect specs earlier.
       | 
       | [1]: https://openid.net/specs/openid-connect-
       | core-1_0.html#CodeFl... 3.1. Authentication using the
       | Authorization Code Flow
       | 
       | [2]: https://openid.net/specs/openid-connect-
       | core-1_0.html#Implic... 3.2. Authentication using the Implicit
       | Flow
       | 
       | [3]: https://openid.net/specs/openid-connect-
       | core-1_0.html#Hybrid... 3.3. Authentication using the Hybrid Flow
       | 
       | [4]: https://openid.net/specs/openid-connect-
       | core-1_0.html#Client... 9. Client Authentication
       | 
       | [5]: https://openid.net/specs/openid-connect-
       | core-1_0.html#Refres... 12. Using Refresh Tokens
       | 
       | [6]: https://openid.net/specs/openid-connect-discovery-1_0.html
       | OpenID Connect Discovery 1.0 incorporating errata set 1
       | 
       | [7]: https://www.rfc-editor.org/rfc/rfc7523.html JSON Web Token
       | (JWT) Profile for OAuth 2.0 Client Authentication and
       | Authorization Grants
       | 
       | [8]: https://www.rfc-editor.org/rfc/rfc7636.html Proof Key for
       | Code Exchange by OAuth Public Clients
       | 
       | [9]: https://www.rfc-editor.org/rfc/rfc8693.html OAuth 2.0 Token
       | Exchange
       | 
       | [10]: https://www.rfc-editor.org/rfc/rfc8628.html OAuth 2.0
       | Device Authorization Grant
       | 
       | [11]: https://www.rfc-editor.org/rfc/rfc8705.html OAuth 2.0
       | Mutual-TLS Client Authentication and Certificate-Bound Access
       | Tokens"
       | 
       | If you dont have patience for RFCs and specs, just go play with
       | open sources IDP or better start from OpenID Connect client and
       | server library, and try to integrate it into your "hellohell" app
       | ;)
       | 
       | Very soon you will find out why developers keep building their
       | own IDPs and how simple OpenID Connect can become a full time
       | business
       | 
       | Go back here and here
       | http://cryto.net/~joepie91/blog/2016/06/19/stop-using-jwt-fo...
       | 
       | And reflect yourself
       | 
       | Trust nobody
        
       | rblatz wrote:
       | Oh man, he goes straight to stateful services as an alternative
       | to JWTs. What an absolute nightmare, if JWTs are too hard
       | stateful services are certainly more difficult.
        
         | tptacek wrote:
         | Stateful services meaning the default way every web framework
         | has structured a web application since 2005?
        
           | vundercind wrote:
           | Longer, if we allow "web application" to mean "anything with
           | a login, on the web". All those popular forums, likely also
           | stuff like yahoo mail, even gaming services (yes, browser-
           | based game matchmaking services existed in the 90s, Microsoft
           | ran one, among others) probably just because anything else
           | would have been needlessly complicated and expensive.
        
             | finack wrote:
             | Amazing how using a session ID stored in a cookie was
             | entirely possible in 2005, but is somehow out of our reach
             | with today's hardware.
        
               | kbolino wrote:
               | A simple session cookie does not protect against CSRF. In
               | 2005, session IDs were generated with low quality RNGs
               | and too few bits making them easy to guess. OWASP
               | happened for a reason.
        
               | rblatz wrote:
               | It's not hard to stand up, managing state across sessions
               | and versions of your app is hard. For example a site I
               | use frequently the Morgan Stanley portal is stateful and
               | you can only be logged in from a single device/tab at
               | once.
               | 
               | Most websites don't need it, and it makes things harder
               | to manage when rolling out new versions of your services.
               | Life got significantly easier once I moved away from
               | stateful services.
        
           | rblatz wrote:
           | Yeah, and life was tough back then. My services have gotten
           | significantly easier to manage since moving away from
           | stateful services.
           | 
           | Even at a new company I don't think I'd ever want to go back
           | to stateful sessions. I'm not close to Google or Amazon
           | scale, and managing state was significantly harder than
           | dealing with JWTs.
        
       | lvlabguy wrote:
       | You should not use JWT if you have a single application in your
       | organization. However, whenever you have multiple applications,
       | you need some form of central authentication / authorization
       | service. Otherwise, you would have to maintain auth databases in
       | each application, each application will need to be logged-in
       | separately, you won't be able to implement a simple "suspend a
       | user's accounts after X unsuccessful auth attempt", you won't
       | have a central auth log.
        
       | zemo wrote:
       | External services use jwts pretty often, so if you have to handle
       | jwts anyway, using jwts means that there's only one primitive,
       | set of libraries, and concepts for your devs to know.
       | 
       | "You don't need all of that!" sure but you probably already
       | _have_ it somewhere in your codebase and it 's pretty universal.
       | You also probably don't utilize every feature of http itself,
       | that isn't a cogent argument against using http.
       | 
       | JWTs are supported by a large number of tools, libraries,
       | middleware appliances, etc; there's a huge ecosystem out there to
       | support it.
       | 
       | You also might delegate auth to a third party like Auth0 or
       | FusionAuth so that you don't handle _any_ PII, because all of the
       | PII is handled by a vendor, and you only store application-
       | specific data.
       | 
       | "You want to implement logout" means a few things; in most apps
       | you just ... have the client forget the token and you go about
       | your day and it's fine. "but what about if a nefarious actor
       | stole the token!!!" you might say, but hand-rolled session tokens
       | have the same problem.
       | 
       | "You want to turn off access for all users" is something you can
       | do in http middleware; e.g., I have used middleware that do
       | things like "only allow through requests that have jwts with the
       | 'admin' role in their claims because we have turned off the
       | system from users for downtime", and that works fine.
       | (specifically I wrote a traefik plugin to do this in an
       | afternoon).
       | 
       | "You want to ban a single specific user really quickly" is a
       | thing JWT won't do out of box.
        
         | tptacek wrote:
         | The problem isn't the mechanisms inside of JWT (though they are
         | gross and worth avoiding on their own), it's the systems-level
         | tradeoffs you have to make to use them idiomatically,
         | particularly around refresh tokens and revocation.
         | 
         | If you read this and think "this doesn't apply as long as I
         | have to use JWTs for some service I rely on, anyways", you
         | missed its point.
        
           | zemo wrote:
           | refreshing the refresh token is always a database hit, and in
           | the case of using a third party like Auth0 or FusionAuth, you
           | can invalidate refresh tokens at the individual and at the
           | user level. Saying "the refresh token is the real token" as
           | the article does is pretty misleading; the refresh token
           | always goes to the same system that would accept the
           | username/password, but the jwt itself gets carted around to
           | other systems. So again, in the auth0/fusionauth case, the
           | refresh token is sent to auth0/fusionauth, not your app, so
           | even without any particular knowledge of what's going on, the
           | application developer is forced to utilize them in different
           | ways. There's a big assumption in this article that you're
           | talking about a monolithic system where logins are processed
           | by the same application that handles all requests. Even if
           | you _do_ prefer to structure your application as a monolith
           | and avoid microservices, once you delegate auth to a third
           | party or a system separate from your app, the bearer token
           | versus refresh token thing starts to matter a _lot_.
           | 
           | I think there's a cyclic thing that's been happening for
           | _years_ where people in the security community like to talk
           | about how bad jwt is, but then not produce anything that
           | meets application developers needs in a meaningful way. I
           | spent _years_ avoiding jwt and ultimately found avoiding jwts
           | wasn 't actually a good use of my time.
        
             | tptacek wrote:
             | This still doesn't engage with the point. "Refresh tokens"
             | are not a natural feature of every session scheme. They're
             | required by stateless JWTs because JWTs are motivated by
             | migrating authN into its own independently-scaled
             | microservice, and because online revocation is difficult in
             | stateless schemes. If you just use your framework session
             | system, you don't ever think about refresh tokens. That's
             | the point the article is making. It's not that JWT makes
             | refresh harder, it's that it makes it a thing at all.
        
               | zemo wrote:
               | right so then the argument is less about "should you use
               | JWTs" and more about "should you use stateless session
               | tokens".
               | 
               | > JWTs are motivated by migrating authN into its own
               | independently-scaled microservice
               | 
               | that's definitely one use-case, although I don't actually
               | think having auth in a separate microservice under your
               | own custody is the dominant use-case. The auth being in
               | an entirely separate database means _PII_ is in an
               | entirely separate database, which can be a useful access
               | control mechanism, or in the case of using a third-party
               | auth service, means that _there is no PII in databases
               | under your custody at all_. It makes the situation of
               | "developers can access all data created by the software
               | that they work on" really easy to implement while also
               | maintaining "developers do _not_ have access to all of
               | the PII " as an access barrier. I think "I just use
               | Auth0/FusionAuth and don't think about it" is actually
               | the dominant use-case, and every third party offering
               | that kind of developer experience utilizes stateless
               | session tokens to make that happen.
               | 
               | > If you just use your framework session system, you
               | don't ever think about refresh tokens.
               | 
               | right, but now the problem of keeping PII data access
               | rules separate from your application domain data is an
               | additional thing you have to engineer and think about
               | securing, so I think the article is underestimating some
               | of the negatives of that tradeoff; I've never seen a
               | framework with a built in session system that did a good
               | job of keeping access to the PII separate from the
               | application data.
        
               | tptacek wrote:
               | You wrote a comment upthread that misapprehended the post
               | you were critiquing. I was motivated to offer some
               | corrections. I'm less interested in the philosophical
               | argument you now propose, except to say that PII
               | segregation has only very rarely been the reason I've
               | seen people adopt stateless tokens. It is _still_ easier
               | to segregate data using stateful token schemes than with
               | JWT.
               | 
               | But I don't want to pretend we're still having the same
               | conversation that started up thread. I assume you take my
               | point, that if you think "I already have to do JWT so I
               | don't save anything by not using them everywhere" rebuts
               | the post, you've misread it a bit.
        
               | zemo wrote:
               | > if you think "I already have to do JWT so I don't save
               | anything by not using them everywhere" rebuts the post
               | 
               | I have never believed that and I don't think my comment
               | ever suggested I did, I mentioned "you might have jwt-
               | handling tooling already" as one concern among many, not
               | an entire argument. It really seems like you've honed in
               | on a single point and, as you would say, misapprehended
               | my comment. That one argument is not reason alone to use
               | JWT and I really don't think my original comment ever
               | implied I thought it was the entirety of the topic.
        
             | jupp0r wrote:
             | My impression is that the article author is following the
             | good old "everything lives in my Rails monolith" philosophy
             | from 10 years ago where login and authn is just another
             | library including db migrations that you slap onto your
             | monolith to get user management set up in 15 minutes.
        
               | zemo wrote:
               | in all fairness that's what 95% of web application
               | projects really should be.
        
               | jupp0r wrote:
               | 70% of the web is Wordpress, so yeah.
        
               | Aeolun wrote:
               | 95% of the non-wordpress part of the web.
               | 
               | We're rebuilding our microservice hell as a monolith
               | anyway.
        
           | notnullorvoid wrote:
           | Having authn stateless whether with JWTs or not is a bad
           | idea. By extension refresh tokens are a bad idea. Doesn't
           | mean JWTs are a bad idea, if used as a general auth token
           | they are fine. Implementing revocation of JWTs also isn't
           | very hard but you need somewhere to store the revocation
           | state.
        
             | tptacek wrote:
             | Sorry, none of this is responsive to what I just wrote. I
             | had a particular complaint about the critique I responded
             | to, that's all.
        
           | Aeolun wrote:
           | I'm not sure what you are trying to say here? That dealing
           | with refresh token expiry revocation for a single system (and
           | two sessions) is better than dealing with expiry and
           | revocation for 3?
           | 
           | I see some strong arguments for standardizing on a single
           | one.
        
       | firefoxd wrote:
       | Some people made the distinction here, jwt on the front end vs
       | the micro services across the network.
       | 
       | I've experienced more than once, issues where the auth service
       | has bugs and the logged out session is still valid for a long
       | time. Or an attacker that figured out the micro services
       | blueprint and now had authed access to the entire network.
       | 
       | Jwt is still useful between services, however the front end can
       | do just fine with a session id that can be easily revoked.
        
       | Galanwe wrote:
       | The article misses the point of JWT: it's dead simple to
       | implement.
       | 
       | I don't implement JWT because I fancy big data terascale
       | technologies. I do it because it's mostly stateless, meaning it's
       | very easy to mock locally, and easy to deploy.
       | 
       | > You wanted to implement log-out, so now you're keeping an
       | allowlist of valid JWTs, or a denylist of revoked JWTs. To check
       | this you hit the database on each request.
       | 
       | No, you just remove the JWT from the local storage. Sure, the
       | user could then relogin if he copied it, but if it's on his own
       | will, why not. And if he got his JWT stolen before logout, bah
       | anyway any token could have been stolen that way, whatever the
       | tech.
       | 
       | > You need to be able to block users entirely, so you check a
       | "user active" flag in the database. You hit the database on each
       | request.
       | 
       | Right but that contradicts the whole premise of the article,
       | being that you probably don't need fancy features. 99.9% of
       | websites don't need to ban users _instantly_. If you have a fair
       | JWT expiration, it's usually OK.
       | 
       | I'd argue for the exact opposite of the article. If you want dead
       | simple Auth, without fancy tech, just use JWT.
        
         | CiPHPerCoder wrote:
         | > The article misses the point of JWT: it's dead simple to
         | implement.
         | 
         | Implementing it _securely_ , however, is far from dead simple.
         | 
         | https://scottarc.blog/2023/09/06/how-to-write-a-secure-jwt-l...
        
         | pjerem wrote:
         | > And if he got his JWT stolen before logout, bah anyway any
         | token could have been stolen that way, whatever the tech.
         | 
         | The thing is, with the good old "token in the database" method,
         | logging out means deleting the token from the database so no,
         | you can't reuse a stolen token after a logout.
        
           | gkbrk wrote:
           | If the user can somehow get their token stolen from the
           | browser, they can also get their username/password stolen
           | from the browser.
        
             | sweetjuly wrote:
             | Plus, a compromised browser could just block the logout
             | request. It's a nonsense and indefensible threat model. If
             | you need to worry about compromised browsers, you need a
             | mitigation which doesn't rely on the browser or that'd be
             | easily compromised by someone who could compromise the
             | browser.
             | 
             | It's a slightly different story with a "logout all
             | sessions" button where the user might press it from a
             | trusted device, but that's a different functionality than a
             | logout button. There you would need revocation, but if you
             | (like many websites) don't actually support this
             | functionality you really don't even need to support a
             | logout functionality more complex than just asking the
             | client nicely to please delete the token.
        
         | adhamsalama wrote:
         | What if you want to implement logging out all user sessions?
         | 
         | You'd have to store something somewhere, so you end up losing
         | the benefits of stateless authentication.
        
           | dudus wrote:
           | That's a fancy feature. The article is all about sticking to
           | what is simple and without fancy features
        
             | finack wrote:
             | No, it's a necessary feature if your credentials get
             | stolen, which wouldn't surprise me considering how many
             | people just cram JWTs into local storage, which is not the
             | same as a secure cookie and has weaker security. Either
             | you're setting the expiry time real low (which still won't
             | stop someone who has four minutes 59 seconds left to wreck
             | your stuff, because computers are fast), or you're
             | maintaining a blacklist, which means, congratulations,
             | you've just reinvented overly-complicated session tokens.
        
           | redman25 wrote:
           | You could probably rotate the secret which would invalidate
           | all existing sessions.
        
       | menthe wrote:
       | Everyone's talking about how you MUST hit the database for
       | revocations / invalidations, and how it may defeat the purpose.
       | 
       | How is no one thinking of a mere pub-sub topic? Set the TTL on
       | the topic to whatever your max JWT TTL is, make your applications
       | subscribe to the beginning of the topic upon startup, problem
       | solved.
       | 
       | You need to load up the certificates from configuration to verify
       | the signatures anyways, it doesn't cost any more to load up a
       | Kafka consumer writing to a tiny map.
        
         | akerl_ wrote:
         | So, basically a database where you store a replica in memory on
         | every edge node.
        
           | ak217 wrote:
           | Not really. A pub/sub bus cluster (or PaaS) is pretty
           | different from a database.
        
             | akerl_ wrote:
             | I'm not talking about the pub sub cluster.
             | 
             | If you have a pub sub cluster that you push revocation
             | details into, and running servers subscribe to that feed
             | and then track a rolling list of tokens that would
             | otherwise still be active but have been revoked, you are
             | effectively storing a revocation database on every edge
             | node.
        
               | ak217 wrote:
               | Again not really. If the pub/sub broker is persistent,
               | you don't have to persist the revocation list on edge
               | nodes. And just pointing out that it's a db in some loose
               | sense of the word doesn't help with the actual challenge
               | of organizing the flow of data reliably in a federated
               | system (i.e. one that can't share a single database).
        
         | marcosdumay wrote:
         | For maximum scalability you'd want a bloom filter at each
         | service for testing the token, and some central revocation
         | lists where you go test the token that fail this.
         | 
         | But this is way overkill for anybody that isn't FAANG, and it's
         | probably overkill for most of FAANG too. On normal usage, it's
         | standard to keep the revocation filter centralized at the same
         | place than handles renewals and the first authentication. This
         | is already overkill for most people, but it's what comes pre-
         | packaged.
        
       | meling wrote:
       | A former student of mine (Vera Yaseneva) redesigned our old auth
       | architecture using jwts and I'm pretty happy with how it turned
       | out. Maybe it is overkill for our simple autograder server, but
       | it was fun getting it to work and I'm sure it is more secure than
       | the old architecture which had many many flaws... it was a
       | maintenance nightmare for years. After the redesign it has been a
       | breeze. Here is the project
       | https://github.com/quickfeed/quickfeed
       | 
       | The security arch is mainly in web/auth and web/interceptor
       | packages if anyone is interested in learning from the code. It
       | uses connectrpc, which has a nice interceptor arch.
       | 
       | Happy to share Vera's thesis report if anyone is interested...
        
         | hu3 wrote:
         | Thank you for sharing! I took a look at the code. Looks clean.
         | 
         | Please share her thesis. I'm interested.
        
           | meling wrote:
           | Here it is https://uis.brage.unit.no/uis-
           | xmlui/bitstream/handle/11250/3...
        
             | hu3 wrote:
             | Thank you! I'm looking to write a paper about a software
             | I'm developing and this will help immensely since it's a
             | fitting format.
        
       | phendrenad2 wrote:
       | I'm thankful every day I don't have to work on a team where JWTs
       | are a valid solution, and nobody suggests them regardless. What a
       | nightmare.
        
       | dathinab wrote:
       | It's the wrong question.
       | 
       | It's like asking what language should I use for programming a
       | game? A common question, but of which the answer isn't a language
       | choice. Instead it is that you approach the problem from the
       | wrong direction.
       | 
       | The more useful question is what framework/service/library do I
       | use for handling that, and then you use whatever it uses. Which
       | most times means use whatever your web framework provides (as the
       | blog author concluded).
       | 
       | The next question is what do you concretely hope to get from
       | doing so and are there easier ways to get it (likely yes, and not
       | some vague "but statles is better argument").
       | 
       | Then ask yourself how do you handle revocation? A question which
       | is essential when using JWT and other stateless auth tokens (a
       | answer of idk./I will think later about don't count. I don't can
       | be a valid answer, but only very very rarely).
       | 
       | I think it's not an understatement to say that the huge majority
       | of custom JWT usage falls under harmful premature optimization.
       | 
       | I wrote "custom" because sometimes you don't have a choice, e.g.
       | you need to use OIDC for social login as the main form of AuthN &
       | AuthZ and then already have some service (not just library) which
       | fully handles OIDC/JWT including revocation for you (i.e. you
       | don't validate the JWT stateless but ask the service every time).
       | Through that approach (especially the later part) can have
       | scaling limits but, eh, we are back at premature optimization ;)
        
       | bakugo wrote:
       | The beauty of JWTs is that, if you have to ask "do I need JWTs?",
       | you probably don't need JWTs.
        
       | jonplackett wrote:
       | I'd like to hear a reply from someone at Supabase
        
       | Aldipower wrote:
       | Fair enough, access token/refresh token pairs all have those
       | issues described in the article. But why hating against JWTs
       | (pronounced 'JOT' btw) in general? There are other stateless
       | techniques making use of a JWT, which are very easy and secure to
       | implement. For examples the single auth token approach, with
       | maybe a 2 days expiry and a renew window if the user is active.
       | For some scenarios this is perfectly fine, it is stateless and
       | has no refresh token. User logs out by just deleting the token
       | client side.
        
         | oxidant wrote:
         | Not saying you're wrong about the pronunciation, but "jot" is
         | the suggested pronunciation, not the required one [0]. I used
         | "jay double-you tee" for a long time and it doesn't bother me
         | how people pronounce it.
         | 
         | [0] https://datatracker.ietf.org/doc/html/rfc7519#section-1
        
       | jupp0r wrote:
       | "Just use the normal session mechanism that comes with your web
       | framework and that you were using before someone told you that
       | Google uses jwt. It has stood the test of time and is probably
       | fine."
       | 
       | You don't need to be Facebook or Google to have more than one
       | service in your infrastructure that needs to authenticate a
       | user's existing session without forcing the user to log in again.
       | Sharing the session across multiple services is its own
       | distributed systems problem with numerous security implications
       | to be aware of and bearer tokens might be a good alternative.
       | 
       | If all you have is a single monolith web app that is the identity
       | provider, makes all authentication decisions etc then yes, you
       | don't need JWTs probably. There is a huge gap between that and
       | being Google/Facebook.
       | 
       | Apart from that, Google and Facebook don't even use JWTs between
       | the browser and backends after the initial login but actually do
       | have some sort of distributed session concept last time I
       | checked.
        
         | _heimdall wrote:
         | This is a perfect example of "it depends" being the right
         | answer.
         | 
         | Should a project use sessions or JWTs? One isn't right or
         | wrong, it all depends on the context of the project.
        
           | wokwokwok wrote:
           | I mean, to be fair, the article literally calls out a fairly
           | reasonable checklist.
           | 
           | Do you maintain a database of JWT session tokens for refresh
           | and revoke?
           | 
           | Do you have a real session that you load for every user every
           | request _anyway_?
           | 
           | If the answer is 'yes', then the answer to 'use JWT' isn't
           | 'it depends'.
           | 
           | It's no.
        
             | _heimdall wrote:
             | The author here seems to be arguing that you should
             | effectively never use JWTs. That, in my opinion, is a
             | mistake.
             | 
             | JWTs have absolutely been over-hyped for the last 8-10
             | years, but they do have a use and you don't have to be at
             | the scale of Google for it to be the right approach.
             | 
             | Software isn't as simple as creating a checklist of a few
             | basic categories and saying there is always a right or
             | wrong answer. The answer should be "it depends" because
             | there are many more factors at play when deciding something
             | as fundamental as authentication and authorization.
        
               | meindnoch wrote:
               | Ok, then show us an example scenario where using JWTs
               | makes sense, while the author's checklist says it
               | doesn't.
        
               | _heimdall wrote:
               | I may not have been clear there. I'm not saying the
               | author's list is wrong, I'm saying its incomplete.
               | 
               | For starters, the blog post leads with the premise that
               | the answer is always "No". Even ignoring that as playful
               | banter, the checklist is way to broad to be universally
               | true.
               | 
               | > You wanted to implement log-out, so now you're keeping
               | an allowlist of valid JWTs, or a denylist of revoked
               | JWTs. To check this you hit the database on each request.
               | 
               | > You need to be able to block users entirely, so you
               | check a "user active" flag in the database. You hit the
               | database on each request.
               | 
               | > You need additional relationships between the user
               | object and other objects in the database. You hit the
               | database on each request.
               | 
               | > Your service does anything at all with data in the
               | database. You hit the database on each request.
               | 
               | These all assume that I authorization data _and_ that the
               | user data lives in the same database. I may store my own
               | user object with a unique ID that references an
               | authenticated user, while the author flow is actually
               | managed by an entirely different service (whether first
               | party or third party). I also may have my user data
               | stored in a separate database, say for legacy reasons or
               | data security /integrity requirements. Not every database
               | is crested equal, its totally possible that one database
               | is a better fit for user data while a totally different
               | database is needed for application data.
               | 
               | Coming up with a list of broad scenarios and claiming
               | that if you check any one there is _never_ a reason to
               | pick JWTs over sessions is misguided and overly
               | simplistic. Context _always_ matters.
               | 
               | edit: formatting
        
               | meindnoch wrote:
               | >These all assume that I authorization data and that the
               | user data lives in the same database
               | 
               | Please tell us how the following assumes that
               | authorization data and user data lives in the same
               | database:
               | 
               | "You wanted to implement log-out, so now you're keeping
               | an allowlist of valid JWTs, or a denylist of revoked
               | JWTs. To check this you hit the database on each
               | request."
        
               | _heimdall wrote:
               | It doesn't have to assume that, again because _context
               | matters_.
               | 
               | If authorization data is stored outside my database, I
               | may not store _anything_ related to that user other than
               | their unique ID. I may not know anything about the JWTs
               | because a middleware talking to the external
               | authorization service authorizes a request before I ever
               | query my database.
               | 
               | I could also store valid JWTs in my own database or in a
               | caching layer, say with redis for quick access. There's
               | nothing wrong with that, but without more details on
               | _what_ someone is building and what it needs to be
               | designed for there is no absolute right or wrong answer.
        
               | meindnoch wrote:
               | I don't see how anything you said would refute OPs
               | premise, that if you want proper logout [1] in your app,
               | you can't use JWTs without constantly querying whether a
               | particular JWT should be accepted or not, from which it
               | follows that this query mechanism may as well be a
               | traditional database with a "sessions" table.
               | 
               | How you organize your data, your services, your
               | middleware, etc. does not change this the slightest,
               | hence are irrelevant.
               | 
               | ----
               | 
               | [1] proper logout is instant (this invalidates the "just
               | use a JWT with short timeout!" argument), and prevents
               | session stealing (this invalidates the "just throw away
               | your JWT to logout" argument)
        
         | Aurornis wrote:
         | > You don't need to be Facebook or Google to have more than one
         | service in your infrastructure that needs to authenticate a
         | user's existing session without forcing the user to log in
         | again.
         | 
         | Thank you. This middle ground between hyperscaler
         | infrastructure and super simple web apps is where most of my
         | career has been spent, yet the recent trend is to pretend like
         | there are only two possible extremes: You're either Facebook or
         | you're not doing anything complicated.
         | 
         | It has an unfortunate second order effect of convincing people
         | that as soon as they encounter something more complicated than
         | a simple web app, they need to adopt everything the
         | hyperscalers do to solve it.
         | 
         | I wish we could spend more time discussing the middle ground
         | rather than pretending it's some sort of war between super
         | simple or ultra complex.
        
           | rescbr wrote:
           | Specially since the middle is way larger than people think.
        
           | kelnos wrote:
           | I still don't think people in the middle need JWTs.
           | 
           | If we're talking about a web session, time-limited randomly-
           | generated session tokens that are stored in a DB still work
           | fine. If you really need it, put a caching layer (memcached
           | or redis or valkey or whatever) in front of it. Yes, then
           | you've created cache invalidation problems for yourself, but
           | it's still less annoying than JWT.
           | 
           | If we're talking about authenticating API requests, long-
           | lived randomly-generated auth tokens stored in a database
           | work fine, generally. (But allow your users to create more
           | than one, and make rotation and revocation easy. Depending on
           | your application, allowing your users to scope the tokens can
           | also be a good thing to do.) Again, put a caching layer in
           | front of your database once you get to the scale where you
           | need it. You probably won't need it for a while if you're
           | sending your reads to read-only replicas.
           | 
           | (Source: worked at Twilio for 10 years; we definitely
           | eventually ran into scaling problems around our user/auth DB,
           | and our initial one-auth-token-is-all-you-need setup was
           | terrible for users, but these problems were fixed over time.
           | Twilio does use JWTs for some things, but IMO that was
           | unnecessary, and they created more headaches than they
           | solved.)
           | 
           | I'm not saying no one ever needs JWTs, but I think they're
           | needed in far fewer circumstances than most people think,
           | even people who agree that JWTs should be looked upon with
           | some skepticism. If you need to be able to log people out or
           | invalidate sessions or disable accounts, then JWTs are going
           | to create problems that are annoying to solve.
           | 
           | (One possibly-interesting solution for JWT-using systems that
           | I haven't tried anywhere is to do the reverse: don't cache
           | your user/auth database, but have a distributed cache of JWTs
           | that have been revoked. The nice thing about JWTs is that
           | they expire, so you can sweep your cache and drop tokens that
           | have expired every night or whenever. Not sure how well this
           | would work in practice, but maybe it's effective. One big
           | problem is that now your caching layer needs to be fail-
           | closed, whereas in a system where you're caching your
           | user/auth DB, a caching layer failure can fall back to the
           | user/auth DB... though that may melt it, of course. I also
           | feel like it's easier to write logic bugs around "if this
           | record is not found, allow" rather than "if this record is
           | not found, deny".)
        
             | crabmusket wrote:
             | > But allow your users to create more than one, and make
             | rotation and revocation easy
             | 
             | It's shocking how often this advice isn't followed. We
             | often see it with non-tech companies who nonetheless
             | deliver services over the internet.
        
             | MrDarcy wrote:
             | > If we're talking about a web session, time-limited
             | randomly-generated session tokens that are stored in a DB
             | still work fine
             | 
             | This works fine for a single service but you're replying to
             | a thread about the middle ground of multiple services. It's
             | an anti pattern to have every service talk to the same
             | database just to authenticate every request.
             | 
             | By the time you add a caching layer you're truly better off
             | using an off the shelf oidc id provider and validating the
             | id token claims.
        
               | a_random_canuck wrote:
               | In my experience for medium sized services it's still
               | better to have everything talk to the same authentication
               | database.
               | 
               | Postgres has insanely good read performance. Most
               | companies and services are never going to reach the scale
               | where any of this matters, and developer time is usually
               | the more precious resource.
               | 
               | My advice is always, don't get your dev team bogged down
               | supporting all this complicated JWT stuff (token
               | revocation, blacklisting, refresh, etc) when you are not
               | Facebook scale / don't have concrete data showing your
               | service really truly needs it.
        
               | j16sdiz wrote:
               | +1
               | 
               | For mostly-read flow like authentication, a centralized
               | database can scale really well. You don't even need
               | postgres for that.
               | 
               | If you have mutable state, JWT can't help you anyway.
               | 
               | JWT start make sense only when you are doing other
               | hyperscaler stuffs and you can reuse part of those
               | architecture
        
               | PaulHoule wrote:
               | Funny, people used systems like JWT in the late 1990s.
               | Back then you couldn't really trust the session mechanism
               | in your language because inevitably these had bugs and
               | would toss your cookies for "no reason at all".
               | 
               | I was inspired by https://philip.greenspun.com/panda/
               | circa 2001 to develop a complete user management
               | framework based on that kind of cookie which had the
               | advantage over other systems that the "authentication
               | module" it took to get authentication working in a new
               | language was maybe 40-100 lines of code. Software like
               | PHPNuke that combined second or third rate
               | implementations of apps all in the same codebase was the
               | dominant paradigm then, the idea that you could pick
               | "best of breed" applications no matter what language you
               | were using was radical and different.
               | 
               | I used the framework for 10+ projects, some of which got
               | 350,000+ active users. As an open source project it was a
               | complete wash. Nobody got interested in user management
               | frameworks (as opposed to writing your own buggy,
               | insecure and hard-to-use auth system in a hurry) until
               | around 2011 or so when frameworks based on external
               | services all of a sudden popped up like mushrooms. Seemed
               | like the feature I was missing was "needs to depend on an
               | external service that will get shut down with the vendor
               | gets acquired"
        
               | stickfigure wrote:
               | Alternatively, just don't worry about token revocation
               | and all that complicated stuff? So you have a window of 5
               | minutes (or whatever your access token expiry is) that
               | you can't revoke - is that a big deal?
               | 
               | A simple JWT implementation isn't that complicated, but
               | you have to accept some limitations.
        
               | andrewmcwatters wrote:
               | > It's an anti pattern to have every service talk to the
               | same database just to authenticate every request.
               | 
               | Bullshit.
        
               | mynameisvlad wrote:
               | Do you want to expand on that? Because having a single
               | point of failure certainly seems like a horrible practice
               | when that single point goes down.
        
               | andrewmcwatters wrote:
               | You're already talking to stateful systems to do anything
               | meaningful. A in-memory cache on top of session retrieval
               | is so trivial and adds so few microseconds that it's
               | imperceptible even at large volumes of traffic.
               | 
               | If you're having trouble with that, you've got bigger
               | issues. Any regular work queries will take longer, and so
               | it's not even a meaningful area of concern if you broke
               | down a request from end to end on a flame graph.
        
               | mynameisvlad wrote:
               | > You're already talking to stateful systems to do
               | anything meaningful.
               | 
               | Yeah, so? They don't have to be talking to the _same_
               | system, and in fact it was _literally_ what you called
               | bullshit to originally.
               | 
               | > If you're having trouble with that, you've got bigger
               | issues.
               | 
               | That does absolutely nothing to changing the fact that a
               | SPoF is _still_ an anti-pattern that should be avoided.
               | 
               | For that matter...
               | 
               | > A in-memory cache on top of session retrieval is so
               | trivial and adds so few microseconds that it's
               | imperceptible even at large volumes of traffic.
               | 
               | Also does absolutely nothing to change that fact. You
               | have done nothing to _actually_ elaborate on why it's
               | totally not a horrendous idea to have everything
               | communicate to the same database. Just because there's a
               | caching layer does not mean that fresh data wouldn't be
               | available if a SPoF goes down, which, once again, is the
               | whole point here.
        
             | jupp0r wrote:
             | "have a distributed cache of JWTs that have been revoked.
             | The nice thing about JWTs is that they expire, so you can
             | sweep your cache and drop tokens that have expired every
             | night or whenever."
             | 
             | Every cache has TTL, so you just set the TTL of the entry
             | to the expiration date of the token you are caching. No
             | need for nightly cleanups.
        
               | rezonant wrote:
               | I'm not sure cache was the right word in the parent
               | post-- you don't want to use a cache (at least one with
               | LRU/bounded size) to store revocation without a backing
               | store, or else the revocation could get pushed out of the
               | cache and become ineffective. The backing store (likely a
               | DB) would require such cleanups once the revocation
               | record is no longer relevant.
        
               | sturgill wrote:
               | Potentially you take both at once: use something like
               | DynamoDB as the storage layer that also supports TTL
               | natively.
        
               | jupp0r wrote:
               | I would challenge your assumption. Unless you absolutely
               | need to have 100% durable, consistent revocations for
               | some reasons, something like memcached is perfect here as
               | the worst case scenario in case of a failure is a slight,
               | temporary degradation in security without any visible
               | user impact or operations nightmare (ie restoring
               | backups). This assumes that your token lifetime is
               | reasonably short (at least for access tokens), refresh
               | tokens are a different story but only need to be tracked
               | at the authn service, not globally.
        
               | rezonant wrote:
               | If the revocation use case is soft, then totally fair.
               | But if the application is potentially dangerous and the
               | user says "Sign out all devices", I think that should be
               | a deterministically successful operation. Similarly, if
               | there is a compromised account in an organization, I'd
               | like to be confident that revoking all credentials was
               | successful.
               | 
               | Revocation of tokens can be done for a simple logout
               | operation, in which case the stakes are low, but more
               | often it is the "pull the fire alarm and get that user
               | out", and in that case it should be reliable.
        
             | jupp0r wrote:
             | "If we're talking about a web session, time-limited
             | randomly-generated session tokens that are stored in a DB
             | still work fine. If you really need it, put a caching layer
             | (memcached or redis or valkey or whatever) in front of it.
             | Yes, then you've created cache invalidation problems for
             | yourself, but it's still less annoying than JWT."
             | 
             | You just (somewhat handwavingly) described what Google and
             | Facebook are doing. You might not need to build this
             | globally highly available distributed session store, JWTs
             | might be an ok solution for your use case too (because you
             | are not Google or Facebook) - or not. It depends on what
             | your requirements are. AuthN across services is somewhat
             | complex in any case, I don't think there is an easy way
             | around it without making tradeoffs somewhere. JWTs are a
             | great tool to consider here.
        
             | makeitdouble wrote:
             | As you point out, in most use cases a random token will be
             | fine and it all comes down to how and where it is stored.
             | 
             | But that also means that you can have JWTs that are used as
             | "random token" for most of your app, cost to produce them
             | isn't high, and only make use of the additional capacities
             | for instance when
             | 
             | - when you want to check signatures (e.g. reject before
             | hitting your application layer)
             | 
             | - store non sensitive base64 data that you want before
             | restoring the session
             | 
             | Creating and handling JWT is only as costly and complicated
             | as you want it to be, so there's IMHO enough flexibility to
             | have light use with very few penalities for it.
        
             | bcrosby95 wrote:
             | Why not just stick your auth token in the cache. It's
             | supposed to expire anyways.
             | 
             | Back in the day we used memcached for our primary store for
             | all sorts of ephemeral things. Including user sessions.
        
               | withinboredom wrote:
               | Items are evicted from caches all the time for non-
               | expired reasons. Memcached, in particular has "slabs"
               | (spaces for objects of a certain size) and once those
               | slabs are full, items are evicted to make space for new
               | items.
        
             | torginus wrote:
             | Honestly, do you even need support for revoke? If you have
             | a token whose lifetime can be measured in 2-3 minutes, I
             | don't think the abuse potential is huge, especially when
             | some other security measures are in place.
             | 
             | Thing is, token refresh service can be stateless, but
             | adding a revoke service basically kills JWTs main
             | advantage, since every time we check its validity, we need
             | a query to see if its been revoked.
        
               | nine_k wrote:
               | Revocation is needed because you want to disable access
               | to an intruder in the very second you detect unauthorized
               | access using a stolen token. Same for certain kinds of
               | banned users who must lose access immediately.
               | 
               | But since such a revocation list is going to be short
               | (usually 0 entries, dozens at worst), it's trivial to
               | replicate across all the auth service nodes (which can as
               | well be worker nodes) or keep it in Redis replicated per
               | DC, with sub-millisecond lookup times.
               | 
               | Things get harder if you want a feature like logging out
               | other sessions, or just an explicit logout on a shared
               | computer (think about business settings: stores,
               | pharmacies, post offices), you may have to have larger
               | revocation lists. This may still not be a problem: a
               | million tokens is a few dozen megabytes, again, a per-DC
               | replicated Redis cluster would handle it trivially and
               | very cheaply.
        
               | torginus wrote:
               | I still feel like the need for revocation kills the
               | simplicity of JWT and thus the reason for its existence.
               | 
               | I'm of a more gradual opinion regarding this - say you
               | operate a movie streaming service and control access to
               | movies via JWT. It's not a problem if an attacker has
               | access for two more minutes than intended.
               | 
               | If you are talking to a single client, I think checking
               | the remote IP address and encoding it in the token might
               | work to see if the token is not stolen, but don't quote
               | me on that.
        
               | g15jv2dp wrote:
               | It's a complicated problem. I don't see why it should
               | have a simple solution.
        
               | stickfigure wrote:
               | > Revocation is needed because you want to disable access
               | to an intruder in the very second you detect
               | 
               | I get that this has conceptual appeal, but I doubt this
               | makes any difference in real life. Unless you have some
               | very sophisticated infrastructure, it takes many minutes
               | to discover the issue and then many more minutes even to
               | decide what to do about it. A few extra minutes to cut
               | off access is probably not going to make a big difference
               | one way or another.
        
               | nine_k wrote:
               | An intruder might be not a sophisticated black hat
               | hacker. It could be somebody who picked up an unlocked
               | phone or keyboard.
               | 
               | When I had a chance to design a token-based authn/authz
               | system, we had two types of tokens, general access (with
               | hours of expiration, mostly read-only access) and
               | privileged access, with expiration time set to a minute
               | or so. All auto-refreshed on use, all separately
               | revokable.
        
               | withinboredom wrote:
               | All you really need for revocation in a revocation
               | service are two fields: user id + inb (issued not before)
               | and a bloom filter.
               | 
               | To revoke a token:
               | 
               | 1. issue a new token to the revoker that is issued at
               | current time (if business rules require revoker to be
               | logged in).
               | 
               | 2. set user inb to current time - 1 second with a TTL of
               | longest issue * 1.5.
               | 
               | 3. Add user to bloom filter.
               | 
               | 4. upload bloom filter to s3, every service downloads
               | this every 5 minutes.
               | 
               | 5. Then on request, check bloom filter. If the user id is
               | in the bloom filter, check with revocation service that
               | inb > issued time.
               | 
               | This is probably less than five hundred lines of code and
               | pretty easy to maintain.
        
               | littlecranky67 wrote:
               | Often overlooked middle-ground that vastly simplifies
               | your revocation logic: Just have a single field "not-
               | issued-before" timestamp assigned to each user account.
               | Instead of revoking a single token, you have a "log out
               | from all devices" logic - i.e. you revoke all tokens at
               | once based on their "iat" claim (issued at). No need for
               | revocation lists alltogether, you just make sure any
               | tokens "iat" is never before the "not-issued-before"
               | associated with the user. Sure, this is not as perfect UX
               | as being able to revoke individual tokens, but token
               | revocation in generall is something only a fraction of
               | your uses is ever going to need.
        
               | withinboredom wrote:
               | Yeah, this works very well. A nice "log me out of
               | everywhere, including this device" link is often all you
               | need on the settings page.
               | 
               | It also makes e2e testing very easy since you should be
               | logged out after pushing that button.
        
               | pico303 wrote:
               | How about invalidate the user's refresh token and the
               | public signing key, which forces everyone to refresh and
               | then logs out the hacked account. If it's really serious,
               | lock the account before doing this so the user can't
               | login again.
               | 
               | But yeah, if you have a revoke service, might as well
               | just use session keys.
               | 
               | Edit: typo
        
               | littlecranky67 wrote:
               | No you don't, thats why i.e. even big players like Amazon
               | with their AWS Cognito service (OAuth/OpenID Connect)
               | don't even support revoking access tokens (only refresh
               | tokens).
        
             | devjab wrote:
             | You don't need jwts to pass internal permissions. We don't,
             | but we still extract claims from a jwt token at the
             | beginning of a user flow. Then later we only use the claims
             | to determine which resource a user has access to.
             | 
             | It's not necessarily easier than just passing the jwt, but
             | with our internal setups where when you first pass through
             | the authorisation system, our traffic on your behalf is
             | secure it doesn't really warrant a reason to decode your
             | token multiple times rather than simply passing your access
             | permission claims.
             | 
             | We do still pass your jwt between isolated "products" where
             | your access request doesn't pass through dapr, but rather
             | back through our central access gateway and then into the
             | other "product". A product is basically a collection of
             | related services which are restricted to be a business
             | component. Like a range of services which handles our solar
             | plants, and another business component which handles our
             | investment portfolios, and so on.
        
             | DeathArrow wrote:
             | >If we're talking about a web session, time-limited
             | randomly-generated session tokens that are stored in a DB
             | still work fine.
             | 
             | How is this better than JWT if we have 30 microservices
             | called from front-end?
        
             | nicoburns wrote:
             | > don't cache your user/auth database, but have a
             | distributed cache of JWTs that have been revoked
             | 
             | My understanding was that this is the ENTIRE benefit of
             | JWTs (over plain session token): they allow you go from a
             | allowlist to a blocklist, which is more efficient at really
             | large scales because you only have to store expired
             | sessions (until their time-limit expires) rather than every
             | session).
             | 
             | And if you're not doing this then there's no point in using
             | JWTs (which will be the case for most people).
             | 
             | Are there any other benefits I'm missing?
        
               | andrewmcwatters wrote:
               | > you only have to store expired sessions (until their
               | time-limit expires) rather than every session).
               | 
               | I don't know of any companies that even do this. As far
               | as I know, most use cases store nothing, except for of
               | course the client storing the response.
        
           | turtlebits wrote:
           | It's not a trend. Those on the extreme ends of the spectrum
           | are always the most vocal.
        
           | DeathArrow wrote:
           | >Thank you. This middle ground between hyperscaler
           | infrastructure and super simple web apps is where most of my
           | career has been spent, yet the recent trend is to pretend
           | like there are only two possible extremes: You're either
           | Facebook or you're not doing anything complicated.
           | 
           | 100% this. I am tired of you don't need microservices, you
           | don't need JWT, you don't need Kubernetes, you don't need
           | ElasticSearch, you don't need IAM, you don't need Redis, you
           | don't need Mongo and everything should stay in one SQL
           | database.
           | 
           | Things are being used not because they exist, because people
           | want to be fancy or because they don't have something better
           | to do. Things are being used because they solve problems and
           | do so with least effort possible.
        
             | qaq wrote:
             | "Things are being used because they solve problems and do
             | so with least effort possible" in an ideal world sure in
             | the real world there are many factors that influence
             | technical decisions often having nothing to do with actual
             | problem being solved
        
               | zimpenfish wrote:
               | Having worked at many places over the last 30 years, yes,
               | there is definitely "resume-driven development" where
               | people pick something they want to put on their resume to
               | solve a problem regardless of its suitability to the task
               | in hand.
               | 
               | There's also "blinker-driven development" where people
               | pick the solution based on their own personal set of
               | hammers rather than, again, something more suitable.
               | 
               | (There's loads of these though - e.g. "optimisation-
               | driven development" where the solution MUST GO BRRRRR
               | even if the problem could be fixed by Frank typing in
               | "Yes" once a week. "GOF-driven development" where
               | everything has to rigidly fit into a GOF pattern
               | regardless of whether it actually does. "Go-driven
               | development" where everything has to be a interface and
               | you end up reading a method called Validate which calls
               | an interface method Validate which calls an interface
               | method Validate which calls an interface method Validate
               | and you wake up screaming every morning because why just
               | wtf why please help me pleasehelp)
        
               | sunshinerag wrote:
               | chuckle ... how far did you go into the validate rabbit
               | hole
        
               | zimpenfish wrote:
               | Thankfully it was only 3 interfaces down.
               | 
               | The whole codebase is riddled with the same kind of
               | layering but we do now have guidance about doing stuff
               | like that ("DON'T") and a plan to simplify some of the
               | worst offenders (like the multi-layer `Validate` hell
               | hole.)
        
               | DeathArrow wrote:
               | If I'd find myself in a place where they do "GOF-driven
               | development" or "Go-driven development" I'd search for
               | another job ASAP.
               | 
               | I don't say what you describe doesn't happen, but it's my
               | impression that most people try to adopt solutions that
               | minimize costs and development time (which also
               | translates to money). 99% of the time it's not "do the
               | best thing to satisfy solve this problem" but it's "solve
               | this problem as fast as possible, without adding
               | additional costs and using as few developers as
               | possible".
        
               | zimpenfish wrote:
               | > "solve this problem as fast as possible, without adding
               | additional costs and using as few developers as possible"
               | 
               | Agree with that but from my experience that's more like
               | 20% of the time. The rest is the various kinds of
               | bullshit development where people are padding resumes,
               | having boss's pet hobby horse forced on them, latest
               | shiny flimflam, etc.
               | 
               | (A decent chunk of that 30 years has been contracting and
               | that tends to be at places with problems which might be
               | biasing my sample set.)
        
               | qaq wrote:
               | Well in big orgs people get shuffled around teams so even
               | if you joined a team that is aligned with the way you
               | feel things should be done you might end up in totally
               | different env. after a period of time.
        
               | ggregoire wrote:
               | > there is definitely "resume-driven development" where
               | people pick something they want to put on their resume to
               | solve a problem regardless of its suitability to the task
               | in hand.
               | 
               | Or people in this industry are geeks and curious and like
               | to try new stuff and technologies just for fun?
               | 
               | That's the case for the majority of people I've worked
               | with.
        
               | zimpenfish wrote:
               | > like to try new stuff and technologies just for fun?
               | 
               | Sure but do that at home or on POCs. Not on production
               | code.
        
             | rbanffy wrote:
             | > everything should stay in one SQL database.
             | 
             | At least on different schemas.
             | 
             | That and don't let one concern access data from another, or
             | you'll have to coordinate schema changes between those
             | different concerns.
        
           | kayo_20211030 wrote:
           | JWT's really can span this middle ground. They're helpful in
           | answering the who-are-you question without resorting to
           | elaborate db work. Even middle-ground monoliths are often
           | deployed across more than one independently operating web
           | server (say, JVM processes) and JWT's ensure that each server
           | answers the who-are-you question with the same answer - the
           | code is the same, and although the process space is different
           | on each web server, the answer is the same. So chained
           | requests, with API.REQ.1 to Server 1 and API.REQ.2 to Server
           | 2 will actually work. Maybe session mechanics will work, but
           | what if you don't actually have a session and just a bunch of
           | API requests?
        
             | andrewmcwatters wrote:
             | Querying a database for a session id isn't elaborate work.
             | It's also trivial because as TFA mentioned, literally every
             | major web framework ecosystem with has a solution for this.
             | 
             | God, how hard is SELECT * WHERE ..., seriously.
             | 
             | You need to share a session across websites? Wow! Connect
             | to the database holding the sessions.
             | 
             | Boring.
        
               | jupp0r wrote:
               | Ever heard of speed of light? If you really think that
               | "just connect to the same db" was an easy solution to the
               | problem you describe in the general sense then you
               | haven't done so in a moderately complex system yet. It
               | can be a good solution for a very limited set of
               | circumstances, but that's about it.
        
               | andrewmcwatters wrote:
               | It's bullshit. A majority of high volume systems can do
               | this just fine. This is just engineering wank.
               | 
               | Standard solution is query a session table on from a
               | single location, and once you actually start to need to
               | trim request time, it's not even the first place you
               | look.
        
               | jupp0r wrote:
               | Assumptions you are making:
               | 
               | * everything is located physically close together (good
               | luck reading from a DB table in Singapore from a service
               | in Europe for every request)
               | 
               | * you have few enough client services that want to do
               | this that the number of db connections does not become a
               | problem (this is 100 by default for Postgres, you might
               | need to tune this or deploy proxies, etc)
               | 
               | * high availability is already taken care of (you don't
               | want that one DB server to bring down everything in case
               | of a failure)
        
           | nicoburns wrote:
           | You have to be pretty big before "store the session
           | information in Redis" doesn't work anymore.
        
             | bitexploder wrote:
             | And most of these session mechanisms are easy to work with
             | as middleware in common web app frameworks making it pretty
             | simple to stick with simpler sessions if everyone can get
             | to the session store. Everyone way over complicates authn
             | and sometimes barely even think about authorization. I have
             | seen many a web app with poor JWTs implementation and
             | abusable authz get broken. Sometimes the apps warranted the
             | JWT implementation but it is a lot harder than many devs
             | think.
        
               | zaat wrote:
               | That was a battle I fought with some developer
               | consultancy not long ago. I won't tell the whole story,
               | but I will say that if you have issue with JWT tokens
               | that are too big due to the number of groups each user
               | have, you probably do need to use JWTs and you are most
               | definitely doing it wrong and should educate yourself or
               | bring a consultant who at least get the difference
               | between authentication and authorization.
        
             | keredson wrote:
             | or you have crappy code that can only handle a dozen RPS.
             | [facepalm]
        
               | nicoburns wrote:
               | I mean, if anything this just means that session storage
               | won't be your bottleneck.
        
             | jupp0r wrote:
             | Or just have infrastructure that needs to validate the
             | session in different parts of the continent (world).
        
             | seabrookmx wrote:
             | As far as system load sure. Not so much uptime.. keeping
             | your session in Redis creates a single point of failure.
             | HA/Clustered Redis exists but definitely has some
             | associated complexity.
        
         | closeparen wrote:
         | Why would there be any relationship between user sessions and
         | microservices? Are you exposing them directly to the internet?
        
         | Illniyar wrote:
         | It's pretty rare to have more then 1 client facing API even for
         | large apps. Whether it's a monolith, an API gateway, Apollo
         | federation or whatever.
         | 
         | What you do to authenticate between the BFF (for lack of a
         | better name) and other services is a different matter.
        
         | unscaled wrote:
         | > Sharing the session across multiple services is its own
         | distributed systems problem with numerous security implications
         | to be aware of and bearer tokens might be a good alternative.
         | 
         | JWT makes it possible to distribute the same access token
         | across multiple systems, but so do stateful tokens. The
         | security implications when you're using JWT for this solution
         | are much higher than with database tokens. Let's look at this
         | for a moment:
         | 
         | JWT Security Issues:
         | 
         | - Inherent design issues (alg=none, algorithm confusion, weak
         | ciphers like RSA)
         | 
         | - Cannot be revoked.
         | 
         | - Key rotation and distribution is necessary to keep token safe
         | over a long period of time
         | 
         | - Claim parsing needs to be standardized and enforced correctly
         | in all services (otherwise impersonation is possible)
         | 
         | Database Tokens Security Issues:
         | 
         | - Timing Attacks against B-Tree indexes [1]
         | 
         | - Giving direct access to the database to all microservices is
         | risky
         | 
         | The security issues with databases are ridiculously easy to
         | solve: To prevent timing attacks, you can just use a hash table
         | index, split the token into a search-key part and a constant-
         | time-compare part or add an HMAC to your token. To prevent
         | direct access to the database by all your microservices, you
         | just wrap it with an API the verifies tokens.
         | 
         | The JWT security issues are much harder to solve. To prevent
         | misconfiguration or misuse and standardize the way claims are
         | used across your organization, you probably need to write your
         | own library or deploy an API gateway. To counter the lack of
         | revocation support, you either need to use very short lived
         | access tokens (so your refresh token DB will still get a lot of
         | hits and you would still need to deal with all the scaling
         | issues) or set up a distributed revocation service (not easy at
         | all). Setting up seamless key rotation also requires additional
         | infrastructure that is not part of the hundreds of JWT
         | libraries out there.
         | 
         | It's really easy to get a JWT solution that just works and
         | scales easily, but if you really care about security --
         | especially if you care about security! -- JWTs are not
         | necessarily easier than stateful tokens. They're probably
         | harder.
         | 
         | > Apart from that, Google and Facebook don't even use JWTs
         | between the browser and backends after the initial login but
         | actually do have some sort of distributed session concept last
         | time I checked.
         | 
         | Last time I checked (which was today for Google), neither
         | Google, nor Facebook is using JWT for their access or refresh
         | tokens. The only place I saw JWT with the ID Token in their
         | Open ID Connect Flow, and they can't really avoid that even if
         | their wanted, since this is mandated by the spec.
         | 
         | Facebook and Google don't need JWT. Scaling and distributing a
         | read-only token database to handle a large amount of traffic is
         | easier -- not harder! -- for these companies. Stateless tokens
         | can be useful for them in certain scenarios, but even then, if
         | you're at Google or Facebook's scale, why would you opt for JWT
         | over an in-house format that is smaller, faster and suffers
         | from less vulnerabilities?
         | 
         | [1]
         | https://www.usenix.org/legacy/event/woot07/tech/full_papers/...
        
           | sverhagen wrote:
           | I don't think keys need to be distributed per se, rather made
           | available at a URL that can be served by the same service
           | that issues the tokens? You could call that distribution, but
           | that's probably not what you meant. I agree that a lot can go
           | wrong, but isn't that also true for home growing a
           | distributed database tokens solution (I surely have seen some
           | monsters in the wild). So can't the problems with both
           | solutions be mitigated using some good libraries?
        
             | unscaled wrote:
             | "Made available at a URL" is one possible distribution
             | mechanism, yes. But this only works for asymmetric keys. If
             | you publish symmetric keys (e.g. for HS256) at a shared
             | URL... Well, now everyone can get these keys and forge
             | tokens to their heart's content.
             | 
             | Even with asymmetric tokens and a key distribution URL, you
             | still have to make sure the clients periodically update
             | their list of keys -- this is not something you get built-
             | in with every JWT library. And you still have to setup a
             | mechanism for generating the keys and distributing them
             | between the various instances of your auth server. This is
             | not so hard nowadays with cloud KMS services, but setting
             | up this solution on our own infra was quite painful.
             | 
             | > I agree that a lot can go wrong, but isn't that also true
             | for home growing a distributed database tokens solution (I
             | surely have seen some monsters in the wild). So can't the
             | problems with both solutions be mitigated using some good
             | libraries?
             | 
             | My point is not that a database solution is without its own
             | issues. At my $DayJob we're also using a mix of stateful
             | and stateless tokens (with distributed revocation lists)
             | and we had to deal with issues with both of them. But
             | stateful tokens on a database rarely suffer from security
             | issues -- the issues we had were always related to
             | scalability and performance. The mitigations for these
             | issues are also different: they almost always have to do
             | with optimizing the infrastructure (scaling out the
             | database, adding a cache) rather than using a library. In
             | fact, when we use stateful tokens in a distributed scenario
             | (and I'm sure this is true for almost everyone out there),
             | all token handling is centralized in the auth service, so
             | libraries are not strictly necessary. At most, client
             | libraries would be very thin wrapper around HTTP API calls.
        
         | pojzon wrote:
         | Just use elasticcache for sessions duh. Its a very very old
         | pattern.
        
         | likis wrote:
         | Agree, and he even mentions in the article "If you process less
         | than 10k requests per second, you're not Google nor are you
         | Facebook."
         | 
         | There is a huuuuuge gap between services handling 10k req/s and
         | Google/Facebook.
         | 
         | I think one big upside with JWT that he doesn't mention is that
         | if you have some services geographically distributed, then
         | having decentralized auth with JWTs is quite nice without
         | having to geographically distribute you auth backend system.
         | 
         | So, yes, if you have a monolith or services colocated, or have
         | some kind of monolothic API layer, then no, perhaps JWT does
         | not make sense. But for a lot of distributed services, having
         | JWTs makes perfect sense.
         | 
         | And you don't have to introduce JWT revocation for logout, if
         | you have short token expirations, you can accept the risk of
         | token leakage. If the token is valid for like 30 seconds or 1
         | minute, you would probably never be able to notice that a token
         | has been leaked anyway.
        
         | Hendrikto wrote:
         | Bearer tokens are simple until you have to invalidate them.
         | Then you get all the complexity of both solutions.
        
         | supermatt wrote:
         | As per the JWT spec: " Finally, note that it is an application
         | decision which algorithms may be used in a given context. Even
         | if a JWT can be successfully validated, unless the algorithms
         | used in the JWT are acceptable to the application, it SHOULD
         | reject the JWT."
         | 
         | There is no reason you can't keep a list of valid "session"
         | identifiers and check the JWT is valid against that as part of
         | verification. Then the only state you need to store server-side
         | is the identifier. You get the exact same benefits of server-
         | based session stores without needing to store the entire
         | session on the server - just the identifier.
        
       | dudeinjapan wrote:
       | At TableCheck we use JWTs to enable user logins across several
       | sub-apps, using an auth service we built in-house using Elixir.
       | 
       | It's a well-documented standard and we've never had an issue with
       | them since launching.
       | 
       | The supposed drawback that sessions must live for X minutes, is
       | just not a problem in practice.
        
         | unscaled wrote:
         | I'm happy that it works for you, but you've just been lucky.
         | Statistically speaking, JWT attacks are far more common in the
         | wild than attacks against stateful tokens or other token
         | formats (such as PASETO).
         | 
         | The author of this essay even gave us a few example
         | links[1][2][3]. Most users of JWT didn't go through a security
         | incident, but a large enough percentage did. Far larger than
         | you get with any other method.
         | 
         | > It's a well-documented standard
         | 
         | I beg to differ at this point. Yes, the base standard itself is
         | well-documented, but a safe way to use it is not. The JWS and
         | JWE specify a great many algorithms without recommending which
         | one of them is safest to use. Some of these algorithms (e.g.
         | most types of RSA in the spec, AES-GCM with KW) are unsafe
         | without extra precaution. The also doesn't mandate any claim
         | (including the "exp" claim!) and does not mandate setting up
         | key ids (which are necessary for key rotation).
         | 
         | If you need to encrypt your token contents the standard also
         | doesn't help you: It just gives examples of how you'd use an
         | unauthenticated encrypted JWT[4] or a "sign-then-encrypt" JWT-
         | in-JWE with RSAES-PKCS1-v1_5[5]. I cannot stress this enough,
         | but _BOTH_ examples in the standards are very obviously
         | insecure. The first example has no signature or authentication
         | at all (i.e. everybody can forge this  "encrypted JWT"), while
         | the second example uses a naive sign-then-encrypt scheme with
         | the worst protocol for this type of scheme: RSA PKCS #1 v1.5,
         | which is vulnerable to Bleichenbacher's attack[6].
         | 
         | If you want to use JWT _securely_ (assuming you 're ok with 5
         | minutes access tokens that cannot be revoked and you'll still
         | be maintaining a database for refresh tokens), here is what you
         | need to do to use it safely:
         | 
         | 1. Use the HS256 algorithm for monoliths or services behind a
         | single proxy or API pgateway.
         | 
         | 2. If you want to distribute authentication across multiple
         | services without making it possible for the various services to
         | generate tokens, use Ed25519 (as per RFC 8037). Unfortunately,
         | this is not supported by many libraries out there. ES256 is not
         | terribly insecure for signing tokens, but there are enough
         | caveats (such as its behavior when the RNG fails) that scare
         | me.
         | 
         | 3. Encrypted tokens should be avoided. If you really need them,
         | they can only be used symmetricly (i.e. same way you'd use
         | HS256). They should be done by embedding a JWE payload inside a
         | JWS. The JWE should use alg="A256KW" and
         | enc="AES_256_CBC_HMAC_SHA_512".
         | 
         | 4. "exp", "iat" and "jti" claims are mandatory. "jti" should
         | include a 64 byte random value encoded as base64url.
         | 
         | 5. "sub", "aud" and "iss" claims are mandatory and should be
         | verified.
         | 
         | 6. A "scope" claim containing the tokens scope should be
         | included and verified.
         | 
         | 7. A "clid" or "azp" claim containing the Client ID should be
         | included and verified, to identify the client.
         | 
         | 8. All keys MUST have a "kid" specified, to allow rotation.
         | 
         | Or you could just use PASETO v4.local/v4.public and get most of
         | that work done within the library itself.
         | 
         | [1] https://nvd.nist.gov/vuln/detail/CVE-2024-22513
         | 
         | [2] https://www.howmanydayssinceajwtalgnonevuln.com/
         | 
         | [3] https://auth0.com/blog/critical-vulnerabilities-in-json-
         | web-...
         | 
         | [4] https://datatracker.ietf.org/doc/html/rfc7519#appendix-A.1
         | 
         | [5] https://datatracker.ietf.org/doc/html/rfc7519#appendix-A.2
         | 
         | [6] https://datatracker.ietf.org/doc/html/rfc8017#section-7.2
        
           | dudeinjapan wrote:
           | Thank you, this is very great advice! We are adhering to most
           | of these items today but I will double-check with our
           | security team about closing any gaps.
        
       | tzahifadida wrote:
       | Probably a waste of time to answer due to the long thread here.
       | But short answer: you can store tokens in a server session which
       | will manage it for you. In case you need to refresh it, you are
       | redirected to the idp and get a refreshed token which again
       | stored inside the session. So you can handle any "microservice"
       | scenario as was called here, not sure why micro is important...
       | Also, it is a misconseption that the tokens,as it where, are not
       | stored on the oidc providing service. How are you going to logout
       | someone or invalidate or simply track devices? It is going to be
       | stored somewhere and there is nothing wrong with it. It is matter
       | of scale, if you are not facebook the addition is miniscule,
       | especially with distributed cache. Again, a misconseption it is
       | not being used already, e.g. on keycloak if you want HA you have
       | to enable distributed cache. So really naive thinking that
       | session is bad or jwt is bad. They are simply tools used by
       | protocols and the only question is usually what do you prefer
       | unless you get to the edge cases of performance which unless you
       | are facebook, my face would look daughtful to begin with if you
       | raise this argument.
        
         | Arch-TK wrote:
         | It's super common to see websites which don't properly
         | invalidate sessions because they use JWTs without tracking them
         | anywhere.
        
       | YetAnotherNick wrote:
       | > In this setup the refresh token, not the authentication token,
       | is the real session token
       | 
       | Yes, and why is that a problem? It is the best of both worlds as
       | the verification of access token is standardized and fast while
       | refresh token could be used at the first call of the session.
       | Yes, it could happen that the user is logged in for 5 more
       | minutes if the user is in middle of session, but it's really such
       | a edge case which most companies don't need to worry about.
        
       | snowstormsun wrote:
       | One huge benefit of random session tokens is also that you can't
       | include arbitrary metadata that is sent to the client.
       | 
       | Decoding JWTs of web apps can give you a lot of suprises like the
       | account email address or even password being stored inside them.
       | For devs that don't know the difference between signing and
       | encrypting JWTs are a footgun and even for those that do it can
       | be confusing. The specifications are also a hard read. A handful
       | of RFCs where you don't really know which one to lookup. JWT,
       | JWK, JWE, JWA, WTF? It seems to do everything at once, signing
       | and encryption, symmetric and asymmetric, and the format is
       | always quite similar!
       | 
       | On the other side, if you are able to avoid the many pitfalls,
       | JWTs can be very useful in the right place when used properly.
       | You could probably write an equally or more secure JSON based
       | token format from scratch without that much effort by just
       | keeping it simple and restrictive/opinionated, though.
        
         | tored wrote:
         | One huge benefit of using a non-randomized token is that you
         | can verify that you created it.
        
       | therealfiona wrote:
       | Here I was hoping someone was using the James Webb Telescope to
       | do some crazy authentication process that I never could have
       | imagined. Was hoping something like the Cloudflare lava lamp
       | wall, but much slower.
        
       | jurassic wrote:
       | The more confidently people make blanket pronouncements, the less
       | you should believe them. There are a lot of use cases for OAuth2
       | and OIDC that are not covered by "just use a web session".
       | 
       | The real thing to push back on is the logout requirement.
       | Everyone pretends they need this, when what almost everyone
       | should do is just mandate appropriately short token lifetimes and
       | revoke refresh tokens as needed.
        
         | Aeolun wrote:
         | > revoke refresh tokens as needed
         | 
         | That's a logout requirement?
        
           | jurassic wrote:
           | Not as I understand it. When I've seen this discussed, a
           | "logout requirement" has usually meant some stakeholder
           | thinks they need a way to prevent previously issued access
           | tokens from being used even though the tokens are signed by
           | the trusted authorization server and not expired (i.e. still
           | valid). This requirement asks that you find a way to
           | instantly shut off access even though the auth server has
           | previously issued access tokens that should entitle the
           | bearer to perform actions against protected resources until
           | the token expires.
           | 
           | Blocking refresh in the authorization server is trivial, but
           | trying to implement the same on access tokens in the resource
           | server at the point of use breaks the entire security model
           | of JWT. It's unreliable, because now every resource server
           | has to take on partial responsibility for authorization which
           | multiplies opportunities for mistakes. As the OP points out,
           | you need to keep track of some sort of block list and lose
           | out on many of the benefits of JWT (i.e. a resource server
           | being able to rely fully on claims in a signed token before
           | allowing an action).
           | 
           | When people show up with this kind of requirement, in my
           | experience, it is often because they foolishly configured a
           | client with a very long expiration on access tokens (e.g.
           | ~months/years instead of ~minutes/hours). This creates a
           | problem when some aspect of a user's access needs to change
           | (e.g. disgruntled employee was fired, customer didn't pay
           | their bill, etc). You can address this more easily by pairing
           | a short access token lifetime with a long refresh token
           | lifetime.
        
         | u32480932048 wrote:
         | Yeah, we had a couple customers ask about this, but they were
         | ultimately satisfied with dropping the token from the session
         | to give the appearance of logging out (so they could log in as
         | another user), and just decided to accept whatever risk goes
         | along with someone copying the session token, hitting "logout",
         | then running cURL commands for 4 more minutes.
        
       | aranchelk wrote:
       | Even running in smaller environments:
       | 
       | 1) You may not want your application servers having direct access
       | to your auth service or auth database. You may not have the
       | resources to control employee access to sensitive data when it's
       | shared across services. You may want to spend limited resources
       | for security audits on the systems that contain the most
       | sensitive data, and having them separated from everything else is
       | helpful.
       | 
       | 2) Depending on what 3rd party services you use it may not even
       | be practical to have connectivity between auth and other
       | services, and if you do, the latency may be bad enough that you
       | wouldn't want it to be blocking every request. This is especially
       | compelling for hybrid environments, e.g. the 20 year old database
       | in a colo with your user data, and a new service being built for
       | you by consultants on a PaaS.
       | 
       | 3) People act like revocation is such a nightmare, but you only
       | need the auth service to sign invalidation tokens to be passed to
       | the client-facing services, and those services only need to
       | retain them for the max TTL of the auth tokens, after which they
       | can be evicted. Yes, that's something that could be motivated by
       | having a massive environment like Google, it could also just be a
       | way to keep costs down when you're paying by the byte of storage
       | or by the outbound request on some cloud. You could try to make
       | the argument that storing invalidation data is just as bad as
       | storing session data, but the key questions are "Where?" and "For
       | how long?", and then in some circumstances it breaks down very
       | quickly.
       | 
       | 4) You may not want sensitive user data stored in the
       | jurisdictions where you want to host your apps. That's not a big
       | company problem, it's dependent on the nature of the services you
       | provide.
        
       | greenthrow wrote:
       | This post is not very good. JWTs exist for good reasons. They may
       | not fit your needs, and if not, don't use them. But they exist to
       | allow Entity A to tell Entities B through Z that A had
       | authenticated the user and authorizes them without needing to
       | make API calls, and they serve that purpose well.
        
       | egberts1 wrote:
       | Wrote a brief recap of "permission" and "login" for
       | authentication from my work in JavaScript malwares.
       | 
       | It was a rush outline article with citations.
       | 
       | Large binning, salting of hash, and revocatable are my criteria.
       | 
       | Some toolkits that went out the window firstly are:
       | 
       | * auth0,
       | 
       | * Fusion auth, and
       | 
       | * Gluu.
       | 
       | So, some of the basic criteria are:
       | 
       | * User and password login instead of plain HTTP session cookies.
       | 
       | * HTTP-only over TLS v1.2+ (secured HTTP, HTTPS)
       | 
       | * ECDSA 1K or better
       | 
       | * SameSite [1]
       | 
       | * __Host prefix [1]
       | 
       | * preload HTTP Strict-Transport-Security header line [2]
       | 
       | * Bearer token supplied by API clients
       | 
       | * Don't listen on port 80... like ever. Or revoke token if over
       | non-port 443.
       | 
       | * DO NOT use JWT [3]
       | 
       | * DO NOT use CORS [4]
       | 
       | Hope the citations help more.
       | 
       | JWT, not recommended, IMHO.
       | 
       | https://egbert.net/blog/articles/authentication-for-api.html
        
         | banish-m4 wrote:
         | Do not outsource auth to a third-party. The Okta failure was
         | bad enough.
        
       | flexterra wrote:
       | no
        
       | thayne wrote:
       | So first of all, JWT is part of the OpenID Connect specification.
       | So if you want to be either a service provider or identity
       | provider for OIDC, you need to use JWTs as an authentication
       | token in at least some cases.
       | 
       | Secondly, you don't have to hit the database on every request.
       | Unless you have really strict security requirements, you can have
       | a short expiration time on the jwt with a refresh mechanism, and
       | then you only have to check the database say once every 5
       | minutes.
       | 
       | Related to the above point, the database you check for the
       | "session" token isn't necessarily the same as the one used for
       | other data used in the request, even if you are much smaller than
       | Google or Facebook. It might not even ve the same type of
       | database.
       | 
       | Finally, even if it makes sense to use a "traditional" session
       | cookie for brower sessions, that probably doesn't make sense for
       | an external API, where the client may not have persistent cookies
       | at all, and there may not really be a concept of a session.
       | 
       | So as was mentioned in another comment, I think the answer to the
       | title question is a solid "it depends".
        
       | Savageman wrote:
       | Maybe it's irrelevant but for JWT to be passed as a Bearer in the
       | header Authentication header, it needs to be accessible from the
       | browser? Aren't httpOnly cookie safer in this regard? Or do we
       | see set the JWT in the cookie too?
        
         | 4star3star wrote:
         | Some people advocate for a secure httpOnly session cookie for
         | the client, letting the server hold onto the JWT and manage
         | refresh. This gets you the benefit of server to server access
         | via the token as well as the "session" concept and the warm
         | fuzzy feeling of knowing the client doesn't hold the token.
        
           | Savageman wrote:
           | Sounds a nice compromise, thanks
        
       | osigurdson wrote:
       | What if you want to use AzureAD or Auth0? Aren't those services
       | completely built around jwts?
        
         | hnbad wrote:
         | AzureAD is built around OIDC. Yes, the tokens use JWT but that
         | can be treated as an implementation detail. The mechanism here
         | is OIDC, not specifically JWT. The tokens can be treated as
         | mostly opaque.
         | 
         | Also in that case _you_ aren 't using JWTs for authentication,
         | AzureAD is and you're integrating with it. And as I said,
         | you're integrating with it via OIDC.
        
           | whizzter wrote:
           | opaque apart from that you should verify that the JWT token
           | is issued by a trusted party and fulfills claims that your
           | service requires? :P (Unless those parts are handled by a
           | library you've configured to check this).
        
             | hnbad wrote:
             | The point is that when logging in with AzureAD your app
             | needs to talk to AzureAD using OIDC but JWT is an
             | implementation detail of that specifically and there's no
             | reason this means you need to or should be using JWT
             | throughout the rest of your architecture.
             | 
             | If you're using AzureAD for IAM, you're not "using JWT for
             | authentication tokens", you're using AzureAD. This isn't
             | what the article is about. The article is about building
             | your own services that generate and process JWTs. And yeah,
             | if you use an OIDC API you likely use an OIDC library
             | instead of rolling your own. So as far as your own code is
             | concerned the token is entirely opaque.
        
           | osigurdson wrote:
           | Wouldn't an app normally at least need to look at some claims
           | in the jwt (email for example)? Otherwise, how do you
           | identify the user?
        
             | hnbad wrote:
             | By using the token to access the userinfo endpoint of the
             | OIDC API? Yes, some info is encoded in the token already
             | but that's why OIDC includes that endpoint unlike OAuth2
             | (which was authorization only even if it was often used for
             | authentication).
        
       | drivebycomment wrote:
       | The main claim of the article is:
       | 
       | > jwt as authentication tokens are constructed for
       | Google/Facebook scale environments, and absolutely no one who is
       | not Google/Facebook needs to put up with the ensuing tradeoffs.
       | 
       | The first sentence is factually incorrect. Google doesn't use JWT
       | for most of its own authentication. Try analyzing their traffic -
       | web and mobile apps - and you'll find that none of their 1st
       | party applications / web use JWT. They do use JWT for OIDC, for
       | third parties, which makes sense since with OIDC/JWT, third party
       | sites can verify the token without talking to Google using a
       | standard. This is largely similar for Facebook.
       | 
       | JWT RFC starts with:
       | 
       | > JSON Web Token (JWT) is a compact, URL-safe means of
       | representing claims to be transferred between two parties.
       | 
       | Notice "between two parties". Of course, nothing stops from a
       | single party to use JWT, but claiming JWT was invented for
       | "Google/Facebook scale environments" implying they are using it
       | for their 1st party authentication is just factually wrong. JWT
       | was always meant to be an information transport between two
       | separate parties, like OIDC.
       | 
       | "Ensuing tradeoffs" here is essentially about stateless
       | authentication vs stateful authentication, and even there, this
       | "absolutely no one" is simply incorrect. There are many scenarios
       | where stateless authentication is sufficient, even for the
       | authentication for a single party (let alone between two separate
       | parties). Not all applications and use cases require the security
       | properties of stateful authentication - a sufficiently short
       | expiration would provide sufficient security in many cases,
       | making the loss of any security worth it for the added benefit of
       | simplicity and reliability with the stateless authentication.
       | 
       | Now with more nitpick:
       | 
       | The article claims, since "denylist" would require a database
       | read, it's equivalent to stateful authentication. Theoretically
       | that's true. In practice, denylist has some nice properties, that
       | makes it worthwhile if server-side logout is the only missing
       | feature you need from JWT. Denylist is often very small - if most
       | users of your application or service do not explicitly log out,
       | the number of denylist will remain very small. With the
       | expiration, the size can not grow indefinitely either. Thus, the
       | denylist you maintain can be much smaller than the record of all
       | stateful authentication - which makes it cheaper/easier to
       | replicate / cache across your systems than the full
       | authentication records. So "denylist" is definitely a legitimate
       | design option, with slightly different trade-offs than a full
       | stateful authentication.
       | 
       | ---
       | 
       | If I re-interpret the main claim of the article in the way I
       | think would make sense, it would be: services should prefer
       | stateful authentication, and the cost of stateful authentication
       | is lower than people imagine it to be. That I can be behind 100%.
       | As written, the article is at best some big exaggeration with
       | incorrect details and hidden assumptions.
        
       | teliskr wrote:
       | Some people get entirely too dogmatic about their "XYZ is wrong,
       | don't do it!" beliefs. At the time I implemented JWT in our
       | system, many years ago; it was the most straightforward way to
       | solve the problems that I had. I read about the pitfalls and have
       | yet to experience any of them. So in short.. "no regrats" from
       | this heathen.
        
         | hot_gril wrote:
         | Isn't JWT's main benefit being a standard interchange format? 3
         | parts: header, payload containing user info, signature from
         | whatever authenticated the user. Can be encoded for URLs and
         | decoded to JSON. Seems pretty innocent to me.
        
       | apatheticonion wrote:
       | I typically use a service like AWS cognito (using their built-in
       | hosted UI) to handle authentication for my apps. That gives me
       | MFA, Google/Facebook login, email verification, etc for free and
       | has a generous free tier.
       | 
       | I have a template that's backed by terraform and the
       | authentication client is in lambda so the whole thing is
       | serverless, self-contained and practically free. So I just run
       | "terraform apply" and I have scalable auth for my new service.
       | 
       | https://github.com/alshdavid/template-cognito (only 1 dependency
       | on AWS, everything else is stdlib)
       | 
       | If any service I create is lucky enough to break out of the free-
       | tier and cost is an issue, then I can just move to another
       | OAuth2/OIDC provider. The auth mechanism Cognito uses is just a
       | specification meaning I am not coupled to any one service
       | provider (though the user accounts themselves are). Cognito,
       | Auth0, IdentifyServer, or whatever - I can migrate if cost
       | becomes a problem.
       | 
       | The big issue with JWTs are that, if lost, they give permissions
       | to attackers without revocability.
       | 
       | For this reason, I keep auth-tokens short lived and refresh them
       | often. Refresh-tokens are revocable and live for a few days. This
       | means that a lost auth-token is only harmful for a few minutes
       | while a lost refresh token is only harmful until revoked or
       | expired.
       | 
       | Tokens are stored as path-specific http-only cookies so the only
       | vector for attack is if a user physically opens devtools and
       | gives an attacker the token - or if the attacker has access to
       | the computer (physically or via a malicious terminal script).
       | 
       | High risk operations (e.g. delete account, delete content,
       | anything high risk) requires "step-up" authentication - so a user
       | is asked to re-authenticate in those cases.
       | 
       | Overall, when you consider that rolling your own authentication
       | comes with the liability associated with holding user data
       | (companies must announce a breach to users, etc) - if a service
       | provider like Cognito is compromised, you won't be liable or the
       | only one affected.
       | 
       | JWTs have security concerns, but on balance, when used with third
       | party provider, a sensible configuration and considering the risk
       | of rolling your own - they are fine.
        
       | pan69 wrote:
       | My take on it is;
       | 
       | JWTs are good for server to server communication (short lived at
       | ~5 minutes).
       | 
       | Sessions are for clients (browsers, apps, anything controlled by
       | a person) to communicate with a server/api.
        
       | grepLeigh wrote:
       | JWT make the most sense for zero trust machine to machine
       | authentication, where you might also want to authorize certain
       | verbs/actions/roles after confirming the requester's identity.
       | For example, I use a JWT-based authentication and role-based
       | authorization scheme for a fleet of Raspberry Pis communicating
       | with each other on a LAN or network overlay, and also with a
       | multi-tenant API on a public internet-facing VM. The Pis manage
       | 3D print jobs.
       | 
       | For users/people/apps, I usually rely on session-based
       | authentication. Sometimes I need light RBAC at this layer too
       | (users, teams, admins, etc).
        
       | 4star3star wrote:
       | I'm unconvinced of the author's actual understanding of common
       | JWT usage.
       | 
       | Suppose you use Azure. You may have one or more app registrations
       | with defined roles as well as apis exposed within the Azure
       | config. It's convenient to acquire an access token that can be
       | sent to various apis, each of which can accept the token without
       | having to worry about session state or really anything to do with
       | authentication other than validating the token's legitimacy. If I
       | wanted to roll my own security, maybe JWT isn't how I would
       | choose to do it, but I definitely don't want to do that.
        
       | jongjong wrote:
       | JWT is the simplest and most versatile authentication mechanism
       | we have.
       | 
       | I have no idea why there is so much gaslighting about them.
       | People keep pointing to implementation complexity, yet there
       | exist proven, heavily used implementations for almost every
       | language and engine imaginable. That issue is an issue of the
       | past.
       | 
       | It is doubly ironic that people try to point to 'complexity' as
       | an argument against them when most of them are addicted to over-
       | engineered software stacks... JWTs are amazingly simple when
       | compared to most other concepts in this industry.
       | 
       | The aversion towards JWT is weird. I used to think it's probably
       | because some people got burned badly due to past implementation
       | flaws and still hold a grudge... But the sheer persistence of the
       | any-JWT movement leads me to think there may be an agenda behind
       | it.
       | 
       | How could anyone possibly claim that keeping track of sessions on
       | the back end and passing around session IDs is superior? It's way
       | more expensive on the database/datastore, adds latency, it's hard
       | to scale, you often end up with stale sessions because it's not
       | fail-safe (e.g. how to clean up previously active sessions when a
       | worker crashes? Especially if you have multiple workers.).
       | Session IDs are MUCH more work.
        
         | angoragoats wrote:
         | I disagree strongly.
         | 
         | And I'm wondering if you can comment on the points raised in
         | the article regarding how you handle logging out with JWTs? It
         | seems you've got two options:
         | 
         | 1. Don't implement logging out
         | 
         | 2. Implement logging out by keeping your JWT expiration time
         | short (which is probably good practice anyway) and using a
         | refresh token to issue new JWTs periodically.
         | 
         | With the latter, you've gone right back to server side session
         | storage. So JWTs are just an added (unnecessary, for most apps)
         | layer of complexity on top.
         | 
         | Again, all of this was discussed in the article.
        
       | oppositelock wrote:
       | JWT's are perfectly fine if you don't care about session
       | revocation and their simplicity is an asset. They're easy to work
       | with and lots of library code is available in pretty much any
       | language. The validation mistakes of the past have at this point
       | been rectified.
       | 
       | Not needing a DB connection to verify means you don't need to
       | plumb a DB credentials or identity based auth into your service -
       | simple.
       | 
       | Being able to decode it to see its contents really aids
       | debugging, you don't need to look in the DB - simple.
       | 
       | If you have a lot of individual services which share the same
       | auth system, you can manage logins into multiple apps and API's
       | really easily.
       | 
       | That article seems to dislike JWT's, but they're just a tool. You
       | can use them in a simple way that's good enough for you, or you
       | can overengineer a JWT based authentication mechanism, in which
       | case they're terrible. Whether or not to use them doesn't really
       | depend on their nature, but rather, your approach.
        
         | fefe23 wrote:
         | You are confusing simplicity (it's easy to understand and
         | straightforward to implement safely) with convenience (I have
         | zero understanding of how it works and couldn't implement it
         | securely if my life depended on it, but someone already wrote a
         | library and I'm just going to pretend all risk is taken care of
         | when I use it).
        
           | oppositelock wrote:
           | Am I confusing them?
           | 
           | It's not difficult to implement JWT's, the concept is simple,
           | however, with authentication code, the devil is in the
           | details, and that's true for any approach, whether it's
           | JWT's, or opaque API tokens, whatever. There are many, many
           | ways to make a mistake which allows a bypass. Simple concepts
           | can have complex implementations. A JWT is simply a bit of
           | JSON that's been signed by someone that you trust. There are
           | many ways to get that wrong!
           | 
           | Convenience, when it comes to auth, is also usually the best
           | path, and you need to be careful to use well known and well
           | tested libraries.
        
       | jongjong wrote:
       | > Now, let's assume you are not Google. Check which of these
       | apply to you:
       | 
       | > You wanted to implement log-out, so now you're keeping an
       | allowlist of valid JWTs, or a denylist of revoked JWTs. To check
       | this you hit the database on each request.
       | 
       | A JWT is like a keycard. If a hotel gives you a keycard to access
       | your room, the card itself holds the credentials which give you
       | access to the room. That's the core principle behind JWT.
       | 
       | If you want to ban someone due to malicious conduct, then you
       | need some kind of ban mechanism but this is a concern of spam
       | prevention and attack mitigation. You need such mechanism
       | regardless of what session mechanism you use. With JWT, you may
       | need to do 1 lookup to check the account object (you can keep an
       | isBanned flag on the account itself), with regular session IDs,
       | you need 2 lookups (1 for the account object and 1 for the
       | session object). JWT still saves you 1 lookup... Also, if gives
       | you a lot of flexibility. For example, you may be able to keep a
       | 'blacklist' in-memory and separate for each worker (that may be
       | appropriate for certain use cases); you're not necessarily forced
       | to hit a datastore or database as claimed. Banning and spam
       | prevention should be seen as a separate concern.
       | 
       | Also, at small scale, there are scenarios where you don't even
       | need a blacklist. Maybe your app is being served on an internal
       | private network or maybe it's public but it's not so critical
       | that you can't wait for 10 minutes or 1 hour (or whatever) for
       | the JWT to expire for the ban to take effect. I mean, with the
       | physical hotel scenario as an example; your keycard remains
       | active until it expires. There are many such scenarios in the
       | software industry where a ban doesn't need to take effect
       | immediately.
       | 
       | When you need instant blocking for DDoS protection (where
       | immediacy of the ban/block really matters), you typically rely on
       | IPs, not specific account or session IDs.
       | 
       | > You need to be able to block users entirely, so you check a
       | "user active" flag in the database. You hit the database on each
       | request.
       | 
       | As above.
       | 
       | > You need additional relationships between the user object and
       | other objects in the database. You hit the database on each
       | request.
       | 
       | The number of times you hit the database (and the reason for each
       | lookup) matters and it's important not to mix up different
       | concerns.
       | 
       | > Your service does anything at all with data in the database.
       | You hit the database on each request.
       | 
       | This is just a variation which has the same flaws as previous
       | arguments.
       | 
       | The author claims that Session IDs provide:
       | 
       | > Greatly reduced complexity. No need to manage a secure jwt
       | signing/authentication key
       | 
       | You can provide the auth key as an environment variable when you
       | launch the service. This is well supported in all environments at
       | any scale including Docker, Kubernetes... anyway, 99% of services
       | rely on various secrets like API keys so you need to pass secrets
       | as env vars anyway, what's the issue with having just one more
       | env var to hold the JWT signing auth key? There is no added
       | complexity there as the mechanism for handling such secrets
       | already exists in most applications. It's rare to see an
       | application which doesn't have secret API keys... You need them
       | for payments (e.g. Stripe API), for database API services (e.g.
       | MongoDB cloud), Amazon AWS, etc...
       | 
       | On the other hand, managing sessions on the back end requires
       | weird workarounds like setting and constantly refreshing expiries
       | on the session keys or running complex cleanup cron jobs because
       | you end up with complicated situations where your worker might
       | crash and leave behind stale sessions. Session IDs are much more
       | complicated.
       | 
       | Not to mention that session lookups becomes a single point of
       | failure and can become a bottleneck for your application as you
       | get more users. It's really difficult to scale well. You're going
       | to run a Redis cluster just to manage sessions? Hello LATENCY!!!
       | Welcome DDoS headache!!! Did anyone say backpressure issues???
       | How are you going to clean up stale sessions when a worker
       | crashes? Good luck.
       | 
       | I hope you're billing for your development work by the hour, coz
       | you're gonna need a lot of those! I hope your health insurer
       | covers Panadol! Don't be surprised when GlaxoSmithKline opens up
       | a new factory next to your office.
        
         | angoragoats wrote:
         | > Not to mention that session lookups becomes a single point of
         | failure and can become a bottleneck for your application as you
         | get more users. It's really difficult to scale well. You're
         | going to run a Redis cluster just to manage sessions? Hello
         | LATENCY!!! Welcome DDoS headache!!! Did anyone say backpressure
         | issues??? How are you going to clean up stale sessions when a
         | worker crashes? Good luck.
         | 
         | I think this is pretty far-fetched hyperbole. How do you think
         | websites managed logging in before JWTs existed roughly a
         | decade ago? They used databases and in memory caches, which
         | work fine up to a surprisingly large number of users.
        
       | menacingly wrote:
       | We don't use JWTs because we think we're google scale, we use
       | them because they're kinda cool. Cheap, stateless auth across
       | services is really handy. If I rolled my own solution, it would
       | just look like a shitty jwt.
       | 
       | There are definitely arguments to me made against ridiculous
       | over-engineering, modern web dev has taken the problems of 1% of
       | engineers and made them problems for 100% of engineers, but I
       | think this is a bit of a silly one to focus on
        
         | j16sdiz wrote:
         | We don't use JWT because we are NOT google scale.
         | 
         | A centralized database for sessions, and we can extend/revoke
         | any token anytime.
        
           | rezonant wrote:
           | What's interesting about this argument is that nothing is
           | stopping you from doing this same thing using JWTs. Just
           | generate your token and store it as a claim in the JWT. You
           | can check the revocation of the stored token without even
           | validating that the token is genuine, if that is fortuitous.
           | What this buys you is the ability to attach clear text
           | information with the token, and, if you are doing asymmetric
           | validation, the ability to validate both the security
           | credential and the included plaintext information in an
           | untrusted (client side) setting.
        
           | menacingly wrote:
           | I actually think the revocation argument is the over-
           | engineering case here.
           | 
           | I'd argue that people who need to avoid hitting their
           | database on every request outnumber people who need sub-
           | minute revocation
        
           | hot_gril wrote:
           | I don't see how this solution means you cannot use JWTs.
        
       | jankboy wrote:
       | Yes if the JWT token can only become invalid based on an
       | expiration time. You can add the expiration time in the token and
       | check it during authentication.
       | 
       | No if the token can become invalid due to other reasons because
       | lets say the user deletes the token because it got leaked. But
       | since you have no way of invalidating the token other than
       | changing the encryption key, you can't selectively invalidate
       | that one token.
        
       | rezonant wrote:
       | Since JWTs can't handle revocation on their own, the main benefit
       | (other than the ability to do validation without a central
       | authority) of JWTs over opaque tokens is the ability to embed
       | data that an untrusted holder (ie client) can make use of. For
       | example, attach the display name of the user and their avatar
       | profile, so that even after the token expires the application can
       | represent to the user who the token is (could be used for example
       | to show a "Sign back in, Tom" view). This makes a Switch User
       | feature very elegant to implement: the application need only
       | store the signed authentication tokens, and those tokens are self
       | describing.
       | 
       | Additionally, when using asymmetric validation, you can rely on
       | JWTs as licenses: Your software can restrict offline features
       | based on a locally stored token, simply by checking that the JWT
       | was signed by the authority. In tandem with the ability to store
       | metadata, your app (with code held by the untrusted user) can use
       | the token to determine the user's license features without
       | requiring an always on connection. (Obviously patching out the
       | license checks is another matter)
       | 
       | These features can be layered on top of opaque tokens, but since
       | a JWT has all the benefits of an opaque token (store the opaque
       | token as a claim just like the rest of the metadata), it's
       | actually a complete package that does it all without needing to
       | roll it yourself.
        
         | cryptonector wrote:
         | > Since JWTs can't handle revocation [...]
         | 
         | Revocation is just hard. Even PKI's CRLs/OCSP don't have a
         | subscription mechanism by which an end-point can get
         | revocations of some small set of certificates. Even a public
         | feed of revocations could be problematic if you're going to
         | terminate employees because the timing has to be perfect: too
         | early and you're tipping off employees, too late and you're
         | allowing disgruntled employees run malware on your systems and
         | network. In practice being late to revoke access is generally
         | accepted as acceptable as long as it's not too late.
        
       | shermantanktop wrote:
       | So what's "Facebook scale"? Is there an edge TPS number? Number
       | of discrete major services? If you are consistently doing 10TPS
       | on 3 services, sure. But many systems are bigger than that.
       | 
       | Everyone wants to be big. You can either frontload those
       | problems, and risk drowning in irrelevant complexity, or defer
       | them. The problem with deferring is that switching lanes appears
       | like it will be expensive and time consuming.
       | 
       | The secret is that if you solve actual problems, rather than
       | dreams and future hopes, the answers are almost always obvious.
       | The work may be painful and people may be angry, but the
       | rationale will be clear.
        
       | abalone wrote:
       | I see a flaw in the argument. He shifted from saying you'd use a
       | 5 minute access token timeout to querying the DB on every
       | request. There can actually be a big difference between those two
       | scenarios. Some web APIs can be bursty. Even caching credentials
       | for 5 minutes could take significant load off the DB.
        
         | rezonant wrote:
         | I'm pro JWT, but reducing load on the DB itself isn't a massive
         | argument in favor of JWTs, because an opaque token solution can
         | simply cache the result of a revocation check at whatever time
         | interval is comfortable for the use case of the token. So
         | assuming the API has access to a cache layer, there isn't a
         | difference there. If there is no cache layer, there probably
         | should be.
         | 
         | In a hyperscaler situation things are different, but we should
         | avoid treating that as the norm.
        
       | ahasani wrote:
       | While mignt not agreeing with all the reasons mentioned,
       | verifying signature for every resource access is cpu intensive
       | (your commercial compute provider would love you though).
       | Comparing session id to a map is cheap. For me jwt to
       | authenticate and random session token for resource access,
       | problem solved
        
       | crunchybeeftaco wrote:
       | I'm confused though. JWT isn't hard to configure. Why not just
       | use it?
        
         | ruszki wrote:
         | I don't understand this article and thread either. Using OpenID
         | is not rocket science. You can start to use it in matter of
         | minutes. The question should be rather why you should care at
         | all to use anything else at this point.
        
       | begueradj wrote:
       | The article completely ignores single page applications where
       | these JWT can be needed a lot.
        
       | redwoolf wrote:
       | I keep reading criticism of JWTs that involves impersonation or
       | replay attacks. With JWT (or non JWT bearer tokens) you use a
       | refresh token. It seems to me that if an attacker gets a JWT they
       | also have the refresh token. So how are JWTs inherently more
       | insecure than other authentication methods? Almost all data
       | passed over the wire nowadays is TLS encrypted.
       | 
       | In my projects, we have used encrypted JWTs and it seems to me a
       | fine solution. Log out can be implemented in a user facing client
       | by deleting the JWT and refresh token. Given a short enough
       | expiration time, this is sufficient for most use cases involving
       | user facing applications. Isn't it? Generally, at least in the
       | domains of the applications I've worked on, users only intend to
       | log out of their current application when logging out. Meaning if
       | they are signed in on their phone and on their desktop browser,
       | when they sign out in the browser they don't intend to also log
       | out of their phone application.
       | 
       | The only downside I see is that if you want to log out of all
       | sessions it is impossible to implement without maintaining
       | session state in the server.
        
         | mikeryan wrote:
         | _It seems to me that if an attacker gets a JWT they also have
         | the refresh token._
         | 
         | No they don't. JWTs can be used with third party services that
         | should never see a refresh token. Those are the requests that
         | should be presumed vulnerable.
        
           | redwoolf wrote:
           | I've not come across such a use case as I've only ever
           | written applications where we own both the service and the
           | clients. Thanks for the explanation.
        
         | cryptonector wrote:
         | > So how are JWTs inherently more insecure than other
         | authentication methods? Almost all data passed over the wire
         | nowadays is TLS encrypted.
         | 
         | JWT's security -and that of _all_ bearer token systems- depends
         | utterly on the use of TLS.
         | 
         | The security of JWT used correctly is comparable to that of
         | Kerberos or PKI. There are some things to look out for, like
         | that it's way way too easy to write implementations that fail
         | open (e.g., you don't recognized a signature algorithm so you
         | don't validate the signature but also fail to fail), but other
         | than that there's nothing particularly bad about JWT.
         | 
         | > Log out can be implemented in a user facing client by
         | deleting the JWT and refresh token.
         | 
         | Logout has to be implemented either by letting time pass w/o
         | re-authenticating (i.e., let the session expire) or telling the
         | server that you're logging out. You can login again, even with
         | the same token as before (IFF it's not expired yet). There is
         | no reason to have to "burn" a token, and that would add a
         | significant amount of complexity akin to revocation.
         | 
         | > The only downside I see is that if you want to log out of all
         | sessions it is impossible to implement without maintaining
         | session state in the server.
         | 
         | Sessions imply session state. What you probably meant is that
         | if you want to revoke access then you need a revocation system,
         | and that effectively bolts on extra state on your servers (and
         | a pub/sub system). Generally no one wants that headache.
        
       | unscaled wrote:
       | Here's the thing: neither Google, nor Facebook is using JWT for
       | their access or refresh tokens. The only place they use JWT, as
       | far as I know, is for the ID Token in their Open ID Connect Flow,
       | since this is a mandatory (and terribly misguided) part of the
       | spec.
       | 
       | I keep seeing the idea that JWT was designed for Google or
       | Facebook scale being repeated over and over, but the reality is
       | that neither company uses it. Last time I checked, both used
       | rather short access and refresh tokens and it appears that at
       | least the refresh token (if not both) is stateful.
       | 
       | Implementing stateful tokens on a global scale and sharing them
       | across hundreds of service is HARD, but it's easier when you are
       | Google or Facebook, and you've got enough resources to throw at
       | this trouble.
       | 
       | And if you do need to implement a stateful token, you've got
       | every reason to choose your own format. Your applications are
       | using your own authentication libraries and infrastructure (e.g.
       | API gateways), so you don't have to worry about complicating
       | their life with a non-standard format. The upside for using your
       | own stateless format is that you avoid all the design issues with
       | JWT (alg=none, algorithm confusion, questionable support for
       | outdated algorithms from the 1970s) and you can design a far more
       | compact format that takes a fraction of the size of JWT[1].
       | 
       | There's a reason JWT got popular with scrappy startups,
       | enterprises hobby projects and Udemy/Medium tutorial authors:
       | they're very easy to spin up and library support is everywhere. I
       | don't mean to say JWT is the right choice for any of these uses
       | -- it probably isn't. But it's the easy choice, the worse-is-
       | better solution. The worse solution needs only be better in one
       | respect to win: it should be easier to implement, copy and
       | spread.
       | 
       | In the end of the day, JWT is not a good solution for either
       | Google-scale companies or small startups. But it's the small
       | startups that usually lack the resources and awareness to adopt
       | another solution.
       | 
       | [1] https://fly.io/blog/api-tokens-a-tedious-survey/#protobuf
        
       | Joel_Mckay wrote:
       | In general, a breadcrumb or cookie with symmetrically encrypted
       | session UUID, epoch time, IP, and transaction counter is wise.
       | You should also permute the servers hash salt with date daily to
       | boot session campers.
       | 
       | If for some unknown reason the recovered client transaction
       | counter losses its expected position, than you know someone has
       | tampered with the session, and or stolen your clients session
       | token.
       | 
       | Donate to the FOSS project of your choice if you find this
       | useful. =3
        
       | devdiary wrote:
       | Start with usual session based authentication. Keep it until you
       | see the absolute need to move to JWT. And make sure you
       | understand JWT and "invalidating JWT" before using it -
       | https://github.com/gitcommitshow/auth-jwt?tab=readme-ov-file...
        
       | nine_k wrote:
       | Yes. I mean, no; you should use macaroons [1] instead, which are
       | better than JWTs but are built on similar ideas.
       | 
       | [1]: https://fly.io/blog/macaroons-escalated-quickly/
        
       | axelv wrote:
       | What about jwt's in a session cookie? I think the whole story
       | needs more nuance. JWT's have many purposes.
        
       | alex_young wrote:
       | "No."
       | 
       | Whenever I see such a simple answer to a complex question, I know
       | it's probably an oversimplification.
       | 
       | The real answer is a solid "it depends on what you want to
       | achieve."
       | 
       | Say you handle invalidation by maintaining a cached table of
       | revoked tokens. Is this table larger or smaller than the table of
       | all users?
       | 
       | Perhaps you would like to embed some RBAC info in the token. Is
       | validating both the token and it's absence from your revocation
       | table more efficient than looking up all of the other
       | information?
       | 
       | Perhaps you need to do this on a distributed basis. Is the
       | overhead of maintaining such a table more or less than making all
       | the DB calls and creating a central choke point in your
       | architecture?
        
       | DeathArrow wrote:
       | I don't get why he is pretty sure everybody has only one database
       | which stores user data and application data. At work we have user
       | data in Keycloack and application data in at least 20 different
       | databases.
        
       | hansoolo wrote:
       | Off topic: the domain name is great!
        
       | 1penny42cents wrote:
       | It's a great analysis but I think it's too either-or.
       | 
       | If you have a monolith anyways then yes, why use a distributed
       | systems solution like JWT? Completely agree.
       | 
       | But if you already have an auth service, making it optional for
       | the majority of requests is a distributed systems win. Even if
       | you need to implement forced logout or some other features which
       | require hitting the auth database, they can be optional requests.
       | If the auth service is available, you get better security,
       | otherwise the services can decide whether to continue or not.
       | 
       | This is better than your entire app going down or slowing down
       | with the auth service. Though the refresh token bit is still a
       | challenge, it's a smaller one than a hard dependency on the auth
       | service on every request.
       | 
       | Again, if your auth service is just a component in your monolith,
       | the author is completely right. It's context-specific.
        
       | vldb7 wrote:
       | I don't think it is either-or type of choice. A lot, if not all
       | arguments against that use of JWT disappear if you're ok with
       | compromise solution: user management actions will take time to
       | propagate. If JWT lifetime is 5 mins, it means that all actions
       | like logout, deactivating account etc will take 5 mins to
       | finalize - and you'd be hitting the database exactly once per 5
       | mins for the purpose of checking if user is still active, refresh
       | token is not barred by log out etc.
        
       | koliber wrote:
       | Nothing wrong with JWT bearer tokens as a technology.
       | 
       | However, too many times they're implemented incorrectly or
       | without forethought. I've seen a few teams who used the bearer
       | token, set the age really high, and never bothered to implement a
       | refresh mechanism.
       | 
       | Fast forward a few months and someone says it is not possible to
       | log someone out of our service.
        
       | winternewt wrote:
       | Without JWT, how do you support log in using third party identity
       | providers like Office 365, Google or Facebook?
        
       | ChicagoDave wrote:
       | The JWT has some extra fluff for the client, but only the Bearer
       | token is used for secure communication. And every call to an API
       | validates the Bearer token with an identity service. There is no
       | automatic security because you have a token. That Bearer token
       | (not the JWT) must clearly be validated and also validated with
       | whatever functionality (Claims) is potentially being requested.
       | 
       | The meta data in the JWT is sort of a short cut to let the front-
       | end make assumptions, but it has no bearing on the actual
       | capabilities. Only a valid Bearer token can determine if a call
       | is secure (authenticated) and has the correct permissions
       | (authorized).
       | 
       | So, you don't need a JWT, but without it, you're still going to
       | need a way to send mundane meta data back to the front-end. This
       | used to be (and still can be) a separate call for "config" or
       | "permissions" data, but why bother. Just create claims in your
       | identity server, mark your API's with those claims and token
       | validation, and you're in great shape.
        
         | gizzlon wrote:
         | Changing the JWT because the frontend suddenly requires "middle
         | name" is a PITA. Also, the JWT can become become large.
        
       | datarez wrote:
       | The main advantage of jwt is that is stateless, so it reduces
       | load on database and or caching layer when checking user session.
       | Alongside being able to share the public key to verify the token
       | across different services
        
       | tracker1 wrote:
       | Authentication is often the first thing I want to break out of
       | the application and backend anyway. Good password hashing is
       | expensive... Why should an entire application have to go down
       | when your login is being DDoSed?
       | 
       | More so, A revocation list on something like Redis can expire
       | with a token and a lot less expensive than an rdbms lookup with
       | joins.
       | 
       | Not too mention, why add extra load with extra DB calls in the
       | first place?
       | 
       | You don't have to be Google scale to want to separate logins from
       | the main service. And that's just for starters.
        
       | mstaoru wrote:
       | JWTs are rather convenient for API access, especially for APIs
       | that are hit at high req/s rate, as they allow to decouple auth
       | check and actual request processing. In more complicated cases
       | you can use JWT subject for some simple preflight ACL checks as
       | well.
        
       | PaulHoule wrote:
       | Here is a quote from a comment in that article:
       | I feel like this subject comes up every couple of months
       | with another iteration of why people think JWTs are bad.
       | 
       | The regularity which with this subject comes up makes me think of
       | how Triplebyte used to pay off bloggers in the day to write posts
       | with the title "hiring is broken". I don't have the proof but I
       | am picking up a bad smell here.
       | 
       | Gotta be _yet another_ of the vendors which is selling some
       | authentication system that they host. I remember trying to
       | promote an open source user management system in the early 2000s
       | and nobody cared until they added killer feature "our service
       | will get shut down when we get acquired" which somehow drew the
       | pointy haired boss like a moth to a flame.
        
       | stanac wrote:
       | 1. You can set JWT as cookie value, I think author is describing
       | OAuth 2 problems, not JWT problems. Interestingly JWT is not
       | mentioned in OAuth 2 spec [0].
       | 
       | 2. JWT doesn't have to contain user details, it can be a simple
       | reference token used for introspection [1]. This approach removes
       | problems with invalidation and logout since identity service
       | should store invalidated tokens.
       | 
       | 3. JWT can contain refresh token id (or hash) and check for
       | invalidated refresh tokens in (distributed) cache. Cache entry
       | should not be long lived, and refresh token shouldn't be long
       | lived if you are using proper refresh token rotation with family
       | invalidation in case of a leak.
       | 
       | That being said cookies are still simplest and safest bet for
       | simple monolith applications.
       | 
       | [0] https://datatracker.ietf.org/doc/html/rfc6749
       | 
       | [1] https://datatracker.ietf.org/doc/html/rfc7662
        
       | blntechie wrote:
       | I have used OAuth2 with and without JWTs as bearer tokens and
       | pretty much used them the same way. JWT helps with fetching the
       | user details without having to hit the DB/backend, and that's
       | basically was the only difference to me.
       | 
       | I believe the issues he is describing is more OAuth2 rather than
       | JWT.
        
       | cryptonector wrote:
       | This is a bit nonsensical. Using the same logic regarding
       | logout/revocation you would also be opposed to using Kerberos,
       | say. So let's discuss the whole logout/revocation case.
       | 
       | In a typical Windows/AD or Unix installation you might have
       | Kerberos (definitely in the Windows/AD case, possibly in the Unix
       | case) _and_ a directory (definitely in both cases, though in the
       | Unix case it might be purely local), and you use the
       | authentication system (Kerberos, say) in such a way that it
       | allows you to elide some directory lookups on Windows /AD or not
       | at all on Unix. Either way you get a worst-case latency for
       | revocation of the user's service ticket's lifetime (Windows) or
       | whatever you do to stop locked user processes (Unix). Well,
       | that's the theory, but in practice many SSHv2/whatever
       | implementations do not force session end at ticket expiration. So
       | Windows/AD uses group policy for revocation. On the Unix side...
       | we don't really have a standard solution, but essentially one can
       | build a daemon that periodically looks for and kills processes
       | running as now-locked users.
       | 
       | (For SSHv2, if you build re-authentication into a KEX, like GSS-
       | KEX does, then you can force re-authentication and session end
       | when authentication expires. In practice on Unix systems it's not
       | really possible to elide a directory, so this is not necessary.)
       | 
       | Now for HTTPS apps it's a different story. You can use cookie
       | expiration and force re-authentication so that revocation latency
       | is bearer token lifetime, then keep bearer token lifetimes short.
       | If you don't control the token issuer then you can still impose a
       | shorter cookie lifetime in your app and demand a fresh token when
       | you force re-authentication.
       | 
       | In any case, one does not need a revoked token/ticket/whatever
       | list!
       | 
       | But it _is_ true that bearer tokens don 't completely elide the
       | need for a directory for things like SSH logins onto hosts. But
       | they do for HTTPS apps.
       | 
       | So I think TFA is just wrong.
        
       | jmartrican wrote:
       | I worked in a backend team that introduced JWT, before I got
       | there. The problem we had with JWT was that the data was stale.
       | Even if it wasn't stale, it needed to be treated as stale because
       | every service wanted the up to date data, even within 1 sec that
       | data is old. The user could have changed something in their
       | account from the time that the JWT was issued. I removed JWT and
       | went back to the old UUIDs as tokens.
        
       | dwhitney wrote:
       | oof - this article is pushing bad advice - general disclaimer:
       | use a third party authentication system like Okta or Cognito.
       | It's going to save you so much grief down the road. They pretty
       | much all use the same pattern (OIDC), so you just need to learn
       | it once, and you're good for at least another decade.
       | Authentication is one of the easiest and most important things to
       | take off of your plate. Do it.
        
       | skywhopper wrote:
       | This article has a pretty narrow view of what uses a JWT can be
       | put to. Yeah, in his limited examples, JWT is overengineered. And
       | for non-distributed systems, JWT is overengineered. But there are
       | plenty of use cases where JWT can simplify things and minimize
       | complexity. Eg, when network connectivity is restricted and the
       | consuming service cannot talk to the issuing service. Or when
       | using a single token to auth against multiple remote systems. Or
       | when latency is high between the central auth system and the
       | consuming service. Or when you don't have tight TTL/revocation
       | concerns and you just want to auth once a day or week. Or when
       | you just need a one-time token to establish some other service
       | relationship. Or when you want to scope down permissions to
       | subsets of what's possible, or embed arbitrary auth metadata into
       | the token, JWT has solutions for you.
       | 
       | But if you just think of this author's narrow view of what system
       | designs look like, then skip the JWT.
        
       | benced wrote:
       | I like the idea of using JWTs as opaque tokens. It sets you up
       | nicely to migrate to using them semantically and there's very
       | little downside (you just need a JWT library where the tokens are
       | minted and then it's just string equality everywhere else) if you
       | never actually end up using JWTs.
        
       | nu11ptr wrote:
       | The article is just as wrong as articles that say you should use
       | JWTs. They are both wrong because they lack context, at least in
       | their opening. Interestingly, the article eventually does give
       | context, but saying "it depends on what you need to do"
       | apparently doesn't get as many clicks as starting by saying "no".
       | 
       | For the record, I use JWTs, I have hardly any scale at all. I
       | have none of the requirements that would hit the database on each
       | request. It works just fine and I didn't need to consult an
       | article to tell me what to think because, and this is the crazy
       | part I know, I'm able to break apart my business requirements and
       | decide for myself what is needed (or not). JWTs work just fine in
       | my case. YMMV.
        
       ___________________________________________________________________
       (page generated 2024-05-28 23:01 UTC)