A modern and production-ready ToDo Web API built with ASP.NET Core, Clean Architecture, JWT Authentication, PostgreSQL, and Docker.
This project was designed as a scalable backend foundation for a notes / task management platform with secure authentication, domain separation, infrastructure isolation, and automated unit testing.
- JWT Authentication
- JWT stored in secure HttpOnly Cookies
- User logout with JWT cookie invalidation
- Argon2 password hashing
- Authorization using
.RequireAuthorization() - Global exception handling middleware
- Secure cookie policy configuration
- HTTPS support
- Create notes
- Update notes
- Delete notes
- Get all notes for authenticated user
- Get note by ID
- One-to-many relationship between users and notes
- Attach tags to notes
- Get all notes of authenticated user by tag
- Detach tags from notes
- Multiple tags can be attached to a single note
- Note visibility authorization based on account visibility settings
- Access to public notes, private notes, and notes of friends
- Friendship-based access control using accepted friendships
- Filtering notes based on the authenticated user's permissions
- Create tags
- Update tags
- Delete tags
- Get all tags for authenticated user
- Get tag by ID
- Many-to-many relationship between notes and tags
- Attach tags to notes
- Detach tags from notes
- User-owned tags
- Tag ownership validation
- Prevention of duplicate tag attachments
- User registration
- User login
- User logout
- Delete own account
- Get info about current authenticated user
- Get all users
- Get user by ID
- User account visibility settings (
Public,Private,OnlyForFriends) - Change account visibility
- Send friend requests
- Accept friend requests
- Reject friend requests
- Remove friends
- View all friends
- View incoming friend requests
- Friendship status management (
Pending,Accepted,Rejected)
The project includes a dedicated unit testing suite built with xUnit, FluentAssertions, and Moq.
- User registration
- User authentication
- Note creation
- Note retrieval
- Note retrieval by tag
- Note updating
- Note deletion
- Account visibility changes
- Note visibility authorization
- Access control based on friendship status
- Filtering notes by visibility permissions
- Tag creation
- Tag retrieval
- Tag updating
- Tag deletion
- Tag attachment to notes
- Tag detachment from notes
- Friend requests
- Friend request acceptance
- Friend request rejection
- Friend removal
- Friendship retrieval
- Account deletion
- Validation and error scenarios
| Technology | Purpose |
|---|---|
| xUnit | Test framework |
| FluentAssertions | Readable assertions |
| Moq | Mocking dependencies |
Tests primarily target Application Layer use cases and handlers, isolating business logic from external infrastructure.
- Clean Architecture
- Domain-Driven Design principles
- CQRS-like Use Cases / Handlers
- Repository Pattern
- Unit Of Work Pattern
- DTO separation
- Value Objects
- Result / Error pattern
- Authorization services
ToDoApp
β
βββ ToDoApp.Domain β Core business entities and value objects
βββ ToDoApp.Application β Use cases, DTOs, interfaces, business logic
βββ ToDoApp.Infrastructure β Database, repositories, authentication, persistence
βββ ToDoApp.WebApi β Minimal API endpoints and application entry point
β
βββ ToDoApp.Application.Tests
β βββ Users β User use case tests
β βββ Notes β Note use case tests
β βββ Tags β Tag use case tests
β βββ Friendships β Friendship use case tests
β
βββ nginx β Nginx reverse proxy configuration
βββ docker-compose.dev.yaml
βββ docker-compose.prod.yaml
βββ Dockerfile
The project follows a layered Clean Architecture approach:
Contains:
- Entities
- Value Objects
- Core business rules
This layer has zero dependencies on external frameworks.
Contains:
- Use Cases
- DTOs
- Repository Interfaces
- Authorization Services
- Friendship-aware authorization
- Visibility-based access control
- Validation
- Result/Error abstractions
Business logic lives here.
Contains:
- Entity Framework Core
- PostgreSQL integration
- JWT token generation
- Argon2 password hashing
- Repository implementations
- Dependency Injection configuration
Contains:
- Minimal API endpoints
- Authentication setup
- Middleware configuration
- Swagger
- HTTP concerns
- Centralized JWT claims access
- Rate limiting configuration
Unit tests are separated from the production application and focus primarily on the Application Layer.
ToDoApp.Application.Tests
β
βββ Users
β βββ CreateUserHandlerTests
β βββ LoginUserHandlerTests
β βββ ChangeUserVisibilityHandlerTests
β βββ DeleteMyAccountHandlerTests
β
βββ Notes
β βββ CreateNoteHandlerTests
β βββ GetNoteHandlerTests
β βββ GetNotesHandlerTests
β βββ GetUserNotesByTagHandlerTests
β βββ NotesAuthorizationServiceTests
β βββ UpdateNoteHandlerTests
β βββ DeleteNoteHandlerTests
β βββ AttachTagToNoteHandlerTests
β βββ DetachTagFromNoteHandlerTests
β
βββ Tags
β βββ CreateNewTagHandlerTests
β βββ GetAllMyTagsHandlerTests
β βββ GetMyTagByIdHandlerTests
β βββ UpdateTagHandlerTests
β βββ DeleteTagHandlerTests
β
βββ Friendships
βββ SendFriendRequestHandlerTests
βββ AcceptFriendRequestHandlerTests
βββ RejectFriendRequestHandlerTests
βββ RemoveFriendHandlerTests
βββ GetFriendsHandlerTests
The tests use mocks to isolate application logic from repositories, unit-of-work implementations, logging, and other external dependencies.
This allows individual use cases to be tested independently and deterministically.
| Technology | Description |
|---|---|
| ASP.NET Core | Web API framework |
| .NET 10 | Runtime |
| Entity Framework Core | ORM |
| PostgreSQL | Database |
| JWT | Authentication |
| Argon2 | Password hashing |
| Docker | Containerization |
| Nginx | Reverse proxy |
| Serilog | Structured logging |
| Swagger/OpenAPI | API documentation |
| xUnit | Unit testing |
| FluentAssertions | Test assertions |
| Moq | Mocking |
- User registers or logs in
- Server generates JWT token
- JWT is stored in secure HttpOnly cookie
- Authorized endpoints validate token automatically
- User identity is extracted from JWT claims
- User can log out by invalidating the JWT cookie
| Method | Endpoint | Description |
|---|---|---|
| POST | /Auth/Register |
Register new user |
| POST | /Auth/Login |
Login user |
| POST | /Auth/Logout |
Logout and remove JWT cookie |
| Method | Endpoint | Description |
|---|---|---|
| GET | /Users |
Get all users |
| GET | /Users/{id} |
Get user by ID |
| GET | /Users/Me |
Get current authenticated user info |
| DELETE | /Users/Me |
Delete current authenticated user |
| Method | Endpoint | Description |
|---|---|---|
| GET | /Notes |
Get all visible notes |
| GET | /Notes/{id} |
Get note by ID |
| GET | /Notes/Me |
Get all notes of authenticated user |
| GET | /Notes/Me/Tags/{tagId} |
Get authenticated user's notes by tag |
| POST | /Notes |
Create new note |
| PUT | /Notes/{id} |
Update note |
| DELETE | /Notes/{id} |
Delete note |
| POST | /Notes/{noteId}/Tags/{tagId} |
Attach tag to note |
| DELETE | /Notes/{noteId}/Tags/{tagId} |
Detach tag from note |
| Method | Endpoint | Description |
|---|---|---|
| GET | /Tags/Me |
Get all tags of authenticated user |
| GET | /Tags/Me/{id} |
Get tag by ID |
| POST | /Tags |
Create new tag |
| PUT | /Tags/{id} |
Update tag |
| DELETE | /Tags/{id} |
Delete tag |
| Method | Endpoint | Description |
|---|---|---|
| GET | /Friends |
Get all friends |
| GET | /Friends/Incoming |
Get incoming friend requests |
| POST | /Friends/{friendId} |
Send friend request |
| PUT | /Friends/{friendId}/Accept |
Accept friend request |
| PUT | /Friends/{friendId}/Reject |
Reject friend request |
| DELETE | /Friends/{friendId} |
Remove friend |
| Method | Endpoint |
|---|---|
| GET | /health |
docker compose -f docker-compose.dev.yaml updocker compose -f docker-compose.prod.yaml up -dgit clone <repository-url>
cd ToDoAppdocker compose -f docker-compose.dev.yaml up --builddotnet run --project ToDoApp.WebApiRun the complete test suite with:
dotnet testTo run tests with detailed output:
dotnet test --verbosity normalSwagger is enabled automatically.
After launching the application open:
localhost/
or
localhost:5000/
Seq is enabled automatically.
After launching the application open:
localhost:5341
User - Admin Password - Admin
The application uses:
- Secure HttpOnly cookies
- Strict SameSite policy
- HTTPS-only cookies
- Argon2 password hashing
- JWT validation
- Centralized exception handling
- Centralized user ID extraction from JWT claims
- Rate limiting to restrict excessive requests
- Friendship-aware authorization for protected notes
This project is licensed under the MIT License.
See LICENSE.txt for more information.
Developed by ArGul.
If you like this project β consider giving it a β on GitHub.