Adding authentication and authorization flow
This commit is contained in:
@@ -28,7 +28,7 @@
|
|||||||
"Microsoft.AspNetCore": "Warning"
|
"Microsoft.AspNetCore": "Warning"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"JWTSettings": {
|
"JwtSettings": {
|
||||||
"ValidAudience": "http://localhost:4200",
|
"ValidAudience": "http://localhost:4200",
|
||||||
"ValidIssuer": "http://localhost:5000",
|
"ValidIssuer": "http://localhost:5000",
|
||||||
"Secret": "JWTAuthenticationHIGHsecuredPasswordVVVp1OH7Xzyr",
|
"Secret": "JWTAuthenticationHIGHsecuredPasswordVVVp1OH7Xzyr",
|
||||||
|
|||||||
@@ -28,7 +28,7 @@
|
|||||||
"Microsoft.AspNetCore": "Warning"
|
"Microsoft.AspNetCore": "Warning"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"JWTSettings": {
|
"JwtSettings": {
|
||||||
"ValidAudience": "http://localhost:4200",
|
"ValidAudience": "http://localhost:4200",
|
||||||
"ValidIssuer": "http://localhost:5000",
|
"ValidIssuer": "http://localhost:5000",
|
||||||
"Secret": "JWTAuthenticationHIGHsecuredPasswordVVVp1OH7Xzyr",
|
"Secret": "JWTAuthenticationHIGHsecuredPasswordVVVp1OH7Xzyr",
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ using System.IdentityModel.Tokens.Jwt;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using BasicDotnetTemplate.MainProject.Models.Settings;
|
||||||
|
|
||||||
namespace BasicDotnetTemplate.MainProject.Core.Attributes
|
namespace BasicDotnetTemplate.MainProject.Core.Attributes
|
||||||
{
|
{
|
||||||
@@ -24,9 +25,66 @@ namespace BasicDotnetTemplate.MainProject.Core.Attributes
|
|||||||
|
|
||||||
public void OnAuthorization(AuthorizationFilterContext context)
|
public void OnAuthorization(AuthorizationFilterContext context)
|
||||||
{
|
{
|
||||||
return;
|
// If [AllowAnonymous], skip
|
||||||
|
if (context.ActionDescriptor.EndpointMetadata.Any(em => em is AllowAnonymousAttribute))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var configuration = context.HttpContext.RequestServices.GetRequiredService<IConfiguration>();
|
||||||
|
var appSettings = new AppSettings();
|
||||||
|
configuration.GetSection("AppSettings").Bind(appSettings);
|
||||||
|
var jwtKey = appSettings?.JwtSettings?.Secret ?? String.Empty;
|
||||||
|
var jwtIssuer = appSettings?.JwtSettings?.ValidIssuer ?? String.Empty;
|
||||||
|
var jwtAudience = appSettings?.JwtSettings?.ValidAudience ?? String.Empty;
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(jwtKey) || string.IsNullOrEmpty(jwtIssuer) || string.IsNullOrEmpty(jwtAudience))
|
||||||
|
{
|
||||||
|
context.Result = new UnauthorizedResult();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var token = context.HttpContext.Request.Headers["Authorization"].FirstOrDefault()?.Split(" ").Last();
|
||||||
|
|
||||||
|
if (token == null)
|
||||||
|
{
|
||||||
|
context.Result = new UnauthorizedResult();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var tokenHandler = new JwtSecurityTokenHandler();
|
||||||
|
var key = Encoding.ASCII.GetBytes(jwtKey);
|
||||||
|
tokenHandler.ValidateToken(token, new TokenValidationParameters
|
||||||
|
{
|
||||||
|
ValidateIssuerSigningKey = true,
|
||||||
|
IssuerSigningKey = new SymmetricSecurityKey(key),
|
||||||
|
ValidateIssuer = true,
|
||||||
|
ValidIssuer = jwtIssuer,
|
||||||
|
ValidateAudience = true,
|
||||||
|
ValidAudience = jwtAudience,
|
||||||
|
ValidateLifetime = true,
|
||||||
|
ClockSkew = TimeSpan.Zero
|
||||||
|
}, out SecurityToken validatedToken);
|
||||||
|
|
||||||
|
var jwtToken = (JwtSecurityToken)validatedToken;
|
||||||
|
|
||||||
|
if (_policyName != null)
|
||||||
|
{
|
||||||
|
var claim = jwtToken.Claims.FirstOrDefault(c => c.Type == _policyName);
|
||||||
|
if (claim == null)
|
||||||
|
{
|
||||||
|
context.Result = new ForbidResult();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
context.Result = new UnauthorizedResult();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using BasicDotnetTemplate.MainProject.Models.Api.Common.Role;
|
using BasicDotnetTemplate.MainProject.Models.Api.Common.Role;
|
||||||
|
using DatabaseSqlServer = BasicDotnetTemplate.MainProject.Models.Database.SqlServer;
|
||||||
|
|
||||||
namespace BasicDotnetTemplate.MainProject.Models.Api.Common.User;
|
namespace BasicDotnetTemplate.MainProject.Models.Api.Common.User;
|
||||||
|
|
||||||
@@ -12,6 +13,16 @@ public class AuthenticatedUser
|
|||||||
public string? Email { get; set; }
|
public string? Email { get; set; }
|
||||||
public UserRole? Role { get; set; }
|
public UserRole? Role { get; set; }
|
||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
|
public AuthenticatedUser(DatabaseSqlServer.User user)
|
||||||
|
{
|
||||||
|
Guid = user.Guid;
|
||||||
|
Username = user.Username;
|
||||||
|
FirstName = user.FirstName;
|
||||||
|
LastName = user.LastName;
|
||||||
|
Email = user.Email;
|
||||||
|
Role = new UserRole();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ namespace BasicDotnetTemplate.MainProject.Models.Database.SqlServer
|
|||||||
{
|
{
|
||||||
public int Id { get; set; }
|
public int Id { get; set; }
|
||||||
public string Guid { get; set; }
|
public string Guid { get; set; }
|
||||||
|
public bool IsDeleted { 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,7 +7,7 @@ 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 JwtSettings? JwtSettings { get; set; }
|
||||||
public EncryptionSettings? EncryptionSettings { get; set; }
|
public EncryptionSettings? EncryptionSettings { get; set; }
|
||||||
|
|
||||||
#nullable disable
|
#nullable disable
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
namespace BasicDotnetTemplate.MainProject.Models.Settings;
|
namespace BasicDotnetTemplate.MainProject.Models.Settings;
|
||||||
|
|
||||||
public class JWTSettings
|
public class JwtSettings
|
||||||
{
|
{
|
||||||
#nullable enable
|
#nullable enable
|
||||||
public string? ValidAudience { get; set; }
|
public string? ValidAudience { get; set; }
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
|
|
||||||
using BasicDotnetTemplate.MainProject.Models.Api.Data.Auth;
|
using BasicDotnetTemplate.MainProject.Models.Api.Data.Auth;
|
||||||
using BasicDotnetTemplate.MainProject.Models.Api.Common.User;
|
using BasicDotnetTemplate.MainProject.Models.Api.Common.User;
|
||||||
|
using BasicDotnetTemplate.MainProject.Core.Database;
|
||||||
using BasicDotnetTemplate.MainProject.Utils;
|
using BasicDotnetTemplate.MainProject.Utils;
|
||||||
|
|
||||||
namespace BasicDotnetTemplate.MainProject.Services;
|
namespace BasicDotnetTemplate.MainProject.Services;
|
||||||
@@ -13,12 +14,16 @@ public interface IAuthService
|
|||||||
public class AuthService : BaseService, IAuthService
|
public class AuthService : BaseService, IAuthService
|
||||||
{
|
{
|
||||||
protected CryptUtils _cryptUtils;
|
protected CryptUtils _cryptUtils;
|
||||||
|
protected readonly IUserService _userService;
|
||||||
|
|
||||||
public AuthService(
|
public AuthService(
|
||||||
IConfiguration configuration
|
IConfiguration configuration,
|
||||||
) : base(configuration)
|
SqlServerContext sqlServerContext,
|
||||||
|
IUserService userService
|
||||||
|
) : base(configuration, sqlServerContext)
|
||||||
{
|
{
|
||||||
_cryptUtils = new CryptUtils(_appSettings);
|
_cryptUtils = new CryptUtils(_appSettings);
|
||||||
|
_userService = userService;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<AuthenticatedUser?> AuthenticateAsync(AuthenticateRequestData data)
|
public async Task<AuthenticatedUser?> AuthenticateAsync(AuthenticateRequestData data)
|
||||||
@@ -29,7 +34,11 @@ public class AuthService : BaseService, IAuthService
|
|||||||
|
|
||||||
if (!String.IsNullOrEmpty(decryptedUsername) && !String.IsNullOrEmpty(decryptedPassword))
|
if (!String.IsNullOrEmpty(decryptedUsername) && !String.IsNullOrEmpty(decryptedPassword))
|
||||||
{
|
{
|
||||||
|
var user = await this._userService.GetUserByUsernameAndPassword(decryptedUsername, decryptedPassword);
|
||||||
|
if (user != null)
|
||||||
|
{
|
||||||
|
authenticatedUser = new AuthenticatedUser(user);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return authenticatedUser;
|
return authenticatedUser;
|
||||||
|
|||||||
@@ -1,10 +1,4 @@
|
|||||||
using Microsoft.IdentityModel.Tokens;
|
using BasicDotnetTemplate.MainProject.Core.Database;
|
||||||
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;
|
using BasicDotnetTemplate.MainProject.Models.Settings;
|
||||||
|
|
||||||
namespace BasicDotnetTemplate.MainProject.Services;
|
namespace BasicDotnetTemplate.MainProject.Services;
|
||||||
@@ -13,12 +7,17 @@ public class BaseService
|
|||||||
{
|
{
|
||||||
protected readonly IConfiguration _configuration;
|
protected readonly IConfiguration _configuration;
|
||||||
protected readonly AppSettings _appSettings;
|
protected readonly AppSettings _appSettings;
|
||||||
|
protected readonly SqlServerContext _sqlServerContext;
|
||||||
|
|
||||||
public BaseService(IConfiguration configuration)
|
public BaseService(
|
||||||
|
IConfiguration configuration,
|
||||||
|
SqlServerContext sqlServerContext
|
||||||
|
)
|
||||||
{
|
{
|
||||||
_configuration = configuration;
|
_configuration = configuration;
|
||||||
_appSettings = new AppSettings();
|
_appSettings = new AppSettings();
|
||||||
_configuration.GetSection("AppSettings").Bind(_appSettings);
|
_configuration.GetSection("AppSettings").Bind(_appSettings);
|
||||||
|
_sqlServerContext = sqlServerContext;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ using System.Collections.Generic;
|
|||||||
using System.IdentityModel.Tokens.Jwt;
|
using System.IdentityModel.Tokens.Jwt;
|
||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using BasicDotnetTemplate.MainProject.Core.Database;
|
||||||
|
|
||||||
namespace BasicDotnetTemplate.MainProject.Services;
|
namespace BasicDotnetTemplate.MainProject.Services;
|
||||||
|
|
||||||
@@ -19,14 +20,38 @@ public class JwtService : BaseService, IJwtService
|
|||||||
private readonly string _jwtAudience;
|
private readonly string _jwtAudience;
|
||||||
|
|
||||||
public JwtService(
|
public JwtService(
|
||||||
IConfiguration configuration
|
IConfiguration configuration,
|
||||||
) : base(configuration)
|
SqlServerContext sqlServerContext
|
||||||
|
) : base(configuration, sqlServerContext)
|
||||||
{
|
{
|
||||||
_jwtKey = _appSettings?.JWTSettings?.Secret ?? String.Empty;
|
_jwtKey = _appSettings?.JwtSettings?.Secret ?? String.Empty;
|
||||||
_jwtIssuer = _appSettings?.JWTSettings?.ValidIssuer ?? String.Empty;
|
_jwtIssuer = _appSettings?.JwtSettings?.ValidIssuer ?? String.Empty;
|
||||||
_jwtAudience = _appSettings?.JWTSettings?.ValidAudience ?? String.Empty;
|
_jwtAudience = _appSettings?.JwtSettings?.ValidAudience ?? String.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public string GenerateToken(string userId, string username)
|
||||||
|
{
|
||||||
|
var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_jwtKey));
|
||||||
|
var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);
|
||||||
|
var expiration = _appSettings?.JwtSettings?.ExpiredAfterMinsOfInactivity ?? 15;
|
||||||
|
|
||||||
|
var claims = new List<Claim>
|
||||||
|
{
|
||||||
|
new Claim(JwtRegisteredClaimNames.Sub, userId),
|
||||||
|
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
|
||||||
|
new Claim("userid", userId)
|
||||||
|
};
|
||||||
|
|
||||||
|
var token = new JwtSecurityToken(
|
||||||
|
_jwtIssuer,
|
||||||
|
_jwtAudience,
|
||||||
|
claims,
|
||||||
|
expires: DateTime.Now.AddMinutes(expiration),
|
||||||
|
signingCredentials: credentials);
|
||||||
|
|
||||||
|
return new JwtSecurityTokenHandler().WriteToken(token);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
59
MainProject/Services/UserService.cs
Normal file
59
MainProject/Services/UserService.cs
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
|
||||||
|
using System.Collections;
|
||||||
|
using BasicDotnetTemplate.MainProject.Core.Database;
|
||||||
|
using BasicDotnetTemplate.MainProject.Models.Database.SqlServer;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
namespace BasicDotnetTemplate.MainProject.Services;
|
||||||
|
|
||||||
|
public interface IUserService
|
||||||
|
{
|
||||||
|
Task<User?> GetUserByUsernameAndPassword(string username, string password);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class UserService : BaseService, IUserService
|
||||||
|
{
|
||||||
|
|
||||||
|
public UserService(
|
||||||
|
IConfiguration configuration,
|
||||||
|
SqlServerContext sqlServerContext
|
||||||
|
) : base(configuration, sqlServerContext)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
private IQueryable<User> GetUsers()
|
||||||
|
{
|
||||||
|
return this._sqlServerContext.Users.Where(x => !x.IsDeleted);
|
||||||
|
}
|
||||||
|
|
||||||
|
private IQueryable<User> GetUserByUsername(string username)
|
||||||
|
{
|
||||||
|
return this._sqlServerContext.Users
|
||||||
|
.Where(x =>
|
||||||
|
!x.IsDeleted &&
|
||||||
|
String.Equals(x.Username, username, StringComparison.CurrentCultureIgnoreCase)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public async Task<User?> GetUserByUsernameAndPassword(string username, string password)
|
||||||
|
{
|
||||||
|
User? user = null;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
user = await this.GetUserByUsername(username).FirstOrDefaultAsync();
|
||||||
|
if (user != null)
|
||||||
|
{
|
||||||
|
var encryptedPassword = user.PasswordHash;
|
||||||
|
Console.WriteLine(encryptedPassword);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Console.WriteLine(exception.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -214,6 +214,7 @@ public static class ProgramUtils
|
|||||||
Logger.Info("[ProgramUtils][AddScopes] Adding scopes");
|
Logger.Info("[ProgramUtils][AddScopes] Adding scopes");
|
||||||
builder.Services.AddScoped<IAuthService, AuthService>();
|
builder.Services.AddScoped<IAuthService, AuthService>();
|
||||||
builder.Services.AddScoped<IJwtService, JwtService>();
|
builder.Services.AddScoped<IJwtService, JwtService>();
|
||||||
|
builder.Services.AddScoped<IUserService, UserService>();
|
||||||
Logger.Info("[ProgramUtils][AddScopes] Done scopes");
|
Logger.Info("[ProgramUtils][AddScopes] Done scopes");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,7 @@
|
|||||||
"Microsoft.AspNetCore": "Warning"
|
"Microsoft.AspNetCore": "Warning"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"JWTSettings": {
|
"JwtSettings": {
|
||||||
"ValidAudience": "http://localhost:4200",
|
"ValidAudience": "http://localhost:4200",
|
||||||
"ValidIssuer": "http://localhost:5000",
|
"ValidIssuer": "http://localhost:5000",
|
||||||
"Secret": "JWTAuthenticationHIGHsecuredPasswordVVVp1OH7Xzyr",
|
"Secret": "JWTAuthenticationHIGHsecuredPasswordVVVp1OH7Xzyr",
|
||||||
|
|||||||
Reference in New Issue
Block a user