diff --git a/src/BHC24.Api/BHC24.Api.csproj b/src/BHC24.Api/BHC24.Api.csproj
index d76c0c4..36963ff 100644
--- a/src/BHC24.Api/BHC24.Api.csproj
+++ b/src/BHC24.Api/BHC24.Api.csproj
@@ -7,6 +7,7 @@
+
@@ -25,6 +26,7 @@
+
diff --git a/src/BHC24.Api/Controllers/AuthController.cs b/src/BHC24.Api/Controllers/AuthController.cs
new file mode 100644
index 0000000..e139f9e
--- /dev/null
+++ b/src/BHC24.Api/Controllers/AuthController.cs
@@ -0,0 +1,70 @@
+using System.IdentityModel.Tokens.Jwt;
+using System.Security.Claims;
+using System.Text;
+using BHC24.Api.Persistence.Models;
+using Microsoft.AspNetCore.Identity;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.IdentityModel.Tokens;
+using JwtRegisteredClaimNames = Microsoft.IdentityModel.JsonWebTokens.JwtRegisteredClaimNames;
+
+namespace BHC24.Api.Controllers;
+
+[Route("api/[controller]")]
+[ApiController]
+public class AuthController : ControllerBase
+{
+ private readonly UserManager _userManager;
+ private readonly IConfiguration _configuration;
+
+ public AuthController(UserManager userManager, IConfiguration configuration)
+ {
+ _userManager = userManager;
+ _configuration = configuration;
+ }
+
+ [HttpPost("register")]
+ public async Task Register([FromBody] RegisterRequest request)
+ {
+ var user = new AppUser { UserName = request.Email, Email = request.Email };
+ var result = await _userManager.CreateAsync(user, request.Password);
+ if (result.Succeeded)
+ {
+ return Ok();
+ }
+ return BadRequest(result.Errors);
+ }
+
+ [HttpPost("login")]
+ public async Task Login([FromBody] LoginRequest request)
+ {
+ var user = await _userManager.FindByEmailAsync(request.Email);
+ if (user != null && await _userManager.CheckPasswordAsync(user, request.Password))
+ {
+ var token = GenerateJwtToken(user);
+ return Ok(new { Token = token });
+ }
+ return Unauthorized();
+ }
+
+ private string GenerateJwtToken(AppUser user)
+ {
+ var claims = new[]
+ {
+ new Claim(JwtRegisteredClaimNames.Sub, user.UserName),
+ new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
+ new Claim(ClaimTypes.NameIdentifier, user.Id.ToString())
+ };
+
+ var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["Jwt:Key"]));
+ var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
+
+ var token = new JwtSecurityToken(
+ issuer: _configuration["Jwt:Issuer"],
+ audience: _configuration["Jwt:Audience"],
+ claims: claims,
+ expires: DateTime.Now.AddDays(1),
+ signingCredentials: creds);
+
+ return new JwtSecurityTokenHandler().WriteToken(token);
+ }
+}
\ No newline at end of file
diff --git a/src/BHC24.Api/Models/LoginRequest.cs b/src/BHC24.Api/Models/LoginRequest.cs
new file mode 100644
index 0000000..1af4e7f
--- /dev/null
+++ b/src/BHC24.Api/Models/LoginRequest.cs
@@ -0,0 +1,7 @@
+namespace BHC24.Api.Controllers;
+
+public class LoginRequest
+{
+ public string Email { get; set; }
+ public string Password { get; set; }
+}
\ No newline at end of file
diff --git a/src/BHC24.Api/Models/RegisterRequest.cs b/src/BHC24.Api/Models/RegisterRequest.cs
new file mode 100644
index 0000000..17aca48
--- /dev/null
+++ b/src/BHC24.Api/Models/RegisterRequest.cs
@@ -0,0 +1,7 @@
+namespace BHC24.Api.Controllers;
+
+public class RegisterRequest
+{
+ public string Email { get; set; }
+ public string Password { get; set; }
+}
\ No newline at end of file
diff --git a/src/BHC24.Api/Program.cs b/src/BHC24.Api/Program.cs
index 4d805ed..3ef705e 100644
--- a/src/BHC24.Api/Program.cs
+++ b/src/BHC24.Api/Program.cs
@@ -1,8 +1,13 @@
+using System.Text;
using BHC24.Api.Extensions;
using BHC24.Api.Persistence;
using BHC24.Api.Persistence.Models;
+using BHC24.Api.Services;
+using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
+using Microsoft.IdentityModel.Tokens;
+using Microsoft.OpenApi.Models;
using Serilog;
var builder = WebApplication.CreateBuilder(args);
@@ -11,7 +16,35 @@
// Add services to the container.
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
-builder.Services.AddSwaggerGen();
+builder.Services.AddSwaggerGen(c =>
+{
+ c.SwaggerDoc("v1", new OpenApiInfo { Title = "Open Project Platform", Version = "v1" });
+
+ c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
+ {
+ In = ParameterLocation.Header,
+ Description = "Please enter a valid token",
+ Name = "Authorization",
+ Type = SecuritySchemeType.Http,
+ Scheme = "bearer",
+ BearerFormat = "JWT"
+ });
+
+ c.AddSecurityRequirement(new OpenApiSecurityRequirement
+ {
+ {
+ new OpenApiSecurityScheme
+ {
+ Reference = new OpenApiReference
+ {
+ Type = ReferenceType.SecurityScheme,
+ Id = "Bearer"
+ }
+ },
+ new string[] { }
+ }
+ });
+});
builder.Services.AddDbContext();
@@ -35,6 +68,32 @@
.AllowAnyHeader();
}));
+builder.Services.AddAuthentication(options =>
+ {
+ options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
+ options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
+ })
+ .AddJwtBearer(options =>
+ {
+ options.TokenValidationParameters = new TokenValidationParameters
+ {
+ ValidateIssuer = true,
+ ValidateAudience = true,
+ ValidateLifetime = true,
+ ValidateIssuerSigningKey = true,
+ ValidIssuer = builder.Configuration["Jwt:Issuer"],
+ ValidAudience = builder.Configuration["Jwt:Audience"],
+ IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Key"]))
+ };
+ });
+
+builder.Services.AddHttpContextAccessor();
+builder.Services.AddAuthorization();
+
+
+builder.Services
+ .AddScoped();
+
builder.Services.AddControllers();
builder.Services.AddAntiforgery();
@@ -55,12 +114,12 @@
app.Map("/", () => Results.Redirect("/swagger"));
app.UseRouting();
+app.UseAuthentication();
+app.UseAuthorization();
+
app.UseAntiforgery();
app.UseCors("MyPolicy");
-app.UseEndpoints(endpoints =>
-{
- endpoints.MapControllers();
-});
+app.UseEndpoints(endpoints => { endpoints.MapControllers(); });
using var scope = app.Services.CreateScope();
var context = scope.ServiceProvider.GetService();
diff --git a/src/BHC24.Api/Services/AuthUserProvider.cs b/src/BHC24.Api/Services/AuthUserProvider.cs
new file mode 100644
index 0000000..ce39061
--- /dev/null
+++ b/src/BHC24.Api/Services/AuthUserProvider.cs
@@ -0,0 +1,34 @@
+using System.Security.Claims;
+using BHC24.Api.Persistence.Models;
+using Microsoft.AspNetCore.Identity;
+
+namespace BHC24.Api.Services;
+
+public class AuthUserProvider
+{
+ private readonly UserManager _userManager;
+ private readonly IHttpContextAccessor _httpContextAccessor;
+
+ public AuthUserProvider(UserManager userManager, IHttpContextAccessor httpContextAccessor)
+ {
+ _userManager = userManager;
+ _httpContextAccessor = httpContextAccessor;
+ }
+
+ public async Task GetAsync()
+ {
+ string? userEmail = _httpContextAccessor.HttpContext?.User.FindFirstValue(ClaimTypes.NameIdentifier);
+ if (string.IsNullOrEmpty(userEmail))
+ {
+ throw new InvalidOperationException("User is not authenticated");
+ }
+
+ AppUser? user = await _userManager.FindByEmailAsync(userEmail);
+ if (user == null)
+ {
+ throw new InvalidOperationException("User not found");
+ }
+
+ return user;
+ }
+}
\ No newline at end of file
diff --git a/src/BHC24.Api/appsettings.json b/src/BHC24.Api/appsettings.json
index 10f68b8..188092d 100644
--- a/src/BHC24.Api/appsettings.json
+++ b/src/BHC24.Api/appsettings.json
@@ -5,5 +5,10 @@
"Microsoft.AspNetCore": "Warning"
}
},
- "AllowedHosts": "*"
+ "AllowedHosts": "*",
+ "Jwt": {
+ "Key": "MIHcAgEBBEIBFAXHNII96JvdSLTE9TVt0OTV3GQTTJPWX82FcjglFtZDGmRX6e+y\nzJlEGJkw32s4V4eEYnMgpR7rZ59Acnwt2CegBwYFK4EEACOhgYkDgYYABAGeOCFo\nGy0W4mMZWH402I8CVPGbzjoFz88cv6I2HQQGiVtnaXQcuERUuC1+e0SfDqNuYfCt\nv45wTwAttS0cLT1jbQGUPmSljatd/TO6oQbRgB8ko8b/09pWjCCylz99GN1tzam4\n5D8F2J4ePFHLFE59cpMRXkv0aRAHRetGIt9rAF/1lg==",
+ "Issuer": "YourIssuer",
+ "Audience": "YourAudience"
+ }
}