From 6962be93a46d9d04310d46767703e4183c7e59cb Mon Sep 17 00:00:00 2001 From: Natalia Portillo Date: Thu, 13 Nov 2025 14:36:14 +0000 Subject: [PATCH] Add token authentication. --- Directory.Packages.props | 45 +++++++++++--------- Marechai.Server/Marechai.Server.csproj | 2 + Marechai.Server/Program.cs | 36 ++++++++++++++++ Marechai.Server/Services/TokenService.cs | 54 ++++++++++++++++++++++++ 4 files changed, 116 insertions(+), 21 deletions(-) create mode 100644 Marechai.Server/Services/TokenService.cs diff --git a/Directory.Packages.props b/Directory.Packages.props index 625136bc..168d0cff 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -5,30 +5,33 @@ - - + + - - - - - - - - - - - - - + + + + + + + + + + + + + - - - - + + + + - + - + + + + \ No newline at end of file diff --git a/Marechai.Server/Marechai.Server.csproj b/Marechai.Server/Marechai.Server.csproj index fb298ef3..d4ee83db 100644 --- a/Marechai.Server/Marechai.Server.csproj +++ b/Marechai.Server/Marechai.Server.csproj @@ -9,6 +9,8 @@ + + diff --git a/Marechai.Server/Program.cs b/Marechai.Server/Program.cs index 8595f765..786bd31e 100644 --- a/Marechai.Server/Program.cs +++ b/Marechai.Server/Program.cs @@ -6,12 +6,15 @@ using Marechai.Database; using Marechai.Database.Models; using Marechai.Helpers; using Marechai.Server.Helpers; +using Marechai.Server.Services; using Markdig; +using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Builder; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; +using Microsoft.IdentityModel.Tokens; using Version = Marechai.Server.Interop.Version; namespace Marechai.Server; @@ -156,6 +159,32 @@ file class Program // Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi builder.Services.AddOpenApi(); + builder.Services.AddAuthentication(options => + { + options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; + options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; + options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme; + }) + .AddJwtBearer(options => + { + options.SaveToken = true; + + options.TokenValidationParameters = new TokenValidationParameters + { + ClockSkew = TimeSpan.Zero, + ValidateIssuer = true, + ValidateAudience = true, + ValidateLifetime = false, + ValidateIssuerSigningKey = true, + ValidIssuer = "apiWithAuthBackend", + ValidAudience = "apiWithAuthBackend", + IssuerSigningKey = + new + SymmetricSecurityKey("!SomethingSecret!!SomethingSecret!!SomethingSecret!!SomethingSecret!"u8 + .ToArray()) + }; + }); + builder.Services.AddDbContextFactory(options => options.UseLazyLoadingProxies() .UseMySql(builder.Configuration .GetConnectionString("DefaultConnection"), @@ -164,6 +193,12 @@ file class Program Version(10, 5, 0)), b => b.UseMicrosoftJson())); + builder.Services.AddDefaultIdentity(options => options.SignIn.RequireConfirmedAccount = true) + .AddRoles() + .AddEntityFrameworkStores(); + + builder.Services.AddScoped(); + WebApplication app = builder.Build(); // Configure the HTTP request pipeline. @@ -171,6 +206,7 @@ file class Program app.UseHttpsRedirection(); + app.UseAuthentication(); app.UseAuthorization(); app.MapControllers(); diff --git a/Marechai.Server/Services/TokenService.cs b/Marechai.Server/Services/TokenService.cs new file mode 100644 index 00000000..bcdba46b --- /dev/null +++ b/Marechai.Server/Services/TokenService.cs @@ -0,0 +1,54 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IdentityModel.Tokens.Jwt; +using System.Linq; +using System.Security.Claims; +using Microsoft.AspNetCore.Identity; +using Microsoft.IdentityModel.Tokens; + +namespace Marechai.Server.Services; + +public sealed class TokenService +{ + public string CreateToken(IdentityUser user, IList roles) + { + JwtSecurityToken token = CreateJwtToken(CreateClaims(user, roles), CreateSigningCredentials()); + var tokenHandler = new JwtSecurityTokenHandler(); + + return tokenHandler.WriteToken(token); + } + + JwtSecurityToken CreateJwtToken(List claims, SigningCredentials credentials) => + new("apiWithAuthBackend", "apiWithAuthBackend", claims, expires: null, signingCredentials: credentials); + + List CreateClaims(IdentityUser user, IList roles) + { + try + { + List claims = + [ + new(JwtRegisteredClaimNames.Sub, "TokenForTheApiWithAuth"), + new(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()), + new(JwtRegisteredClaimNames.Iat, + EpochTime.GetIntDate(DateTime.UtcNow).ToString(CultureInfo.InvariantCulture)), + new(ClaimTypes.Sid, user.Id), new(ClaimTypes.Name, user.UserName), new(ClaimTypes.Email, user.Email) + ]; + + claims.AddRange(roles.Select(role => new Claim(ClaimTypes.Role, role))); + + return claims; + } + catch(Exception e) + { + Console.WriteLine(e); + + throw; + } + } + + SigningCredentials CreateSigningCredentials() => + new(new SymmetricSecurityKey("!SomethingSecret!!SomethingSecret!!SomethingSecret!!SomethingSecret!"u8 + .ToArray()), + SecurityAlgorithms.HmacSha256); +} \ No newline at end of file