A while ago, Microsoft released Identity endpoints it felt like godsend, until I realized that it wasn’t working with JWT tokens, instead, it used a different token type, and couldn’t be customized easily. This lead me to continue using JWT auth for my projects, as I used to. Then found it repetitive and I decided to build a nugget package that would make it easy and fast for you to add JWT authentication to ASP.NET Core to Your ASP.NET Core API. In this article, I’ll walk you through adding and using the package in your projects.
Installation and How to add JWT authentication to ASP.NET Core with “The.Jwt.Auth.Endpoints” nugget package
Open your ASP.net core project, and Just add the nugget package to your project (The.Jwt.Auth.Endpoints). Note, this library doesn’t support cookie auth yet! It works purely for small APIs. and is ideal for projects you’re bootstraping quickly, and you don’t want to spend time writing Auth code.
1 | dotnet add package The.Jwt.Auth.Endpoints |
You could also find the source code of the package on Github here.
Features
- 🔐 Complete Authentication Flow: Login, registration, email confirmation, password reset
- 🔄 JWT Token Management: Access tokens with refresh token support
- 📧 Email Integration: Configurable email confirmation and password reset
- 🌐 Google Social Auth: Optional Firebase Google authentication
API Endpoints
Once configured, the library provides the following endpoints:
Endpoint | Method | Description |
---|---|---|
/api/auth/register | POST | User registration with email confirmation |
/api/auth/login | POST | User login with email/password |
/api/auth/refresh | POST | Refresh JWT access token |
/api/auth/confirmEmail | GET | Confirm user email address |
/api/auth/forgotPassword | POST | Initiate password reset process |
/api/auth/resetPassword | POST | Complete password reset |
/api/auth/social/google | POST | Google Firebase authentication (optional) |
Quick Start
1. Installation
1 | dotnet add package The.Jwt.Auth.Endpoints |
2. Basic Setup
Create Your User Model
1 2 3 4 5 6 7 8 9 10 | public class ApplicationUser : IdentityUser { public string? FirstName { get; set; } public string? LastName { get; set; } public DateTime CreatedAt { get; set; } public DateTime? LastLoginAt { get; set; } public string RefreshToken { get; set; } = string.Empty; public DateTime RefreshTokenExpiryTime { get; set; } public string PictureUrl { get; set; } = string.Empty; } |
Configure Services in Program.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 | using The.Jwt.Auth.Endpoints.Extensions; using Microsoft.AspNetCore.Identity; using Microsoft.EntityFrameworkCore; using Microsoft.IdentityModel.Tokens; using System.Text; var builder = WebApplication.CreateBuilder(args); // Add Entity Framework builder.Services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection"))); // Configure ASP.NET Core Identity builder.Services.AddIdentity<ApplicationUser, IdentityRole>(options => { options.Password.RequireDigit = true; options.Password.RequiredLength = 8; options.Password.RequireNonAlphanumeric = false; options.Password.RequireUppercase = true; options.Password.RequireLowercase = false; options.User.RequireUniqueEmail = true; options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(15); options.Lockout.MaxFailedAccessAttempts = 5; options.Lockout.AllowedForNewUsers = true; }) .AddEntityFrameworkStores<ApplicationDbContext>() .AddDefaultTokenProviders(); // Required service implementations builder.Services.AddScoped<IRefreshTokenRepository, RefreshTokenRepository>(); builder.Services.AddScoped<IIdentityUserFactory<ApplicationUser>, SimpleUserFactory>(); builder.Services.AddScoped<IEmailSender<ApplicationUser>, EmailSender>(); // Configure JWT Authentication builder.Services.AddJwtAuthEndpoints<ApplicationUser>(options => { // JWT Settings options.JwtSettings.Secret = "your-super-secret-key-that-should-be-at-least-32-characters-long"; options.JwtSettings.Issuer = "https://yourapp.com"; options.JwtSettings.Audience = "YourApp"; options.JwtSettings.TokenLifeSpanInMinutes = 60; options.JwtSettings.RefreshTokenLifeSpanInMinutes = 1440; // 24 hours // JWT Bearer Token Validation options.JwtAuthSchemeOptions.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true, ValidateAudience = true, ValidateLifetime = true, ValidateIssuerSigningKey = true, ValidIssuer = "https://yourapp.com", ValidAudience = "YourApp", IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("your-super-secret-key-that-should-be-at-least-32-characters-long")) }; // Optional: Google Firebase Auth options.GoogleFirebaseAuthOptions = new AppOptions() { Credential = GoogleCredential.FromFile("FirebaseServiceAccountFile.json") }; }); var app = builder.Build(); // Configure pipeline app.UseAuthentication(); app.UseAuthorization(); // Map JWT authentication endpoints app.MapJwtAuthEndpoints<ApplicationUser>(); app.Run(); |
3. Required Implementations
To do its job in the background, the package needs services that you’ll just have to plugin to the system. These services will tell the package how to create your users, save and retrieve refresh tokens, and send emails. So, you must provide an implementation for these.
User Factory
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | public class SimpleUserFactory : IIdentityUserFactory<ApplicationUser> { public ApplicationUser CreateUser(string firstName, string lastName, string email, string? picture = null) { return new ApplicationUser { UserName = email, Email = email, FirstName = firstName, LastName = lastName, CreatedAt = DateTime.UtcNow, PictureUrl = picture ?? string.Empty }; } } |
Refresh Token Repository
1 2 3 4 5 6 7 8 9 10 11 | public class RefreshTokenRepository : IRefreshTokenRepository { private readonly ApplicationDbContext _context; public RefreshTokenRepository(ApplicationDbContext context) { _context = context; } //Your implementation } |
Email Sender
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | public class EmailSender : IEmailSender<ApplicationUser> { private readonly ILogger<EmailSender> _logger; private readonly IConfiguration _configuration; public EmailSender(ILogger<EmailSender> logger, IConfiguration configuration) { _logger = logger; _configuration = configuration; } public async Task SendConfirmationLinkAsync(ApplicationUser user, string email, string confirmationLink) { _logger.LogInformation("Sending confirmation email to {Email}", email); // TODO: Implement your email sending logic here // Example: using SendGrid, SMTP, or other email service await Task.CompletedTask; } public async Task SendPasswordResetLinkAsync(ApplicationUser user, string email, string resetLink) { _logger.LogInformation("Sending password reset email to {Email}", email); // TODO: Implement your password reset email logic here await Task.CompletedTask; } public async Task SendPasswordResetCodeAsync(ApplicationUser user, string email, string resetCode) { _logger.LogInformation("Sending password reset code to {Email}", email); // TODO: Implement your password reset code logic here await Task.CompletedTask; } } |
Example Usage
When you add JWT authentication to ASP.NET Core, you will need to test it. Here are the requests you can make to test your API.
Register User
1 2 3 4 5 6 7 8 9 | POST /api/auth/register Content-Type: application/json { "firstName": "John", "lastName": "Doe", "email": "john.doe@example.com", "password": "SecurePassword123" } |
Login
1 2 3 4 5 6 7 | POST /api/auth/login Content-Type: application/json { "email": "john.doe@example.com", "password": "SecurePassword123" } |
Refresh Token
1 2 3 4 5 6 7 | POST /api/auth/refresh Content-Type: application/json { "accessToken": "your-access-token", "refreshToken": "your-refresh-token" } |
Forgot Password
1 2 3 4 5 6 | POST /api/auth/forgotPassword Content-Type: application/json { "email": "john.doe@example.com" } |
Reset Password
1 2 3 4 5 6 7 8 | POST /api/auth/resetPassword Content-Type: application/json { "email": "john.doe@example.com", "token": "reset-token-from-email", "newPassword": "NewSecurePassword123" } |
Google Social Authentication
To enable Google authentication through Firebase:
1. Configure Firebase
- Create a Firebase project in the Firebase Console
- Enable Authentication and configure Google as a sign-in provider
- Download the Service Account Key JSON file
- Place the file in your project root and set its Build Action to Content and Copy Always
2. Configure in Code
1 2 3 4 5 6 7 8 9 | builder.Services.AddJwtAuthEndpoints<ApplicationUser>(options => { // ... other configurations options.GoogleFirebaseAuthOptions = new AppOptions() { Credential = GoogleCredential.FromFile("FirebaseServiceAccountFile.json") }; }); |
3. Usage
1 2 3 4 5 6 | POST /api/auth/social/google Content-Type: application/json { "idToken": "firebase-id-token-from-client" } |
Configuration Options
JWT Settings
1 2 3 4 5 6 7 8 | public class JwtSettings { public string Secret { get; set; } = string.Empty; public string Issuer { get; set; } = string.Empty; public string Audience { get; set; } = string.Empty; public int TokenLifeSpanInMinutes { get; set; } = 60; public int RefreshTokenLifeSpanInMinutes { get; set; } = 1440; } |
Main Configuration
1 2 3 4 5 6 | public class JwtAuthEndpointsConfigOptions { public JwtSettings JwtSettings { get; set; } = new(); public JwtBearerOptions JwtAuthSchemeOptions { get; set; } = new(); public AppOptions? GoogleFirebaseAuthOptions { get; set; } } |
Security Features
- JWT Token Validation: Comprehensive token validation with configurable parameters
- Refresh Token Rotation: Secure refresh token implementation
- Email Confirmation: Required email verification for new accounts
- Password Reset: Secure password reset with time-limited tokens
- Input Validation: Comprehensive validation using Data Annotations
- Error Handling: Consistent error responses that don’t leak sensitive information
- Account Lockout: Configurable account lockout after failed attempts
Testing
The library includes comprehensive integration tests. To run them:
1 | dotnet test |
Advanced Usage
Custom User Factory
Implement IIdentityUserFactory<TUser>
to control user creation:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | public class CustomUserFactory : IIdentityUserFactory<ApplicationUser> { public ApplicationUser CreateUser(string firstName, string lastName, string email, string? picture = null) { return new ApplicationUser { UserName = email, Email = email, FirstName = firstName, LastName = lastName, CreatedAt = DateTime.UtcNow, PictureUrl = picture ?? string.Empty, // Add custom logic here }; } } |
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
Follow me on social media and stay updated