What I’m working on

(Concisely describe my current challenge and the question(s) I’m trying to answer)

Our backend needs to services multiple frontends (web, iOS, Android), so we need a secure and flexible way to manage authentication and authorization:

  • How does JWT-based authentication compare to traditional cookies for our setup?
  • How does JWT being stored on the client (and not the server), impact different clients (frontends)?

What I Learned

(Summarize new knowledge, reflections, or insights gained.)

A JWT (JSON Web Token) lives entirely on the client (e.g., browsers or mobile apps). The server just has a signing key, which is like a secret stamp the server puts on every token it makes. JWTs seem to be naturally cross-platform and can be consumed by any frontend, as long as it knows how to send HTTP headers.

Unlike cookies, JWTs are stateless: The server doesn’t have to remember anything about a client, it just checks the token the client sends against its signing key. This is useful when you have different apps (web, iOS, Android) all talking to the same backend: each app can handle its own tokens without the server keeping track of separate sessions for every user on every frontend. With sessions, the server would have to track every user on every client separately. That’s extra complexity and more memory/DB use.

Cookies are fundamentally a browser concept. They shine in web apps because the browser automatically stores them and sends them along with each request. That’s their main benefit: the browser handles most of it for you. Mobile apps don’t seem to do this naturally, unless you embed a WebView (which is basically a mini-browser), cookies don’t flow.

Overall, token-based authentication seems better for modern applications that need flexibility across multiple platforms, like ours do.

It’s also important to consider the use of refresh tokens in JWT:

  • Access token (JWT): a signed token with claims (user ID, roles, expiry, etc.), stored on the client, sent with every API request.
  • Refresh token (optional, but recommended): usually a long random string. It lets clients renew access tokens without making the user log in again, improving both security and user experience.

Embedding roles as claims in the access token can also allow RBAC on the server itself, though you can optionally cross-check the DB for up-to-date permissions.

Storing tokens on different frontends:

React (SPA):

  • Access token: usually stored in memory (best) or localStorage.
  • Refresh token: can be stored securely, often in HttpOnly cookies or another secure mechanism.

iOS & Android:

  • Access token: stored in Secure Storage (Keychain on iOS, EncryptedSharedPreferences on Android).
  • Refresh token: also stored in Secure Storage.

Backend:

  • Signing keys: stored in environment variables or appsettings.json for development, and in key vaults for production.
  • Refresh tokens: can be stored in the server’s database if you want token revocation or tracking.

What do I need to do

(Write about any obstacles, or upcoming questions, that I may need to address)

While JWTs are stateless and primarily used to authenticate users, they can also carry roles as claims, which the backend can use for authorization and support RBAC:

  • Investigate best practices for embedding RBAC claims in JWTs vs. checking roles in DB.
  • Decide where to store refresh tokens for different frontends (browser vs. native apps).
  • How can RBAC via claims in JWT be used for modularity in functions?

Material

(Links, tutorials, or reference material)