{V0.4} Added Authentication, easily settable for all routes.

This commit is contained in:
Charles Le Maux 2024-10-03 22:14:04 +02:00
parent 77a34a878d
commit fd6860d102
9 changed files with 198 additions and 24 deletions

119
Controllers/Default.cs Normal file
View File

@ -0,0 +1,119 @@
using System.ComponentModel.DataAnnotations;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.IdentityModel.Tokens;
namespace Simple_API.Controllers
{
[Route("auth/")]
[ApiController]
public class Default(IConfiguration configuration) : ControllerBase
{
public class AuthPayload
{
[DataType(DataType.EmailAddress)]
[EmailAddress(ErrorMessage = "Invalid Email Address.")]
[Required(ErrorMessage = "Email address is required.")]
public string? Email { get; init; } = string.Empty;
[DataType(DataType.Password)]
[Required(ErrorMessage = "Password is required.")]
[RegularExpression(@"^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$",
ErrorMessage = "Password must be at least 8 characters long and contain at least one uppercase letter,"
+ " one lowercase letter, one number, and one special character.")]
public string? Password { get; init; } = string.Empty;
}
[HttpPut("register")]
public IActionResult Register([FromBody] AuthPayload authPayload)
{
return Ok();
}
[HttpPost("login")]
public IActionResult Login([FromBody] AuthPayload authPayload)
{
// Here, you would typically validate the user's credentials against a database.
if (authPayload.Email == "test@example.com" && authPayload.Password == "Password123!")
{
var claims = new[]
{
new Claim(ClaimTypes.Email, authPayload.Email),
new Claim(ClaimTypes.Role, "Admin"),
new Claim(ClaimTypes.GivenName, "Test_ID"),
};
var configKey = configuration["Jwt:Key"];
if (string.IsNullOrEmpty(configKey))
{
return StatusCode(500);
}
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(configKey));
var credentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var token = new JwtSecurityToken(
issuer: configuration["Jwt:Issuer"],
audience: configuration["Jwt:Audience"],
claims: claims,
expires: DateTime.Now.AddMinutes(190),
signingCredentials: credentials);
return Ok(new { token = new JwtSecurityTokenHandler().WriteToken(token) });
}
return Unauthorized();
}
}
[Route("test/")]
[ApiController]
public class Test : ControllerBase
{
public class TestPayload
{
[Required(ErrorMessage = "Data field is required.")]
public string? Data { get; init; } = string.Empty;
}
private const string ProtocolOk = "Protocol tested successfully.";
// GET: test/get
[Authorize]
[HttpGet("get")]
public IActionResult TestGet()
{
return Ok($"GET: {ProtocolOk}");
}
// POST: test/post
[HttpPost("post")]
public IActionResult TestPost([FromBody] TestPayload testPayload)
{
return Ok($"POST: {ProtocolOk} Received: {testPayload.Data}");
}
// PUT: test/put
[HttpPut("put")]
public IActionResult TestPut([FromBody] TestPayload testPayload)
{
return Ok($"PUT: {ProtocolOk} Updated: {testPayload.Data}");
}
// DELETE: test/delete
[HttpDelete("delete")]
public IActionResult TestDelete([FromBody] TestPayload testPayload)
{
return Ok($"DELETE: {ProtocolOk} Deleted: {testPayload.Data}");
}
}
}

6
Controllers/Delete.cs Normal file
View File

@ -0,0 +1,6 @@
namespace Simple_API.Controllers;
public class Delete
{
}

6
Controllers/Get.cs Normal file
View File

@ -0,0 +1,6 @@
namespace Simple_API.Controllers;
public class Get
{
}

6
Controllers/Post.cs Normal file
View File

@ -0,0 +1,6 @@
namespace Simple_API.Controllers;
public class Post
{
}

6
Controllers/Put.cs Normal file
View File

@ -0,0 +1,6 @@
namespace Simple_API.Controllers;
public class Put
{
}

View File

@ -1,8 +1,15 @@
using System.Text;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
//Builder configuration
var builder = WebApplication.CreateBuilder(args); var builder = WebApplication.CreateBuilder(args);
builder.Services.AddEndpointsApiExplorer(); builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(); builder.Services.AddSwaggerGen();
builder.Services.AddControllers();
builder.Services.AddControllersWithViews();
builder.Services.AddCors(options => builder.Services.AddCors(options =>
{ {
options.AddPolicy("AllowAllOrigins", corsBuilder => options.AddPolicy("AllowAllOrigins", corsBuilder =>
@ -13,9 +20,44 @@ builder.Services.AddCors(options =>
}); });
}); });
// JWT Configuration
var jwtSettings = builder.Configuration.GetSection("Jwt");
var key = jwtSettings["Key"];
var issuer = jwtSettings["Issuer"];
var audience = jwtSettings["Audience"];
if (string.IsNullOrEmpty(key))
{
return;
}
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 = issuer,
ValidAudience = audience,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(key))
};
});
// Build
var app = builder.Build(); var app = builder.Build();
// App configuration
app.MapControllers();
if (app.Environment.IsDevelopment()) if (app.Environment.IsDevelopment())
{ {
app.UseSwagger(); app.UseSwagger();
@ -23,31 +65,10 @@ if (app.Environment.IsDevelopment())
} }
app.UseHttpsRedirection(); app.UseHttpsRedirection();
app.UseAuthentication();
app.UseAuthorization();
app.UseCors("AllowAllOrigins"); app.UseCors("AllowAllOrigins");
var summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
app.MapGet("/weatherforecast", () =>
{
var forecast = Enumerable.Range(1, 5).Select(index =>
new WeatherForecast
(
DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
Random.Shared.Next(-20, 55),
summaries[Random.Shared.Next(summaries.Length)]
))
.ToArray();
return forecast;
})
.WithName("GetWeatherForecast")
.WithOpenApi();
app.Run(); app.Run();
internal record WeatherForecast (DateOnly Date, int TemperatureC, string? Summary)
{
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
}

View File

@ -8,6 +8,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.8" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.8"/> <PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.8"/>
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0"/> <PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0"/>
</ItemGroup> </ItemGroup>

View File

@ -4,3 +4,7 @@ GET {{Simple_API_HostAddress}}/weatherforecast/
Accept: application/json Accept: application/json
### ###
GET {{Simple_API_HostAddress}}/test/get
Authorization: Bearer 1
###

View File

@ -5,5 +5,10 @@
"Microsoft.AspNetCore": "Warning" "Microsoft.AspNetCore": "Warning"
} }
}, },
"AllowedHosts": "*" "AllowedHosts": "*",
"Jwt": {
"Key": "9831A382FD7395DD4D4F64B554962^&6@Vw1qR!Lg$+Pz",
"Issuer": "HubHarmonyApiTemplate",
"Audience": "HubHarmonyApiTemplate"
}
} }