If you’ve built a few .NET APIs, you’ve probably done authentication more than once.
- JWT setup
- login and register endpoints
- protecting routes
- maybe adding roles
It works, but every time I started a new project, I found myself repeating the same setup again.
After doing this a few times, I decided to settle on a structure that I can reuse and understand without digging through tutorials.
This is the approach I use now.
The goal
I’m not trying to build a full identity system.
I just want:
- a clean login and register flow
- JWT authentication that works
- refresh tokens so users don’t get logged out constantly
- a structure that doesn’t turn into a mess later The structure
I keep things split into simple layers.
- Api
- Application
- Domain
- Infrastructure
- Shared
Nothing fancy. Just enough separation so things don’t get mixed together.
- The API handles HTTP and controllers.
- The Application layer contains the logic like login and register.
- The Domain has the core models like User and Role.
- Infrastructure deals with the database and token generation.
- Shared contains helpers like password hashing.
This makes it easy to change one part without breaking everything.
Authentication flow
Here’s how the flow works.
1. Register or login
User sends email and password.
Password is hashed before storing it.
If login is successful, the API returns:
- a JWT access token
- a refresh token
2. Access protected endpoints
The client sends:
Authorization: Bearer
The API validates:
- signature
- issuer
- audience
- expiration
If everything checks out, the request goes through.
3. Refresh token
When the access token expires, the client sends the refresh token.
If it is valid:
- a new access token is generated
- a new refresh token replaces the old one
This avoids forcing users to log in again.
4. Roles
Each user has a role like User or Admin.
Then I can protect routes like this:
[Authorize]
[HttpGet("protected")]
public IActionResult Protected() { ... }
[Authorize(Roles = "Admin")]
[HttpGet("admin-only")]
public IActionResult AdminOnly() { ... }
Simple and clear.
Why I use refresh tokens
JWT alone is not enough for real apps.
- If the token expires quickly, users get logged out often.
- If it lasts too long, it becomes a security risk.
Refresh tokens solve that.
- Short lived access token
- longer lived refresh token
You keep security and still have a smooth experience.
Database choice
For this kind of starter, I use SQLite.
- No setup needed
- works out of the box
- easy to switch later
The database file is created on first run, which makes testing simple.
What I keep simple on purpose
I don’t try to solve everything here.
- No email verification
- No password reset
- No advanced permissions
Those can be added later depending on the project.
This is just a clean starting point.
Try it yourself
I put a free demo here:
https://github.com/i95compile/.Net-Auth-System-Core-Demo.git
You can run it, test the endpoints, and see how everything is structured.
If you want the full version
I also packaged a version you can reuse directly in your projects.
Same structure, ready to plug in and extend.
If you’re tired of rebuilding auth every time, this can save you a bit of time.
Final thought
Authentication is one of those things that is not hard, but it’s easy to waste time on.
Having a clean base you understand makes a big difference when starting new projects.
That’s what I aimed for here.
Top comments (0)