Adding authentication and authorization flow
This commit is contained in:
@@ -21,6 +21,21 @@
|
|||||||
"Name": "MIT License",
|
"Name": "MIT License",
|
||||||
"Url": "https://github.com/csimonapastore/BasicDotnetTemplate/blob/main/LICENSE.md"
|
"Url": "https://github.com/csimonapastore/BasicDotnetTemplate/blob/main/LICENSE.md"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"Logging": {
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Information",
|
||||||
|
"Microsoft.AspNetCore": "Warning"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"JWTSettings": {
|
||||||
|
"ValidAudience": "http://localhost:4200",
|
||||||
|
"ValidIssuer": "http://localhost:5000",
|
||||||
|
"Secret": "JWTAuthenticationHIGHsecuredPasswordVVVp1OH7Xzyr",
|
||||||
|
"ExpiredAfterMinsOfInactivity": 15
|
||||||
|
},
|
||||||
|
"EncryptionSettings": {
|
||||||
|
"Salt": "S7VIidfXQf1tOQYX"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
42
MainProject.Tests/JsonData/invalidCryptAppsettings.json
Normal file
42
MainProject.Tests/JsonData/invalidCryptAppsettings.json
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
{
|
||||||
|
"AppSettings" :
|
||||||
|
{
|
||||||
|
"Settings": {
|
||||||
|
"Name": "MainProject",
|
||||||
|
"Version": "v1.0",
|
||||||
|
"Description": "This template contains basic configuration for a .Net 8 backend"
|
||||||
|
},
|
||||||
|
"DatabaseSettings": {
|
||||||
|
"SqlServerConnectionString": "SQLSERVER_DB_SERVER",
|
||||||
|
"MongoDbConnectionString": "MONGO_DB_SERVER",
|
||||||
|
"PostgreSQLConnectionString": "POSTGRESQL_DB_SERVER"
|
||||||
|
},
|
||||||
|
"OpenApiSettings": {
|
||||||
|
"TermsOfServiceUrl": "",
|
||||||
|
"OpenApiContact": {
|
||||||
|
"Name": "",
|
||||||
|
"Url": ""
|
||||||
|
},
|
||||||
|
"OpenApiLicense": {
|
||||||
|
"Name": "MIT License",
|
||||||
|
"Url": "https://github.com/csimonapastore/BasicDotnetTemplate/blob/main/LICENSE.md"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Logging": {
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Information",
|
||||||
|
"Microsoft.AspNetCore": "Warning"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"JWTSettings": {
|
||||||
|
"ValidAudience": "http://localhost:4200",
|
||||||
|
"ValidIssuer": "http://localhost:5000",
|
||||||
|
"Secret": "JWTAuthenticationHIGHsecuredPasswordVVVp1OH7Xzyr",
|
||||||
|
"ExpiredAfterMinsOfInactivity": 15
|
||||||
|
},
|
||||||
|
"EncryptionSettings": {
|
||||||
|
"Salt": "AAAAA"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
108
MainProject.Tests/Utils/CryptoUtils_Tests.cs
Normal file
108
MainProject.Tests/Utils/CryptoUtils_Tests.cs
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
using System;
|
||||||
|
using BasicDotnetTemplate.MainProject.Models.Settings;
|
||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using BasicDotnetTemplate.MainProject.Utils;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
|
||||||
|
|
||||||
|
namespace BasicDotnetTemplate.MainProject.Tests;
|
||||||
|
|
||||||
|
[TestClass]
|
||||||
|
public class CryptoUtils_Tests
|
||||||
|
{
|
||||||
|
[TestMethod]
|
||||||
|
public void Decrypt_Success()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
string encryptedData = "d2ejdI1f4GYpq2kTB1nmeQkZXqR3QSxH8Yqkl7iv7zgfQ13qG/0dUUsreG/WGHWRBE5mVWaV43A=";
|
||||||
|
WebApplicationBuilder builder = WebApplication.CreateBuilder(Array.Empty<string>());
|
||||||
|
AppSettings appSettings = ProgramUtils.AddConfiguration(ref builder, System.AppDomain.CurrentDomain.BaseDirectory + "/JsonData");
|
||||||
|
CryptUtils cryptoUtils = new CryptUtils(appSettings);
|
||||||
|
var decryptedData = cryptoUtils.Decrypt(encryptedData);
|
||||||
|
var isEqual = decryptedData == "ThisIsASuccessfullTest";
|
||||||
|
Assert.IsTrue(isEqual);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine(ex.InnerException);
|
||||||
|
Assert.Fail($"An exception was thrown: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void Decrypt_Error()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
string encryptedData = "d1ejdI1f4GYpq2kTB1nmeQkZXqR3QSxH8Yqkl7iv7zgfQ13qG/0dUUsreG/WGHWRBE5mVWaV43A=";
|
||||||
|
WebApplicationBuilder builder = WebApplication.CreateBuilder(Array.Empty<string>());
|
||||||
|
AppSettings appSettings = ProgramUtils.AddConfiguration(ref builder, System.AppDomain.CurrentDomain.BaseDirectory + "/JsonData");
|
||||||
|
CryptUtils cryptoUtils = new CryptUtils(appSettings);
|
||||||
|
var decryptedData = cryptoUtils.Decrypt(encryptedData);
|
||||||
|
var isEqual = decryptedData == "ThisIsASuccessfullTest";
|
||||||
|
Assert.IsFalse(isEqual);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine(ex.InnerException);
|
||||||
|
Assert.Fail($"An exception was thrown: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void Decrypt_ArgumentException()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
string encryptedData = "d1ejdI1f4GYpq2kTB1nmeQkZXqR3QSxH8Yqkl7iv7zgfQ13qG/0dUUsreG/WGHWRBE5mVWaV43A=";
|
||||||
|
WebApplicationBuilder builder = WebApplication.CreateBuilder(Array.Empty<string>());
|
||||||
|
AppSettings appSettings = ProgramUtils.AddConfiguration(ref builder, System.AppDomain.CurrentDomain.BaseDirectory + "/JsonData", "invalidCryptAppsettings.json");
|
||||||
|
CryptUtils cryptoUtils = new CryptUtils(appSettings);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var decryptedData = cryptoUtils.Decrypt(encryptedData);
|
||||||
|
Assert.Fail($"Expected exception instead of response: {decryptedData}");
|
||||||
|
}
|
||||||
|
catch (ArgumentException argumentException)
|
||||||
|
{
|
||||||
|
Assert.IsInstanceOfType(argumentException, typeof(ArgumentException));
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Assert.IsInstanceOfType(exception, typeof(ArgumentException));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine(ex.InnerException);
|
||||||
|
Assert.Fail($"An exception was thrown: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void Decrypt_Empty()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
string encryptedData = "WGHWRBE5mVWaV=";
|
||||||
|
WebApplicationBuilder builder = WebApplication.CreateBuilder(Array.Empty<string>());
|
||||||
|
AppSettings appSettings = ProgramUtils.AddConfiguration(ref builder, System.AppDomain.CurrentDomain.BaseDirectory + "/JsonData");
|
||||||
|
CryptUtils cryptoUtils = new CryptUtils(appSettings);
|
||||||
|
var decryptedData = cryptoUtils.Decrypt(encryptedData);
|
||||||
|
var isEqual = decryptedData == String.Empty;
|
||||||
|
Assert.IsTrue(isEqual);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine(ex.InnerException);
|
||||||
|
Assert.Fail($"An exception was thrown: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,11 +1,3 @@
|
|||||||
using System;
|
|
||||||
using System.Reflection;
|
|
||||||
using System.Net;
|
|
||||||
using System.Net.Http;
|
|
||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
|
||||||
using BasicDotnetTemplate.MainProject;
|
|
||||||
using BasicDotnetTemplate.MainProject.Models.Api.Response;
|
|
||||||
using Microsoft.Extensions.DependencyModel.Resolution;
|
|
||||||
using BasicDotnetTemplate.MainProject.Models.Settings;
|
using BasicDotnetTemplate.MainProject.Models.Settings;
|
||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
using BasicDotnetTemplate.MainProject.Utils;
|
using BasicDotnetTemplate.MainProject.Utils;
|
||||||
|
|||||||
62
MainProject/Controllers/AuthController.cs
Normal file
62
MainProject/Controllers/AuthController.cs
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using BasicDotnetTemplate.MainProject.Core.Attributes;
|
||||||
|
using BasicDotnetTemplate.MainProject.Models.Api.Request.Auth;
|
||||||
|
using BasicDotnetTemplate.MainProject.Models.Api.Response;
|
||||||
|
using BasicDotnetTemplate.MainProject.Services;
|
||||||
|
|
||||||
|
namespace BasicDotnetTemplate.MainProject.Controllers
|
||||||
|
{
|
||||||
|
[Route("[controller]")]
|
||||||
|
public class AuthController : BaseController
|
||||||
|
{
|
||||||
|
private IAuthService _authService;
|
||||||
|
public AuthController(
|
||||||
|
IConfiguration configuration,
|
||||||
|
IAuthService authService
|
||||||
|
) : base(configuration)
|
||||||
|
{
|
||||||
|
this._authService = authService;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost("authenticate")]
|
||||||
|
[ProducesResponseType<string>(StatusCodes.Status200OK)]
|
||||||
|
[ProducesResponseType<BaseResponse>(StatusCodes.Status404NotFound)]
|
||||||
|
[ProducesResponseType<BaseResponse>(StatusCodes.Status400BadRequest)]
|
||||||
|
[ProducesResponseType<BaseResponse>(StatusCodes.Status500InternalServerError)]
|
||||||
|
public async Task<IActionResult> AuthenticateAsync([FromBody] AuthenticateRequest request)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (
|
||||||
|
request == null ||
|
||||||
|
request.Data == null ||
|
||||||
|
String.IsNullOrEmpty(request.Data.Username) ||
|
||||||
|
String.IsNullOrEmpty(request.Data.Password)
|
||||||
|
)
|
||||||
|
{
|
||||||
|
return BadRequest("Request is not well formed");
|
||||||
|
}
|
||||||
|
var data = await this._authService.AuthenticateAsync(request.Data);
|
||||||
|
|
||||||
|
if (data == null)
|
||||||
|
{
|
||||||
|
return NotFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Success(String.Empty, data);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
var message = "Something went wrong";
|
||||||
|
if (!String.IsNullOrEmpty(exception?.Message))
|
||||||
|
{
|
||||||
|
message += $". {exception?.Message}";
|
||||||
|
}
|
||||||
|
return InternalServerError(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -37,6 +37,12 @@ namespace BasicDotnetTemplate.MainProject.Controllers
|
|||||||
return StatusCode((int)HttpStatusCode.OK, CreateResponse(HttpStatusCode.OK, message, data));
|
return StatusCode((int)HttpStatusCode.OK, CreateResponse(HttpStatusCode.OK, message, data));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected IActionResult NotModified(string message, object? data = null)
|
||||||
|
{
|
||||||
|
message = String.IsNullOrEmpty(message) ? "Not modified" : message;
|
||||||
|
return StatusCode((int)HttpStatusCode.NotModified, CreateResponse(HttpStatusCode.NotModified, message, data));
|
||||||
|
}
|
||||||
|
|
||||||
protected IActionResult NotFound(string message, object? data = null)
|
protected IActionResult NotFound(string message, object? data = null)
|
||||||
{
|
{
|
||||||
message = String.IsNullOrEmpty(message) ? "Not found" : message;
|
message = String.IsNullOrEmpty(message) ? "Not found" : message;
|
||||||
|
|||||||
32
MainProject/Core/Attributes/JwtAuthorizationAttribute .cs
Normal file
32
MainProject/Core/Attributes/JwtAuthorizationAttribute .cs
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Filters;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.IdentityModel.Tokens;
|
||||||
|
using System;
|
||||||
|
using System.IdentityModel.Tokens.Jwt;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
|
||||||
|
namespace BasicDotnetTemplate.MainProject.Core.Attributes
|
||||||
|
{
|
||||||
|
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
|
||||||
|
public class JwtAuthorizationAttribute : Attribute, IAuthorizationFilter
|
||||||
|
{
|
||||||
|
private readonly string? _policyName;
|
||||||
|
|
||||||
|
public JwtAuthorizationAttribute() { }
|
||||||
|
public JwtAuthorizationAttribute(string policyName)
|
||||||
|
{
|
||||||
|
_policyName = policyName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnAuthorization(AuthorizationFilterContext context)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
|
<Configuration>Debug</Configuration>
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
@@ -13,18 +14,22 @@
|
|||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Microsoft.AspNetCore" Version="2.2.0" />
|
<PackageReference Include="Microsoft.AspNetCore" Version="2.2.0" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.1" />
|
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.13" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Abstractions" Version="9.0.1" />
|
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.13" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.8">
|
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.2" />
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Abstractions" Version="9.0.2" />
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.2">
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.8" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="9.0.2" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.8">
|
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="9.0.2" />
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="9.0.2">
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
|
||||||
|
<PackageReference Include="Microsoft.Identity.Web" Version="3.7.1" />
|
||||||
<PackageReference Include="MongoDB.Driver" Version="2.28.0" />
|
<PackageReference Include="MongoDB.Driver" Version="2.28.0" />
|
||||||
<PackageReference Include="MongoDB.EntityFrameworkCore" Version="8.1.0" />
|
<PackageReference Include="MongoDB.EntityFrameworkCore" Version="8.1.0" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||||
|
|||||||
9
MainProject/Models/Api/Common/Role/UserRole.cs
Normal file
9
MainProject/Models/Api/Common/Role/UserRole.cs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
namespace BasicDotnetTemplate.MainProject.Models.Api.Common.Role;
|
||||||
|
|
||||||
|
public class UserRole
|
||||||
|
{
|
||||||
|
#nullable enable
|
||||||
|
public string? Guid { get; set; }
|
||||||
|
public string? Name { get; set; }
|
||||||
|
#nullable disable
|
||||||
|
}
|
||||||
19
MainProject/Models/Api/Common/User/AuthenticatedUser.cs
Normal file
19
MainProject/Models/Api/Common/User/AuthenticatedUser.cs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
using BasicDotnetTemplate.MainProject.Models.Api.Common.Role;
|
||||||
|
|
||||||
|
namespace BasicDotnetTemplate.MainProject.Models.Api.Common.User;
|
||||||
|
|
||||||
|
public class AuthenticatedUser
|
||||||
|
{
|
||||||
|
#nullable enable
|
||||||
|
public string? Guid { get; set; }
|
||||||
|
public string? Username { get; set; }
|
||||||
|
public string? FirstName { get; set; }
|
||||||
|
public string? LastName { get; set; }
|
||||||
|
public string? Email { get; set; }
|
||||||
|
public UserRole? Role { get; set; }
|
||||||
|
#nullable disable
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
13
MainProject/Models/Api/Data/Auth/AuthenticateRequestData.cs
Normal file
13
MainProject/Models/Api/Data/Auth/AuthenticateRequestData.cs
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
namespace BasicDotnetTemplate.MainProject.Models.Api.Data.Auth;
|
||||||
|
|
||||||
|
public class AuthenticateRequestData
|
||||||
|
{
|
||||||
|
#nullable enable
|
||||||
|
public string? Username { get; set; }
|
||||||
|
public string? Password { get; set; }
|
||||||
|
#nullable disable
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
14
MainProject/Models/Api/Request/Auth/AuthenticateRequest.cs
Normal file
14
MainProject/Models/Api/Request/Auth/AuthenticateRequest.cs
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
using BasicDotnetTemplate.MainProject.Models.Api.Data.Auth;
|
||||||
|
|
||||||
|
namespace BasicDotnetTemplate.MainProject.Models.Api.Request.Auth;
|
||||||
|
|
||||||
|
public class AuthenticateRequest
|
||||||
|
{
|
||||||
|
#nullable enable
|
||||||
|
public AuthenticateRequestData? Data { get; set; }
|
||||||
|
#nullable disable
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -3,6 +3,7 @@ namespace BasicDotnetTemplate.MainProject.Models.Database.SqlServer
|
|||||||
public class Base
|
public class Base
|
||||||
{
|
{
|
||||||
public int Id { get; set; }
|
public int Id { get; set; }
|
||||||
|
public string Guid { get; set; }
|
||||||
public DateTime CreationTime { get; set; }
|
public DateTime CreationTime { get; set; }
|
||||||
public int CreationUserId { get; set; }
|
public int CreationUserId { get; set; }
|
||||||
public DateTime UpdateTime { get; set; }
|
public DateTime UpdateTime { get; set; }
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ public class AppSettings
|
|||||||
public PrivateSettings? PrivateSettings { get; set; }
|
public PrivateSettings? PrivateSettings { get; set; }
|
||||||
public OpenApiSettings? OpenApiSettings { get; set; }
|
public OpenApiSettings? OpenApiSettings { get; set; }
|
||||||
public DatabaseSettings? DatabaseSettings { get; set; }
|
public DatabaseSettings? DatabaseSettings { get; set; }
|
||||||
|
public JWTSettings? JWTSettings { get; set; }
|
||||||
|
public EncryptionSettings? EncryptionSettings { get; set; }
|
||||||
|
|
||||||
#nullable disable
|
#nullable disable
|
||||||
}
|
}
|
||||||
8
MainProject/Models/Settings/EncryptionSettings.cs
Normal file
8
MainProject/Models/Settings/EncryptionSettings.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
namespace BasicDotnetTemplate.MainProject.Models.Settings;
|
||||||
|
|
||||||
|
public class EncryptionSettings
|
||||||
|
{
|
||||||
|
#nullable enable
|
||||||
|
public string? Salt { get; set; }
|
||||||
|
#nullable disable
|
||||||
|
}
|
||||||
12
MainProject/Models/Settings/JWTSettings.cs
Normal file
12
MainProject/Models/Settings/JWTSettings.cs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
namespace BasicDotnetTemplate.MainProject.Models.Settings;
|
||||||
|
|
||||||
|
public class JWTSettings
|
||||||
|
{
|
||||||
|
#nullable enable
|
||||||
|
public string? ValidAudience { get; set; }
|
||||||
|
public string? ValidIssuer { get; set; }
|
||||||
|
public string? Secret { get; set; }
|
||||||
|
public int? ExpiredAfterMinsOfInactivity { get; set; }
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
}
|
||||||
38
MainProject/Services/AuthService.cs
Normal file
38
MainProject/Services/AuthService.cs
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
|
||||||
|
using BasicDotnetTemplate.MainProject.Models.Api.Data.Auth;
|
||||||
|
using BasicDotnetTemplate.MainProject.Models.Api.Common.User;
|
||||||
|
using BasicDotnetTemplate.MainProject.Utils;
|
||||||
|
|
||||||
|
namespace BasicDotnetTemplate.MainProject.Services;
|
||||||
|
|
||||||
|
public interface IAuthService
|
||||||
|
{
|
||||||
|
Task<AuthenticatedUser?> AuthenticateAsync(AuthenticateRequestData data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class AuthService : BaseService, IAuthService
|
||||||
|
{
|
||||||
|
protected CryptUtils _cryptUtils;
|
||||||
|
|
||||||
|
public AuthService(
|
||||||
|
IConfiguration configuration
|
||||||
|
) : base(configuration)
|
||||||
|
{
|
||||||
|
_cryptUtils = new CryptUtils(_appSettings);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<AuthenticatedUser?> AuthenticateAsync(AuthenticateRequestData data)
|
||||||
|
{
|
||||||
|
AuthenticatedUser? authenticatedUser = null;
|
||||||
|
var decryptedUsername = _cryptUtils.Decrypt(data.Username ?? String.Empty);
|
||||||
|
var decryptedPassword = _cryptUtils.Decrypt(data.Password ?? String.Empty);
|
||||||
|
|
||||||
|
if (!String.IsNullOrEmpty(decryptedUsername) && !String.IsNullOrEmpty(decryptedPassword))
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return authenticatedUser;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
24
MainProject/Services/BaseService.cs
Normal file
24
MainProject/Services/BaseService.cs
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
using Microsoft.IdentityModel.Tokens;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IdentityModel.Tokens.Jwt;
|
||||||
|
using System.Security.Claims;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using BasicDotnetTemplate.MainProject.Models.Settings;
|
||||||
|
|
||||||
|
namespace BasicDotnetTemplate.MainProject.Services;
|
||||||
|
|
||||||
|
public class BaseService
|
||||||
|
{
|
||||||
|
protected readonly IConfiguration _configuration;
|
||||||
|
protected readonly AppSettings _appSettings;
|
||||||
|
|
||||||
|
public BaseService(IConfiguration configuration)
|
||||||
|
{
|
||||||
|
_configuration = configuration;
|
||||||
|
_appSettings = new AppSettings();
|
||||||
|
_configuration.GetSection("AppSettings").Bind(_appSettings);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
32
MainProject/Services/JwtService.cs
Normal file
32
MainProject/Services/JwtService.cs
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
using Microsoft.IdentityModel.Tokens;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IdentityModel.Tokens.Jwt;
|
||||||
|
using System.Security.Claims;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace BasicDotnetTemplate.MainProject.Services;
|
||||||
|
|
||||||
|
public interface IJwtService
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public class JwtService : BaseService, IJwtService
|
||||||
|
{
|
||||||
|
private readonly string _jwtKey;
|
||||||
|
private readonly string _jwtIssuer;
|
||||||
|
private readonly string _jwtAudience;
|
||||||
|
|
||||||
|
public JwtService(
|
||||||
|
IConfiguration configuration
|
||||||
|
) : base(configuration)
|
||||||
|
{
|
||||||
|
_jwtKey = _appSettings?.JWTSettings?.Secret ?? String.Empty;
|
||||||
|
_jwtIssuer = _appSettings?.JWTSettings?.ValidIssuer ?? String.Empty;
|
||||||
|
_jwtAudience = _appSettings?.JWTSettings?.ValidAudience ?? String.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
59
MainProject/Utils/CryptoUtils.cs
Normal file
59
MainProject/Utils/CryptoUtils.cs
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
using System;
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
using System.Text;
|
||||||
|
using BasicDotnetTemplate.MainProject.Models.Settings;
|
||||||
|
|
||||||
|
namespace BasicDotnetTemplate.MainProject.Utils;
|
||||||
|
public class CryptUtils
|
||||||
|
{
|
||||||
|
private readonly string secretKey;
|
||||||
|
private const int M = 16;
|
||||||
|
private const int N = 32;
|
||||||
|
private readonly NLog.Logger Logger = NLog.LogManager.GetCurrentClassLogger();
|
||||||
|
public CryptUtils(AppSettings appSettings)
|
||||||
|
{
|
||||||
|
secretKey = appSettings.EncryptionSettings?.Salt ?? String.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Decrypt(string encryptedData)
|
||||||
|
{
|
||||||
|
var decrypted = String.Empty;
|
||||||
|
|
||||||
|
if (String.IsNullOrEmpty(this.secretKey) || this.secretKey.Length < M)
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Unable to proceed with decryption due to invalid settings");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!String.IsNullOrEmpty(encryptedData) && encryptedData.Length > N)
|
||||||
|
{
|
||||||
|
var iv = encryptedData.Substring(0, M);
|
||||||
|
|
||||||
|
var cipherText = encryptedData.Substring(N);
|
||||||
|
var fullCipher = Convert.FromBase64String(cipherText);
|
||||||
|
|
||||||
|
using (var aes = Aes.Create())
|
||||||
|
{
|
||||||
|
aes.Key = Encoding.UTF8.GetBytes(this.secretKey);
|
||||||
|
aes.IV = Encoding.UTF8.GetBytes(iv);
|
||||||
|
|
||||||
|
using (var decryptor = aes.CreateDecryptor(aes.Key, aes.IV))
|
||||||
|
{
|
||||||
|
using (var msDecrypt = new MemoryStream(fullCipher))
|
||||||
|
{
|
||||||
|
using (var cryptoStream = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
|
||||||
|
{
|
||||||
|
using (var srDecrypt = new StreamReader(cryptoStream))
|
||||||
|
{
|
||||||
|
decrypted = srDecrypt.ReadToEnd();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return decrypted;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@@ -4,6 +4,7 @@ using MongoDB.Driver;
|
|||||||
using NLog;
|
using NLog;
|
||||||
using BasicDotnetTemplate.MainProject.Core.Database;
|
using BasicDotnetTemplate.MainProject.Core.Database;
|
||||||
using BasicDotnetTemplate.MainProject.Models.Settings;
|
using BasicDotnetTemplate.MainProject.Models.Settings;
|
||||||
|
using BasicDotnetTemplate.MainProject.Services;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -41,7 +42,6 @@ public static class ProgramUtils
|
|||||||
|
|
||||||
return appSettings;
|
return appSettings;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static OpenApiInfo CreateOpenApiInfo(AppSettings appSettings)
|
public static OpenApiInfo CreateOpenApiInfo(AppSettings appSettings)
|
||||||
{
|
{
|
||||||
OpenApiInfo openApiInfo = new OpenApiInfo
|
OpenApiInfo openApiInfo = new OpenApiInfo
|
||||||
@@ -83,11 +83,53 @@ public static class ProgramUtils
|
|||||||
builder.Services.AddSwaggerGen(options =>
|
builder.Services.AddSwaggerGen(options =>
|
||||||
{
|
{
|
||||||
options.SwaggerDoc("v1", CreateOpenApiInfo(appSettings));
|
options.SwaggerDoc("v1", CreateOpenApiInfo(appSettings));
|
||||||
|
|
||||||
|
options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
|
||||||
|
{
|
||||||
|
Description = "Inserisci il Bearer Token nel formato **'Bearer {token}'**",
|
||||||
|
Name = "Authorization",
|
||||||
|
In = ParameterLocation.Header,
|
||||||
|
Type = SecuritySchemeType.Http,
|
||||||
|
Scheme = "Bearer"
|
||||||
|
});
|
||||||
|
|
||||||
|
options.AddSecurityDefinition("ApiKey", new OpenApiSecurityScheme
|
||||||
|
{
|
||||||
|
Description = "Inserisci la tua API Key nel campo appropriato.",
|
||||||
|
Name = "ApiKey",
|
||||||
|
In = ParameterLocation.Header,
|
||||||
|
Type = SecuritySchemeType.ApiKey
|
||||||
|
});
|
||||||
|
|
||||||
|
options.AddSecurityRequirement(new OpenApiSecurityRequirement
|
||||||
|
{
|
||||||
|
{
|
||||||
|
new OpenApiSecurityScheme
|
||||||
|
{
|
||||||
|
Reference = new OpenApiReference
|
||||||
|
{
|
||||||
|
Type = ReferenceType.SecurityScheme,
|
||||||
|
Id = "Bearer"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
new string[] {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
new OpenApiSecurityScheme
|
||||||
|
{
|
||||||
|
Reference = new OpenApiReference
|
||||||
|
{
|
||||||
|
Type = ReferenceType.SecurityScheme,
|
||||||
|
Id = "ApiKey"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
new string[] {}
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
Logger.Info("[ProgramUtils][AddOpenApi] Ended swagger doc");
|
Logger.Info("[ProgramUtils][AddOpenApi] Ended swagger doc");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void AddServices(ref WebApplicationBuilder builder)
|
public static void AddServices(ref WebApplicationBuilder builder)
|
||||||
{
|
{
|
||||||
Logger.Info("[ProgramUtils][AddServices] Adding services");
|
Logger.Info("[ProgramUtils][AddServices] Adding services");
|
||||||
@@ -99,7 +141,6 @@ public static class ProgramUtils
|
|||||||
|
|
||||||
Logger.Info("[ProgramUtils][AddServices] Done services");
|
Logger.Info("[ProgramUtils][AddServices] Done services");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void AddMiddlewares(ref WebApplication app)
|
public static void AddMiddlewares(ref WebApplication app)
|
||||||
{
|
{
|
||||||
Logger.Info("[ProgramUtils][AddMiddlewares] Adding middlewares");
|
Logger.Info("[ProgramUtils][AddMiddlewares] Adding middlewares");
|
||||||
@@ -126,7 +167,6 @@ public static class ProgramUtils
|
|||||||
|
|
||||||
Logger.Info("[ProgramUtils][AddMiddlewares] Done middlewares");
|
Logger.Info("[ProgramUtils][AddMiddlewares] Done middlewares");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void AddDbContext(ref WebApplicationBuilder builder, AppSettings appSettings)
|
public static void AddDbContext(ref WebApplicationBuilder builder, AppSettings appSettings)
|
||||||
{
|
{
|
||||||
Logger.Info("[ProgramUtils][AddDbContext] Adding DbContext");
|
Logger.Info("[ProgramUtils][AddDbContext] Adding DbContext");
|
||||||
@@ -168,10 +208,11 @@ public static class ProgramUtils
|
|||||||
messages = String.IsNullOrEmpty(messages) ? "No context" : messages;
|
messages = String.IsNullOrEmpty(messages) ? "No context" : messages;
|
||||||
Logger.Info($"[ProgramUtils][AddDbContext] {messages} added");
|
Logger.Info($"[ProgramUtils][AddDbContext] {messages} added");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void AddScopes(ref WebApplicationBuilder builder)
|
public static void AddScopes(ref WebApplicationBuilder builder)
|
||||||
{
|
{
|
||||||
Logger.Info("[ProgramUtils][AddScopes] Adding scopes");
|
Logger.Info("[ProgramUtils][AddScopes] Adding scopes");
|
||||||
|
builder.Services.AddScoped<IAuthService, AuthService>();
|
||||||
|
builder.Services.AddScoped<IJwtService, JwtService>();
|
||||||
Logger.Info("[ProgramUtils][AddScopes] Done scopes");
|
Logger.Info("[ProgramUtils][AddScopes] Done scopes");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
{
|
{
|
||||||
"Settings": {
|
"Settings": {
|
||||||
"Name": "MainProject",
|
"Name": "MainProject",
|
||||||
"Version": "v1.0",
|
"Version": "v1",
|
||||||
"Description": "This template contains basic configuration for a .Net 8 backend"
|
"Description": "This template contains basic configuration for a .Net 8 backend"
|
||||||
},
|
},
|
||||||
"DatabaseSettings": {
|
"DatabaseSettings": {
|
||||||
@@ -27,6 +27,15 @@
|
|||||||
"Default": "Information",
|
"Default": "Information",
|
||||||
"Microsoft.AspNetCore": "Warning"
|
"Microsoft.AspNetCore": "Warning"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"JWTSettings": {
|
||||||
|
"ValidAudience": "http://localhost:4200",
|
||||||
|
"ValidIssuer": "http://localhost:5000",
|
||||||
|
"Secret": "JWTAuthenticationHIGHsecuredPasswordVVVp1OH7Xzyr",
|
||||||
|
"ExpiredAfterMinsOfInactivity": 15
|
||||||
|
},
|
||||||
|
"EncryptionSettings": {
|
||||||
|
"Salt": "S7VIidfXQf1tOQYX"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user