A modern ASP.NET Core 9 REST API demonstrating JWT (JSON Web Token) authentication with refresh token support, role-based authorization, and SQL Server integration.
- ✅ User Registration & Login - Create accounts and authenticate with username/password
- ✅ JWT Token Authentication - Secure token-based authentication
- ✅ Refresh Token Support - Extend sessions with automatic token refresh
- ✅ Role-Based Authorization - Restrict endpoints to specific user roles (e.g., Admin)
- ✅ Password Hashing - Secure password storage using ASP.NET Identity
- ✅ Entity Framework Core - Database ORM for SQL Server
- ✅ API Documentation - Scalar UI for interactive API exploration
- ✅ Database Migrations - Version-controlled database schema
- .NET 9 - Latest .NET framework
- ASP.NET Core - Web framework
- Entity Framework Core 9 - ORM for database operations
- SQL Server - Relational database
- JWT Tokens - Stateless authentication
- Scalar - OpenAPI/Swagger documentation UI
JwtAuthDotNet9/
├── Controllers/
│ └── AuthController.cs # API endpoints for auth operations
├── Services/
│ ├── AuthService.cs # Business logic for authentication
│ └── IAuthService.cs # Service interface
├── Data/
│ └── UserDbContext.cs # Entity Framework database context
├── Entities/
│ └── User.cs # User database entity
├── Models/
│ ├── UserDto.cs # Login/register request model
│ ├── TokenResponseDto.cs # Token response model
│ └── RefreshTokenRequestDto.cs # Refresh token request model
├── Migrations/ # Database schema history
├── Properties/
│ └── launchSettings.json # Run configurations
├── appsettings.json # Configuration
├── Program.cs # Application startup
└── JwtAuthDotNet9.csproj # Project file
- .NET 9 SDK - Download
- SQL Server - Local or Docker instance
- Visual Studio Code or Visual Studio (optional)
If you don't have SQL Server installed locally, start it with Docker:
docker run -e 'ACCEPT_EULA=Y' \
-e 'SA_PASSWORD=YourStrong(!)Password' \
-p 1433:1433 \
--name sqlserver \
-d mcr.microsoft.com/mssql/server:2022-latestVerify SQL Server is running:
docker ps | grep sqlserverTo stop SQL Server:
docker stop sqlserverTo restart SQL Server:
docker start sqlserverTo remove SQL Server container:
docker rm sqlserverNote: The default
SA_PASSWORDinappsettings.jsonmatches this Docker command. Change both if you use a different password.
Edit appsettings.json and update the connection string (if using custom password):
{
"ConnectionStrings": {
"UserDatabase": "Server=localhost,1433;Database=UserDb;User Id=sa;Password=YOUR_PASSWORD;TrustServerCertificate=true;"
},
"AppSettings": {
"Token": "YourSuperSecureKeyAtLeast32CharactersLong!!!",
"Issuer": "MyAwesomeApp",
"Audience": "MyAwesomeAudience"
}
}- Change the
Tokento a long, random secret key (used for signing JWTs) - Use a strong database password
- Store sensitive configuration in Azure Key Vault or similar in production
- Never commit real credentials to version control
dotnet ef database updateThis creates the database schema with User table and indexes.
dotnet runThe API will start at https://localhost:<port>
For complete API documentation, including detailed request/response examples, request parameters, and error handling, see API Documentation.
Quick reference:
POST /api/auth/register- Register a new userPOST /api/auth/login- Authenticate user and receive JWT tokensGET /api/auth- Get authenticated user info (protected)GET /api/auth/admin-only- Admin-only endpoint (protected)POST /api/auth/refresh-token- Refresh access token
Once the application is running, visit:
https://localhost:<port>/scalar/v1
This opens an interactive API documentation UI where you can:
- Browse all endpoints
- View request/response schemas
- Test endpoints directly from the browser
- Registration → User creates account with username & password
- Login → User authenticates, receives
accessToken(1 hour expiry) andrefreshToken(7 days expiry) - API Calls → Include
accessTokeninAuthorization: Bearer {token}header - Token Expiry → When
accessTokenexpires, userefreshTokento get a new one - Refresh Token Expiry → User must log in again
Tokens contain:
sub(Subject) - Usernamenameid(Name Identifier) - User IDrole- User role for authorization
CREATE TABLE [Users] (
[Id] UNIQUEIDENTIFIER PRIMARY KEY,
[Username] NVARCHAR(MAX) NOT NULL UNIQUE,
[PasswordHash] NVARCHAR(MAX) NOT NULL,
[Role] NVARCHAR(MAX),
[RefreshToken] NVARCHAR(MAX),
[RefreshTokenExpiryTime] DATETIME2
);Passwords are hashed using PasswordHasher<User> from ASP.NET Identity:
var hashedPassword = new PasswordHasher<User>()
.HashPassword(user, request.Password);This uses PBKDF2 with SHA-256 by default (configurable).
Tokens are signed with HMAC-SHA512:
var key = new SymmetricSecurityKey(
Encoding.UTF8.GetBytes(configuration["AppSettings:Token"]!));
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha512);- Refresh tokens are random 32-byte values encoded in Base64
- Stored in database and validated on each refresh
- Automatic expiry after 7 days
dotnet builddotnet testdotnet watch runappsettings.json- Default settingsappsettings.Development.json- Development overrides- Environment variables - Take highest precedence
| Setting | Description |
|---|---|
ConnectionStrings:UserDatabase |
SQL Server connection string |
AppSettings:Token |
Secret key for JWT signing |
AppSettings:Issuer |
JWT issuer claim |
AppSettings:Audience |
JWT audience claim |
Verify SQL Server is running:
- Check connection string in appsettings.json
- Ensure database server is accessible
- Check firewall rules
- Ensure
Tokensecret key is the same on client and server - Verify token hasn't expired
- Check
IssuerandAudiencematch configuration
# View pending migrations
dotnet ef migrations list
# Rollback last migration
dotnet ef database update <PreviousMigrationName>- Use strong, unique JWT secret keys
- Enable HTTPS only
- Store secrets in Azure Key Vault or AWS Secrets Manager
- Implement rate limiting
- Add CORS policies
- Enable logging and monitoring
- Use SQL Server Always Encrypted for sensitive data
- Implement proper exception handling
- Add request validation
Create a Dockerfile:
FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
WORKDIR /src
COPY ["JwtAuthDotNet9.csproj", "."]
RUN dotnet restore "JwtAuthDotNet9.csproj"
COPY . .
RUN dotnet build "JwtAuthDotNet9.csproj" -c Release -o /app/build
FROM mcr.microsoft.com/dotnet/aspnet:9.0
WORKDIR /app
COPY --from=build /app/build .
EXPOSE 80
ENTRYPOINT ["dotnet", "JwtAuthDotNet9.dll"]- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit changes (
git commit -m 'Add amazing feature') - Push to branch (
git push origin feature/amazing-feature) - Open a Pull Request
This project is licensed under the MIT License - see the LICENSE file for details.
- Microsoft JWT Documentation
- ASP.NET Core Authentication
- Entity Framework Core
- JWT.io - JWT Debugger & Documentation
For issues, questions, or suggestions, please open an issue or contact the development team.
This README was generated with the assistance of AI and reviewed by a human for clarity and accuracy. ❤️
Last Updated: November 2025
Framework: .NET 9
License: MIT