
Most applications pick a single authentication strategy and stick with it. In many cases that works well, but it can also become limiting when the system needs to serve different kinds of clients. A browser-based web application often benefits from session-based authentication, while mobile apps and external API consumers usually work more naturally with token-based authentication.
This project started from that exact problem. Instead of forcing one approach everywhere, I wanted to design an authentication system that could support both sessions and JWT in a clean and secure way.
Why this problem matters
Authentication is one of the most important parts of any modern application. If it is designed poorly, the whole system becomes vulnerable. At the same time, overly rigid authentication design can create unnecessary friction for developers and users.
A lot of real-world systems need to support more than one client type:
- a web dashboard used in the browser
- a mobile app
- an API consumed by other services
- admin interfaces with stricter access control
Using only sessions can make API and mobile integration less flexible. Using only JWT everywhere can introduce its own challenges, especially around token storage, refresh flows, revocation, and logout behavior. That is why I explored a hybrid model.

My design goal
The goal was to create a reusable authentication backend that could:
- support session-based authentication for browser flows
- support JWT-based authentication for API or mobile flows
- handle registration and login cleanly
- support role-based access control
- fit into a secure-by-design development approach
- be reusable across multiple future applications
This was not just about getting login to work. It was about building a foundation that could handle different types of users, adapt to new requirements, and scale as the system grows. Modern applications often need to support web, mobile, and API clients all with different needs. A flexible authentication system makes it much easier to add new features, support new platforms, and keep security strong as things change.
Why use sessions for the browser
For traditional web applications, sessions remain a strong option. They are well understood, integrate naturally with server-side logic, and can reduce some client-side token handling risks when implemented properly.
For browser-based flows, sessions offer advantages such as:
- straightforward login state management
- easier server-side invalidation
- strong control over authenticated state
- cleaner handling for standard web dashboards
In a browser context, they can often be the safest and most practical option when backed by secure cookie settings and proper session handling.
Why use JWT for APIs and mobile clients
For APIs and mobile applications, JWT can be more convenient. They allow stateless authentication checks and make it easier for external or distributed clients to authenticate without relying on server-managed session state in the same way.
JWT can be useful when:
- a mobile app needs to authenticate requests
- another frontend consumes the API
- services need lightweight credential transport
- the system may later evolve into a multi-client platform
But JWT should not be treated as automatically better. It solves some problems and introduces others. That is why I did not want to replace sessions with JWT completely. I wanted each strategy to be used where it fits best.

The hybrid approach
The system I designed supports both models instead of forcing one. In practice, that means the same backend can issue and validate the right authentication flow depending on the client type and use case.
At a high level:
- browser users can authenticate with session-based flow
- API or mobile consumers can authenticate using JWT
- authorization rules remain consistent across both
- the user model and roles stay centralized
This approach keeps the architecture flexible without duplicating the entire auth system.
Security considerations
Because authentication is security-critical, I approached the design with security controls in mind from the start. The project was developed with a strong focus on secure SDLC thinking rather than treating security as something added later.
Important considerations included:
- password hashing and safe credential handling
- secure token and session management
- role-aware authorization rules
- clear separation between authentication and authorization
- planning for audit logging and accountability
- designing flows that can be extended without weakening security
The intention was to make the backend not only functional, but also a realistic secure starting point for production-minded applications.
Reusability and real-world use
One of the biggest reasons I built this system was reusability. I did not want a one-off login implementation tied to a single project. I wanted a foundation that could later be adapted for other platforms, especially systems with multiple user roles or multiple client types.
That makes it useful for applications such as:
- SaaS dashboards
- two-sided platforms
- admin portals
- future mobile-connected systems
- internal tools with role separation
Instead of rewriting auth logic for every new idea, the aim is to have a structured base that can be extended in a more professional way.
What I learned
Working on this made something very clear: authentication design is not just about choosing between sessions and JWT. It is about understanding context.
A good authentication system should reflect:
- how users access the system
- what kinds of clients are involved
- what security assumptions exist
- how roles and permissions are enforced
- how the system may evolve later
That is why I found the hybrid approach valuable. It avoids false simplicity and gives the backend room to serve more than one kind of application properly.
Final thought
Many discussions around authentication try to reduce the problem to a simple choice: sessions or JWT. In reality, that choice depends on architecture, client type, and long-term system goals.
For this project, the best answer was not to blindly choose one side. It was to design a system that can support both in a structured and secure way.
That balance between flexibility, security, and reusability is what made this project especially meaningful to build.