[HN Gopher] Authentication with Axum
___________________________________________________________________
Authentication with Axum
Author : mattrighetti
Score : 76 points
Date : 2025-06-04 23:33 UTC (23 hours ago)
(HTM) web link (mattrighetti.com)
(TXT) w3m dump (mattrighetti.com)
| mrbluecoat wrote:
| Not sure how this is better or easier than Neon Auth mentioned a
| few articles further down on today's HN homepage:
| https://news.ycombinator.com/item?id=44184849
| ripley12 wrote:
| This is very useful, thanks. I've implemented similar
| authentication with tower_cookies in a home project, and it took
| me a _long_ time to figure out; Axum is powerful but not always
| the most intuitive. It was a great learning experience, but I 'd
| love to offload more of this to a crate next time.
| tonyhart7 wrote:
| I think https://loco.rs/ is better choice for you then
| echelon wrote:
| Rust ORMs still don't feel great. They're verbose, the
| tooling is flaky, and they feel fly-by-night. They're
| surprisingly not very type safe either. I'd rather use Rust's
| sqlx until the Rust ORM situation improves.
|
| Axum and Actix are already drop in replacements for something
| like Flask. They're mature and good at what they do.
|
| The dream of a Rails or Django in Rust is still really far
| off. I'm glad the Loco folks are trying, but it needs a lot
| more magic and maturity to truly bear that comparison.
| tonyhart7 wrote:
| wdym magic, its rust
|
| there is no magic only a lot of macros, and someone must
| write them
|
| if you talking about mature, of course 20+ years web
| framework is more mature + have more feature than loco.
| what kinda comparison is that
| Spartan-S63 wrote:
| Back when SeaORM first released, I was excited about
| another ORM that supported async (as Diesel didn't have an
| async shim at the time), but man does it have some _weird_
| design decisions.
|
| The underlying query builder's API is just downright odd.
| The ActiveRecord pattern is fine for SeaORM, but it's
| just... weird. The ergonomics aren't right.
|
| It's sent me down the path of wanting to design an ORM for
| myself that has the ergonomics I want. Nothing really to
| show yet at this point, but still something I want to build
| out someday.
| Jalad wrote:
| Can you please explain what you found weird? I just
| started using sea-query and haven't found anything super
| odd quite yet, but I've only written a few migrations and
| queries so far
| mattrighetti wrote:
| I can say a word or two about sea-query.
|
| I've used it for 3-4 months in a lot of my projects. For
| very basic queries it worked very well and I liked it
| quite a bit, but as soon as I had to do something a
| little less standard that's where I started to see the
| issues.
|
| Documentation is not really there yet and I ended up
| loosing tons of time trying to look for the right
| function in the crate that would translate in the query I
| had to make. I've blogged about that too if you're
| interested [0] (TLDR: I got back to using raw SQL)
|
| [0]: https://mattrighetti.com/2025/01/14/ditching-sea-
| query
| jtwaleson wrote:
| I'm using Loco a lot since half a year, and have
| contributed some upstream patches. It's definitely not
| Rails, but where I was always afraid of refactoring Python
| applications, that fear is pretty much gone with Loco/Rust.
| That, and the incredible performance make me enjoy it a lot
| now. You pay extra with development time, but you get it
| back with long term maintainability.
|
| My main remaining gripe is the Rust compilation speed...
| dgroshev wrote:
| I don't think "glue" frameworks like Loco can get us there
| tbh. One problem is that frameworks like Django or RoR
| _have_ to be tightly integrated! Auth needs to be aware of
| models and sessions, autogenerated admin pages like Django
| have to rely on model metadata, templates and view code
| might need to be aware of cache busting and static file
| handling, etc etc. It 's a tough problem and a very high
| barrier to creating a new comprehensive framework.
| Scarblac wrote:
| I looked into it recently. Apparently it has session based
| auth.
|
| I tried to figure out how to do the first thing that came to
| mind: on an incoming request, check that the user is
| authenticated, and if not redirect them to a login page with
| a ?next= parameter to be redirected back to after login.
|
| Couldn't find a way to do it. In Django you use the
| @login_required decorator on a view and that's it.
| johnduhart wrote:
| > I won't get sucked into the session ID vs. JWT argument, but
| honestly, using JWTs in cookies is a win because you don't have
| to fuss with storing session data on the server.
|
| Okay, but then you implement storage of the refresh token and
| build this bespoke JWT re-issuance logic. So where's the win
| here? Just use sessions.
| maz1b wrote:
| What's wrong with storing JWTs in httponly cookies? Having a
| way to validate and track JWTs is a valid use case.
| unscaled wrote:
| 1. Cookies remain valid on logout (unless you implement a
| session revocation mechanism). This can be mitigated by
| making the JWT live a very short time (e.g. 5 minutes), but
| you still don't get immediate expiry. This may not be
| acceptable if you need to comply with certain standards such
| as PCI/DSS, SOX or HIPAA.
|
| 2. Cookie size could grow much larger. It's generally not a
| huge concern if you don't stuff too much data into your JWT
| claims, but if you want to optimize traffic for slow
| networks, JWT is not a great choice.
|
| 3. Since 50% of the JWT standard (and 90% of the JWE
| standard) is made of broken and semi-broken algorithms, you
| need to be careful on what keys and libraries you use.
| Unfortunately, we cannot see the implementation for this
| article, so we cannot judge how safe it is, but JWT just
| requires extra care compared to Session IDs.
|
| 4. If you're using refresh tokens because your JWT is short
| lived, you're not really saving yourself any work, because
| you'd still need a database to store the refresh tokens. This
| is what GP was referring too.
|
| > using JWTs in cookies is a win because you don't have to
| fuss with storing session data on the server.
|
| This is the author's claim, but the code clearly DOES store
| data on the server, for the refresh token.
|
| Using JWT for "simplicity" is unfortunately common mistake.
| With JWT you can choose between simple (no storage required,
| no token revocation) and secure (immediate or delayed session
| revocation at the price of requiring storage). You cannot
| have both. OP chose security (by using a refresh token
| database), but this is no longer a simple solution. If you've
| already deployed a DB, Session IDs would always be simpler:
|
| 1. You could remove the refresh and redirect logic entirely.
|
| 2. You would only need one type of cookie.
|
| 3. You can remove dependency on JWT libraries, choosing the
| right cryptographic algorithm and configuring (and securing!)
| JWT signing keys.
|
| This comes at no cost, since the refresh token DB is already
| there.
|
| There can be good reasons to use JWT (although there are
| better standards out there for stateless tokens[1]), but
| simplicity is not one of them. Stateless tokens can be useful
| in the following cases:
|
| 1. Performance (increasing throughput or reducing latency by
| skipping the database for most calls).
|
| 2. Reliable cross-regional tokens (you don't need to
| replicate session IDs across regions and suffer from
| inconsistencies during failovers).
|
| 3. Decentralized verification (without relying on a central
| database).
|
| 4. Offline attenuation without network calls (with Macaroons
| or Biscuits only).
|
| Unfortunately it seems like in 99% of the cases JWT is chosen
| as the access token format, it's chosen by its perceived
| simplicity and popularity, and the result is either a toy
| implementation that doesn't support token revocation, or an
| implementation that is more complex than it would have been
| with classic Session IDs.
|
| [1] https://fly.io/docs/security/tokens/
| mattrighetti wrote:
| I agree with the overall JWT statements you made.
|
| > This is the author's claim, but the code clearly DOES
| store data on the server, for the refresh token.
|
| You're right, but I was making a different point about
| session IDs versus JWTs.
|
| With session ids, each user request requires a server-side
| lookup to validate the session. For that reason, their
| ideal place would be something in-memory and that's what I
| was referring to with "data on the server".
|
| While storing session IDs in a database is an option, in my
| case it would introduce noticeable latency because I self-
| host my projects on a cluster of Pis at home and even
| though I have a fast connection, a roundtrip to my external
| (I don't self-host it) database still takes a few
| milliseconds under low load.
|
| JWTs allow me to avoid frequent server-side lookups. I can
| trust the client's data without hitting the database,
| except when issuing a new JWT - but even then, that happens
| every 2-3 minutes per user. While verifying JWT signatures
| and decoding claims does consume some CPU cycles, this
| overhead is minimal on my setup compared to the latency of
| database roundtrips.
|
| Nothing against session ids, but I feel JWTs are better
| suited for my resource constrained setup.
| VWWHFSfQ wrote:
| The major benefit of this approach is that you can avoid a
| database lookup when you have a valid JWT available. Only in
| cases where it's missing or expired would you need to hit the
| database for a refresh token.
|
| This might not matter for trivial apps, but for very high-
| concurrency web apps this will matter a lot.
| 0x457 wrote:
| Sure, but unless you're near Google scale, you probably
| shouldn't worry about that. IMO JWTs should not be used on
| public internet.
| Goofy_Coyote wrote:
| > HttpOnly Attribute: Prevents client-side JavaScript from
| accessing the cookie, neutralizing XSS attacks
|
| Just a note that 'HttpOnly' doesn't neutralize XSS.(although this
| is not the main point of this blog)
|
| This is dangerously misleading. HttpOnly prevents cookie theft,
| but it absolutely doesn't "neutralize" XSS.
|
| First, even with HttpOnly cookies, malicious JS can still make
| requests on behalf of the user - the browser happily attaches all
| cookies (including HttpOnly ones) to XHR/fetch requests. So an
| attacker can still
|
| `fetch('/api/admin/add-user', {method: 'POST', body:
| JSON.stringify({email: 'attacker@evil.com', role: 'admin'})})`
|
| or delete data, transfer funds, whatever the victim is authorized
| to do. They don't need to read the cookie, they just need the
| browser to send it.
|
| This is why many apps ask for your password to change your email
| or reauthenticate you/trigger an MFA workflow when doing certain
| things.
|
| Second, tons of XSS attacks don't even care about your cookies.
| They can rewrite the DOM with a fake login page and harvest
| credentials directly. They can keylog everything you type. They
| can steal data that's already on the page, redirect you to
| phishing sites, or mine crypto with your CPU.
|
| HttpOnly is a good defense-in-depth measure, but calling it a
| neutralizer for XSS is like saying a seatbelt neutralizes car
| accidents. You still need proper input validation, (contextual)
| output encoding, CSP etc.
| mattrighetti wrote:
| You're right, I poorly worded that sentence. I was specifically
| referring to XSS attacks targeting cookies or data like
| localStorage/sessionStorage.
| imtringued wrote:
| I fully agree. Anyone who wants to defend against XSS should
| have a tightly locked down CSP. That's the only way. (no,
| "careful" coding isn't reliable enough)
| varun_ch wrote:
| I think you mean a tightly locked down CSP _and_ "careful"
| coding (just escape practically everything you render), a
| tightly locked down CSP is also not reliable enough.
| hiddenfinance wrote:
| with GPU finger printing cookies are least of my concern now
| days. Cookies are about as good as a web session it should be
| discardable if needed.
|
| Yeah but once the attacker get access to the JS they can do
| whatever. That is why stop using CDN for your jquery or react
| libs!
| diggan wrote:
| > That is why stop using CDN for your jquery or react libs!
|
| I agree, but for adding a bit of security to those who
| must/want to use CDNs regardless, make sure to use
| Subresource Integrity https://developer.mozilla.org/en-
| US/docs/Web/Security/Subres...
| julienfr112 wrote:
| Absolutely, there is a confusion with same-site=strict, witch
| effecively prevent XSS.
| 0x457 wrote:
| I think storing JWT in cookie is madness. You got every downside
| of JWT without any upside at all.
|
| Use a normal session, since you already assume there is a db for
| authentication.
|
| That JWT token takes much more space on a client (and sent with
| every request!) than a proper session cookie would.
|
| That JWT refresh token would also take more space than what you
| store in claims would take up even if you naively stored it in DB
| (i.e. not via relation between user and session).
|
| IMO, the only way to correctly use JWT for authz (authn is
| handeled somewhere else as it should):
|
| - authenticate user somehow (not JWT probably, unless it's a
| federated login)
|
| - issue a short-lived JWT with required claims
|
| - pass that JWT with requests to other backends
|
| - now backends can validate that JWT by themselves
| skwee357 wrote:
| In the ancient times before SPA era, we used to generate sessions
| in the server and return them in the cookie. Upon every request
| you would check the cookie value against the db table and if a
| match found, you have an authenticated user.
|
| But then came an era of SPA and we needed another way to
| authenticate users, since XHR did not (and still not?) supported
| cookies, so we created the signed JWT token.
|
| I'm a bit confused as to why one would want to store JWT access
| and refresh token in a cookie? Axum provided both signed (temper
| proof but not secret) and private (allows you to save sensitive
| information) cookies. Why wouldn't you use these instead of
| saving JWT in cookies?
___________________________________________________________________
(page generated 2025-06-05 23:01 UTC)