Redis working
This commit is contained in:
parent
3b8d82049f
commit
b697a4b357
|
@ -87,11 +87,6 @@ namespace BlueWest.WebApi.EF.Model
|
||||||
builder.Entity<ApplicationRoleClaim>().ToTable("RoleClaims");
|
builder.Entity<ApplicationRoleClaim>().ToTable("RoleClaims");
|
||||||
builder.Entity<ApplicationUserRole>().ToTable("UserRole");
|
builder.Entity<ApplicationUserRole>().ToTable("UserRole");
|
||||||
|
|
||||||
// Session Token
|
|
||||||
builder.Entity<SessionToken>()
|
|
||||||
.HasOne(b => b.User)
|
|
||||||
.WithMany(x => x.SessionToken)
|
|
||||||
.HasForeignKey(x => x.UserId);
|
|
||||||
|
|
||||||
// Session Token Primary Key
|
// Session Token Primary Key
|
||||||
builder.Entity<SessionToken>(b =>
|
builder.Entity<SessionToken>(b =>
|
||||||
|
@ -105,12 +100,6 @@ namespace BlueWest.WebApi.EF.Model
|
||||||
.WithMany(x => x.SessionDatas)
|
.WithMany(x => x.SessionDatas)
|
||||||
.HasForeignKey(x => x.UserId);
|
.HasForeignKey(x => x.UserId);
|
||||||
|
|
||||||
// Session Data
|
|
||||||
builder.Entity<SessionData>()
|
|
||||||
.HasOne(b => b.SessionToken)
|
|
||||||
.WithOne(x => x.SessionData);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Session Data Primary Key
|
// Session Data Primary Key
|
||||||
builder.Entity<SessionData>(b =>
|
builder.Entity<SessionData>(b =>
|
||||||
|
|
|
@ -1,30 +0,0 @@
|
||||||
using BlueWest.Data.Application;
|
|
||||||
using BlueWest.WebApi.EF.Model;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Microsoft.EntityFrameworkCore.InMemory;
|
|
||||||
|
|
||||||
namespace BlueWest.WebApi.Context
|
|
||||||
{
|
|
||||||
|
|
||||||
public class SessionDbContext : DbContext
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// CountryDbContext Constructor.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="options"></param>
|
|
||||||
|
|
||||||
public SessionDbContext(DbContextOptions<SessionDbContext> options) : base(options)
|
|
||||||
{
|
|
||||||
Database.EnsureCreated();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
|
||||||
{
|
|
||||||
base.OnModelCreating(modelBuilder);
|
|
||||||
modelBuilder.ConfigureCurrentDbModel();
|
|
||||||
}
|
|
||||||
|
|
||||||
public DbSet<SessionToken> SessionTokens { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,14 +1,10 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using BlueWest.Cryptography;
|
|
||||||
using BlueWest.Data.Application;
|
|
||||||
using BlueWest.WebApi.Context.Users;
|
using BlueWest.WebApi.Context.Users;
|
||||||
using Microsoft.AspNetCore.Authentication;
|
using Microsoft.AspNetCore.Authentication;
|
||||||
using Microsoft.AspNetCore.Authentication.Cookies;
|
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Cors;
|
|
||||||
using Microsoft.AspNetCore.Identity;
|
using Microsoft.AspNetCore.Identity;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
@ -19,29 +15,24 @@ namespace BlueWest.WebApi.Controllers;
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ApiController]
|
[ApiController]
|
||||||
[Route("api/[controller]")]
|
[Route("api/[controller]")]
|
||||||
|
[Authorize(Policy = SessionConstants.ApiNamePolicy)]
|
||||||
[Authorize(Policy = "ApiUser")]
|
|
||||||
|
|
||||||
/*[EnableCors(Constants.CorsPolicyName)]*/
|
/*[EnableCors(Constants.CorsPolicyName)]*/
|
||||||
public class AuthController : Controller
|
public class AuthController : Controller
|
||||||
{
|
{
|
||||||
private readonly IAuthManager _authManager;
|
private readonly IAuthManager _authManager;
|
||||||
private readonly IUserManager _userManager;
|
private readonly IUserManager _userManager;
|
||||||
private readonly ISessionManager _sessionManager;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="authManager"></param>
|
/// <param name="authManager"></param>
|
||||||
/// <param name="userManager"></param>
|
/// <param name="userManager"></param>
|
||||||
public AuthController( IAuthManager authManager, IUserManager userManager, ISessionManager sessionManager)
|
public AuthController( IAuthManager authManager, IUserManager userManager)
|
||||||
{
|
{
|
||||||
_authManager = authManager;
|
_authManager = authManager;
|
||||||
_userManager = userManager;
|
_userManager = userManager;
|
||||||
_sessionManager = sessionManager;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Signup user
|
/// Signup user
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -62,64 +53,49 @@ namespace BlueWest.WebApi.Controllers;
|
||||||
/// <param name="loginViewModel"></param>
|
/// <param name="loginViewModel"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[AllowAnonymous]
|
[AllowAnonymous]
|
||||||
[HttpPost("token")]
|
[HttpPost("login")]
|
||||||
public async Task<ActionResult<IdentityResult>> GetTokenAsync(LoginRequest loginViewModel)
|
public async Task<ActionResult<IdentityResult>> GetSessionToken(LoginRequest loginViewModel)
|
||||||
{
|
{
|
||||||
var (success, sessionToken, token) = await _authManager.GetToken(loginViewModel);
|
var (success, sessionToken, identity) = await _authManager.GetSessionTokenId(loginViewModel);
|
||||||
|
|
||||||
if (success)
|
if (success)
|
||||||
{
|
{
|
||||||
return Ok(new {sessionToken, token});
|
return Ok(new {sessionToken});
|
||||||
|
|
||||||
}
|
}
|
||||||
return Problem();
|
return Problem();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Check if user is logged in
|
/// Gets a bearer token
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="loginViewModel"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpGet("isLoggedIn")]
|
[AllowAnonymous]
|
||||||
|
[HttpPost("bearer")]
|
||||||
public ActionResult<bool> IsLoggedIn()
|
public async Task<ActionResult<IdentityResult>> GetBearerBySessionId(string sessionId)
|
||||||
{
|
{
|
||||||
var identity = new ClaimsIdentity(CookieAuthenticationDefaults.AuthenticationScheme);
|
var (success, bearer) = await _authManager.GetBearerTokenBySessionTokenId(sessionId);
|
||||||
|
|
||||||
if (identity.IsAuthenticated)
|
if (success)
|
||||||
{
|
{
|
||||||
return Ok(true);
|
return Ok(new {bearer});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
return new UnauthorizedObjectResult(new {message = "The provided sessionId didn't return a valid Token."});
|
||||||
return Ok(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Checks if the session is authorized
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="hash"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
[HttpGet("isAuthorized")]
|
|
||||||
|
|
||||||
public ActionResult IsAuthorized(string hash)
|
|
||||||
{
|
|
||||||
var isAuthorized = _sessionManager.IsAuthorized(hash);
|
|
||||||
|
|
||||||
return Ok(isAuthorized ? new {authenticated = true} : new {authenticated = false});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Do Cookie based login.
|
/// Do Cookie based login.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="loginDto"></param>
|
/// <param name="loginDto"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[AllowAnonymous]
|
/*[AllowAnonymous]
|
||||||
[HttpPost("login")]
|
[HttpPost("login")]
|
||||||
public async Task<ActionResult> DoLoginAsync(LoginRequest loginDto)
|
public async Task<ActionResult> DoLoginByCookie(LoginRequest loginDto)
|
||||||
{
|
{
|
||||||
var (success, identity, sessionToken) = await _authManager.DoLogin(loginDto);
|
var (success, sessionToken, identity) = await _authManager.GetSessionTokenId(loginDto);
|
||||||
|
|
||||||
if (success)
|
if (success)
|
||||||
{
|
{
|
||||||
|
@ -129,23 +105,22 @@ namespace BlueWest.WebApi.Controllers;
|
||||||
new AuthenticationProperties
|
new AuthenticationProperties
|
||||||
{
|
{
|
||||||
IsPersistent = true,
|
IsPersistent = true,
|
||||||
ExpiresUtc = DateTime.UtcNow.AddDays(1)
|
ExpiresUtc = DateTime.UtcNow.Add(SessionConstants.DefaultValidForSpan)
|
||||||
});
|
});
|
||||||
|
|
||||||
return Ok(new {authenticated = true, sessionToken});
|
return Ok(new {authenticated = true, sessionToken});
|
||||||
}
|
}
|
||||||
|
|
||||||
return new ForbidResult(CookieAuthenticationDefaults.AuthenticationScheme);
|
return new ForbidResult(CookieAuthenticationDefaults.AuthenticationScheme);
|
||||||
}
|
}*/
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Do Cookie based logout
|
/// Do Cookie based logout
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="loginDto"></param>
|
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[AllowAnonymous]
|
[AllowAnonymous]
|
||||||
[HttpPost("logout")]
|
[HttpPost("logout")]
|
||||||
public async Task DoLogoutAsync()
|
public async Task DoCookieLogoutAsync()
|
||||||
{
|
{
|
||||||
await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
|
await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@ namespace BlueWest.WebApi.Controllers
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ApiController]
|
[ApiController]
|
||||||
[Route("[controller]")]
|
[Route("[controller]")]
|
||||||
[Authorize(Policy = "ApiUser")]
|
[Authorize(Policy = SessionConstants.ApiNamePolicy)]
|
||||||
[EnableCors(Constants.CorsPolicyName)]
|
[EnableCors(Constants.CorsPolicyName)]
|
||||||
// [Authorize(Roles = "Administrator")]
|
// [Authorize(Roles = "Administrator")]
|
||||||
public class CountryController : ControllerBase
|
public class CountryController : ControllerBase
|
||||||
|
|
|
@ -1,33 +0,0 @@
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using BlueWest.Data.Application;
|
|
||||||
using Microsoft.Extensions.Hosting;
|
|
||||||
using Redis.OM;
|
|
||||||
|
|
||||||
namespace BlueWest.WebApi
|
|
||||||
{
|
|
||||||
public class IndexCreationDevice : IHostedService
|
|
||||||
{
|
|
||||||
private readonly RedisConnectionProvider _provider;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Index Creation Device
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="provider"></param>
|
|
||||||
public IndexCreationDevice(RedisConnectionProvider provider)
|
|
||||||
{
|
|
||||||
_provider = provider;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public async Task StartAsync(CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
await _provider.Connection.CreateIndexAsync(typeof(SessionToken)); }
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public Task StopAsync(CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
return Task.CompletedTask; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using BlueWest.Data.Application;
|
||||||
|
using BlueWest.WebApi.Context.Users;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
|
||||||
|
namespace BlueWest.WebApi
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Methods for handling session cache data.
|
||||||
|
/// </summary>
|
||||||
|
public interface ISessionCache : IHostedService
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a Bearer By Access Token Id
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sessionTokenId"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
Task<string> GetBearerByAccessTokenId(string sessionTokenId);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a Session Token by Id.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="tokenId"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
Task<SessionToken> GetSessionTokenByIdAsync(string tokenId);
|
||||||
|
/// <summary>
|
||||||
|
/// Create a new session token
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="token"></param>
|
||||||
|
Task AddSessionToken(SessionToken token);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Save Cache
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
Task SaveAsync();
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Save Cache
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
void Save();
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,16 +0,0 @@
|
||||||
using BlueWest.Data.Application;
|
|
||||||
using BlueWest.WebApi.Context.Users;
|
|
||||||
|
|
||||||
namespace BlueWest.WebApi
|
|
||||||
{
|
|
||||||
public interface ISessionManager
|
|
||||||
{
|
|
||||||
bool IsAuthorized(string tokenHash);
|
|
||||||
|
|
||||||
SessionToken GetSessionToken(string hash, ApplicationUser applicationUser);
|
|
||||||
|
|
||||||
SessionToken GetSessionToken(LoginRequest loginRequest, ApplicationUser user);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace BlueWest.WebApi
|
||||||
|
{
|
||||||
|
internal static class SessionConstants
|
||||||
|
{
|
||||||
|
|
||||||
|
public static TimeSpan DefaultValidForSpan = TimeSpan.FromHours(24);
|
||||||
|
public const string ApiNamePolicy = "ApiUser";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,108 @@
|
||||||
|
using System;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using BlueWest.Cryptography;
|
||||||
|
using BlueWest.Data.Application;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using Redis.OM;
|
||||||
|
using Redis.OM.Searching;
|
||||||
|
|
||||||
|
namespace BlueWest.WebApi.Session
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Session Provider Context
|
||||||
|
/// </summary>
|
||||||
|
public sealed class SessionDataService : IHostedService, ISessionCache
|
||||||
|
{
|
||||||
|
private readonly RedisConnectionProvider _provider;
|
||||||
|
private RedisCollection<SessionToken> _sessionTokens;
|
||||||
|
/// <summary>
|
||||||
|
/// Index Creation Device
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="provider">Redis connection</param>
|
||||||
|
public SessionDataService(
|
||||||
|
RedisConnectionProvider provider)
|
||||||
|
{
|
||||||
|
_provider = provider;
|
||||||
|
_sessionTokens = (RedisCollection<SessionToken>)provider.RedisCollection<SessionToken>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Empty constructor
|
||||||
|
/// </summary>
|
||||||
|
public SessionDataService() { }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get a session token by the respective Id.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="tokenId"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public async Task<SessionToken> GetSessionTokenByIdAsync(string tokenId)
|
||||||
|
{
|
||||||
|
return await _sessionTokens.Where(x => x.Id == tokenId)
|
||||||
|
.FirstOrDefaultAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create a new session token
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="token"></param>
|
||||||
|
public async Task AddSessionToken(SessionToken token)
|
||||||
|
{
|
||||||
|
await _sessionTokens.InsertAsync(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public async Task SaveAsync()
|
||||||
|
{
|
||||||
|
await _sessionTokens.SaveAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Save session data
|
||||||
|
/// </summary>
|
||||||
|
public void Save()
|
||||||
|
{
|
||||||
|
_sessionTokens.Save();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a Bearer By Access Token Id
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sessionTokenId"></param>
|
||||||
|
public async Task<string> GetBearerByAccessTokenId(string sessionTokenId)
|
||||||
|
{
|
||||||
|
var accessToken = await _sessionTokens.Where(t => t.Id == sessionTokenId)
|
||||||
|
.FirstOrDefaultAsync();
|
||||||
|
|
||||||
|
if (accessToken == null) return string.Empty;
|
||||||
|
|
||||||
|
if (accessToken.IsValid)
|
||||||
|
{
|
||||||
|
var createdDate = DateTime.UnixEpoch.AddMilliseconds(accessToken.CreatedDate);
|
||||||
|
if (createdDate.AddMilliseconds(accessToken.ValidFor) < DateTime.Now)
|
||||||
|
{
|
||||||
|
accessToken.IsValid = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await _sessionTokens.SaveAsync();
|
||||||
|
|
||||||
|
return accessToken.IsValid ? accessToken.AccessToken : string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public async Task StartAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
await _provider.Connection.CreateIndexAsync(typeof(SessionToken));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public Task StopAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,97 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Linq;
|
|
||||||
using BlueWest.Cryptography;
|
|
||||||
using BlueWest.Data.Application;
|
|
||||||
using BlueWest.WebApi.Context;
|
|
||||||
using BlueWest.WebApi.Context.Users;
|
|
||||||
|
|
||||||
namespace BlueWest.WebApi
|
|
||||||
{
|
|
||||||
internal class SessionManager : ISessionManager
|
|
||||||
{
|
|
||||||
private readonly SessionDbContext _dbContex;
|
|
||||||
private readonly IHasher _hasher;
|
|
||||||
|
|
||||||
public SessionManager(SessionDbContext sessionDbContext, IHasher hasher)
|
|
||||||
{
|
|
||||||
_dbContex = sessionDbContext;
|
|
||||||
_hasher = hasher;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Check if token is authorized
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="hash"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public bool IsAuthorized(string hash)
|
|
||||||
{
|
|
||||||
var sessionToken = _dbContex.SessionTokens.FirstOrDefault(x => x.Token == hash);
|
|
||||||
|
|
||||||
if (sessionToken is not {IsValid: true}) return false;
|
|
||||||
|
|
||||||
var expirationDate = sessionToken.CreatedDate.Add(sessionToken.ValidFor);
|
|
||||||
|
|
||||||
if (expirationDate >= DateTime.Now)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a new or existing session token
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="hash"></param>
|
|
||||||
/// <param name="applicationUser"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public SessionToken GetSessionToken(string hash, ApplicationUser applicationUser)
|
|
||||||
{
|
|
||||||
var sessionToken = _dbContex.SessionTokens.FirstOrDefault(x => x.Token == hash);
|
|
||||||
|
|
||||||
if (sessionToken == null)
|
|
||||||
{
|
|
||||||
// Create token;
|
|
||||||
var newToken = new SessionToken
|
|
||||||
{
|
|
||||||
Token = hash,
|
|
||||||
CreatedDate = DateTime.Now,
|
|
||||||
ValidFor = TimeSpan.FromDays(1),
|
|
||||||
UserId = applicationUser.Id
|
|
||||||
};
|
|
||||||
|
|
||||||
_dbContex.SessionTokens.Add(newToken);
|
|
||||||
var result = _dbContex.SaveChanges() >= 0;
|
|
||||||
|
|
||||||
return !result ? null : newToken;
|
|
||||||
}
|
|
||||||
|
|
||||||
var expirationDate = sessionToken.CreatedDate.Add(sessionToken.ValidFor);
|
|
||||||
|
|
||||||
if (expirationDate >= DateTime.Now)
|
|
||||||
{
|
|
||||||
return sessionToken;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a session token for the user following a login request
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="loginRequest"></param>
|
|
||||||
/// <param name="user"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public SessionToken GetSessionToken(LoginRequest loginRequest, ApplicationUser user)
|
|
||||||
{
|
|
||||||
var content = $"{loginRequest.Uuid}|{user.Email}";
|
|
||||||
var hash = _hasher.CreateHash(content, BaseCryptoItem.HashAlgorithm.SHA2_512);
|
|
||||||
var sessionToken = GetSessionToken(hash, user);
|
|
||||||
return sessionToken;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -153,7 +153,7 @@ namespace BlueWest.WebApi
|
||||||
switch (allowedDatabase)
|
switch (allowedDatabase)
|
||||||
{
|
{
|
||||||
case "mysql":
|
case "mysql":
|
||||||
services.PrepareMySqlDatabasePool(_configuration, _environment);
|
services.PrepareMySqlDatabasePool(_configuration, _environment, configuration);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "sqlite":
|
case "sqlite":
|
||||||
|
|
|
@ -5,21 +5,19 @@ using BlueWest.Cryptography;
|
||||||
using BlueWest.WebApi.Context;
|
using BlueWest.WebApi.Context;
|
||||||
using BlueWest.WebApi.Context.Users;
|
using BlueWest.WebApi.Context.Users;
|
||||||
using BlueWest.WebApi.EF;
|
using BlueWest.WebApi.EF;
|
||||||
using Microsoft.AspNetCore.Authentication;
|
using BlueWest.WebApi.Session;
|
||||||
using Microsoft.AspNetCore.Authentication.Cookies;
|
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||||
using Microsoft.AspNetCore.Builder;
|
|
||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Identity;
|
using Microsoft.AspNetCore.Identity;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
|
||||||
using Microsoft.Extensions.Hosting;
|
using Microsoft.Extensions.Hosting;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Microsoft.IdentityModel.Tokens;
|
using Microsoft.IdentityModel.Tokens;
|
||||||
using Microsoft.OpenApi.Models;
|
using Redis.OM;
|
||||||
|
|
||||||
namespace BlueWest.WebApi
|
namespace BlueWest.WebApi
|
||||||
{
|
{
|
||||||
|
@ -39,13 +37,36 @@ namespace BlueWest.WebApi
|
||||||
private static DbContextOptionsBuilder GetMySqlSettings(
|
private static DbContextOptionsBuilder GetMySqlSettings(
|
||||||
this DbContextOptionsBuilder optionsBuilder,
|
this DbContextOptionsBuilder optionsBuilder,
|
||||||
IConfiguration configuration,
|
IConfiguration configuration,
|
||||||
|
IConfigurationRoot configurationRoot,
|
||||||
IWebHostEnvironment environment)
|
IWebHostEnvironment environment)
|
||||||
{
|
{
|
||||||
var sqlVersion = GetMySqlServerVersion(8, 0, 11);
|
var sqlVersion = GetMySqlServerVersion(8, 0, 11);
|
||||||
|
|
||||||
|
// Docker / No-Docker
|
||||||
|
var startupMode = configurationRoot["mode"];
|
||||||
|
|
||||||
|
string mySqlConnectionString = String.Empty;
|
||||||
|
|
||||||
|
if (startupMode == "docker")
|
||||||
|
{
|
||||||
|
var config = configuration.Get<ConnectionStringDocker>();
|
||||||
|
if(config != null) mySqlConnectionString = config.MySql;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var config = configuration.Get<ConnectionStringNoDocker>();
|
||||||
|
if(config != null) mySqlConnectionString = config.MySql;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mySqlConnectionString == string.Empty)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Fatal error: MySQL Connection string is empty.");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
optionsBuilder
|
optionsBuilder
|
||||||
.UseMySql(
|
.UseMySql(
|
||||||
configuration.GetConnectionString("DockerMySQL"),
|
mySqlConnectionString,
|
||||||
sqlVersion)
|
sqlVersion)
|
||||||
.UseMySql(sqlVersion,
|
.UseMySql(sqlVersion,
|
||||||
builder =>
|
builder =>
|
||||||
|
@ -74,16 +95,19 @@ namespace BlueWest.WebApi
|
||||||
/// <param name="environment"></param>
|
/// <param name="environment"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static IServiceCollection PrepareMySqlDatabasePool(this IServiceCollection serviceCollection,
|
public static IServiceCollection PrepareMySqlDatabasePool(this IServiceCollection serviceCollection,
|
||||||
IConfiguration configuration, IWebHostEnvironment environment)
|
IConfiguration configuration, IWebHostEnvironment environment, IConfigurationRoot configurationRoot)
|
||||||
{
|
{
|
||||||
return serviceCollection
|
return serviceCollection
|
||||||
.AddDbContextPool<UserDbContext>(options => options.GetMySqlSettings(configuration, environment))
|
.AddDbContextPool<UserDbContext>(options =>
|
||||||
.AddDbContextPool<CountryDbContext>(options => options.GetMySqlSettings(configuration, environment))
|
options.GetMySqlSettings(configuration, configurationRoot, environment))
|
||||||
.AddDbContextPool<FinanceDbContext>(options => options.GetMySqlSettings(configuration, environment))
|
.AddDbContextPool<CountryDbContext>(options =>
|
||||||
.AddDbContextPool<CompanyDbContext>(options => options.GetMySqlSettings(configuration, environment))
|
options.GetMySqlSettings(configuration, configurationRoot, environment))
|
||||||
|
.AddDbContextPool<FinanceDbContext>(options =>
|
||||||
|
options.GetMySqlSettings(configuration, configurationRoot, environment))
|
||||||
|
.AddDbContextPool<CompanyDbContext>(options =>
|
||||||
|
options.GetMySqlSettings(configuration, configurationRoot, environment))
|
||||||
.AddDbContextPool<ApplicationUserDbContext>(options =>
|
.AddDbContextPool<ApplicationUserDbContext>(options =>
|
||||||
options.GetMySqlSettings(configuration, environment))
|
options.GetMySqlSettings(configuration, configurationRoot, environment));
|
||||||
.AddDbContextPool<SessionDbContext>(options => options.UseInMemoryDatabase("_s"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -103,22 +127,23 @@ namespace BlueWest.WebApi
|
||||||
.AddDbContextPool<CountryDbContext>(options => options.UseSqlite(sqliteConString))
|
.AddDbContextPool<CountryDbContext>(options => options.UseSqlite(sqliteConString))
|
||||||
.AddDbContextPool<FinanceDbContext>(options => options.UseSqlite(sqliteConString))
|
.AddDbContextPool<FinanceDbContext>(options => options.UseSqlite(sqliteConString))
|
||||||
.AddDbContextPool<CompanyDbContext>(options => options.UseSqlite(sqliteConString))
|
.AddDbContextPool<CompanyDbContext>(options => options.UseSqlite(sqliteConString))
|
||||||
.AddDbContextPool<ApplicationUserDbContext>(options => options.UseSqlite(sqliteConString))
|
.AddDbContextPool<ApplicationUserDbContext>(options => options.UseSqlite(sqliteConString));
|
||||||
.AddDbContextPool<SessionDbContext>(options => options.UseInMemoryDatabase("_s"));
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static IServiceCollection AddAuthServerServices(this IServiceCollection services, IConfiguration configuration , IWebHostEnvironment environment)
|
internal static IServiceCollection AddAuthServerServices(this IServiceCollection services, IConfiguration configuration , IWebHostEnvironment environment)
|
||||||
{
|
{
|
||||||
|
|
||||||
services.AddScoped<IJwtTokenHandler, JwtTokenHandler>()
|
|
||||||
|
services
|
||||||
|
.AddSingleton(new RedisConnectionProvider("redis://redisinstance:6379"))
|
||||||
|
.AddScoped<IJwtTokenHandler, JwtTokenHandler>()
|
||||||
.AddScoped<IJwtFactory, JwtFactory>()
|
.AddScoped<IJwtFactory, JwtFactory>()
|
||||||
.AddSingleton<IndexCreationDevice>()
|
.AddHostedService<SessionDataService>()
|
||||||
.AddScoped<ISessionManager, SessionManager>()
|
.AddSingleton<ISessionCache, SessionDataService>()
|
||||||
.AddScoped<UserRepository>()
|
.AddScoped<UserRepository>()
|
||||||
.AddScoped<IUserManager, ApplicationUserManager>()
|
.AddScoped<IUserManager, ApplicationUserManager>()
|
||||||
.AddScoped<IAuthManager, AuthManager>()
|
.AddScoped<IAuthManager, AuthManager>()
|
||||||
|
|
||||||
.AddScoped<IHasher, Hasher>();
|
.AddScoped<IHasher, Hasher>();
|
||||||
|
|
||||||
// Database Context and Swagger
|
// Database Context and Swagger
|
||||||
|
@ -201,7 +226,7 @@ namespace BlueWest.WebApi
|
||||||
// api user claim policy
|
// api user claim policy
|
||||||
services.AddAuthorization(options =>
|
services.AddAuthorization(options =>
|
||||||
{
|
{
|
||||||
options.AddPolicy("ApiUser",
|
options.AddPolicy(SessionConstants.ApiNamePolicy,
|
||||||
policy => policy.RequireClaim(Context.Users.Constants.JwtClaimIdentifiers.Rol,
|
policy => policy.RequireClaim(Context.Users.Constants.JwtClaimIdentifiers.Rol,
|
||||||
Context.Users.Constants.JwtClaims.ApiAccess));
|
Context.Users.Constants.JwtClaims.ApiAccess));
|
||||||
|
|
||||||
|
@ -230,6 +255,15 @@ namespace BlueWest.WebApi
|
||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
internal class BlueWestConnectionString
|
||||||
|
{
|
||||||
|
public string Redis { get; set; }
|
||||||
|
public string MySql { get; set; }
|
||||||
|
}
|
||||||
|
internal class ConnectionStringDocker : BlueWestConnectionString { }
|
||||||
|
internal class ConnectionStringNoDocker : BlueWestConnectionString { }
|
||||||
|
|
||||||
|
|
|
@ -1,92 +1,130 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using BlueWest.Cryptography;
|
using BlueWest.Cryptography;
|
||||||
using BlueWest.Data.Application;
|
using BlueWest.Data.Application;
|
||||||
|
using Duende.IdentityServer.Extensions;
|
||||||
using Microsoft.AspNetCore.Authentication.Cookies;
|
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||||
using Microsoft.AspNetCore.Http;
|
|
||||||
using Microsoft.AspNetCore.Identity;
|
using Microsoft.AspNetCore.Identity;
|
||||||
|
|
||||||
namespace BlueWest.WebApi.Context.Users;
|
namespace BlueWest.WebApi.Context.Users
|
||||||
|
|
||||||
internal class AuthManager : IAuthManager
|
|
||||||
{
|
{
|
||||||
|
internal class AuthManager : IAuthManager
|
||||||
|
{
|
||||||
private readonly ApplicationUserManager _userManager;
|
private readonly ApplicationUserManager _userManager;
|
||||||
private readonly UserRepository _usersRepo;
|
|
||||||
private readonly ISessionManager _sessionManager;
|
|
||||||
private readonly IHasher _hasher;
|
private readonly IHasher _hasher;
|
||||||
private readonly IJwtFactory _jwtFactory;
|
private readonly IJwtFactory _jwtFactory;
|
||||||
|
private readonly ISessionCache _sessionCache;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Auth manager constructor
|
/// Auth manager constructor
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="userManager"></param>
|
/// <param name="userManager"></param>
|
||||||
/// <param name="hasher"></param>
|
/// <param name="hasher"></param>
|
||||||
/// <param name="usersRepo"></param>
|
|
||||||
/// <param name="jwtFactory"></param>
|
/// <param name="jwtFactory"></param>
|
||||||
|
/// <param name="sessionCache"></param>
|
||||||
public AuthManager(
|
public AuthManager(
|
||||||
ApplicationUserManager userManager,
|
ApplicationUserManager userManager,
|
||||||
IHasher hasher,
|
IHasher hasher,
|
||||||
UserRepository usersRepo,
|
IJwtFactory jwtFactory,
|
||||||
ISessionManager sessionManager,
|
ISessionCache sessionCache)
|
||||||
IJwtFactory jwtFactory)
|
|
||||||
{
|
{
|
||||||
_userManager = userManager;
|
_userManager = userManager;
|
||||||
_hasher = hasher;
|
_hasher = hasher;
|
||||||
_usersRepo = usersRepo;
|
|
||||||
_jwtFactory = jwtFactory;
|
_jwtFactory = jwtFactory;
|
||||||
_sessionManager = sessionManager;
|
_sessionCache = sessionCache;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<(bool, ClaimsIdentity, SessionTokenUnique)> DoLogin(LoginRequest loginRequest)
|
private async Task<SessionToken> GetSessionToken(LoginRequest loginRequest)
|
||||||
|
{
|
||||||
|
var uuid = loginRequest.GetUuid();
|
||||||
|
var hashUuid = GetHashFromUuid(uuid);
|
||||||
|
var sessionToken = await _sessionCache.GetSessionTokenByIdAsync(hashUuid);
|
||||||
|
if (sessionToken != null) return sessionToken;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetHashFromUuid(string uuid)
|
||||||
|
{
|
||||||
|
return _hasher.CreateHash(uuid, BaseCryptoItem.HashAlgorithm.SHA2_512);
|
||||||
|
}
|
||||||
|
|
||||||
|
private SessionToken GetNewSessionToken(LoginRequest loginRequest, ApplicationUser user, string token)
|
||||||
|
{
|
||||||
|
long unixTime = ((DateTimeOffset)DateTimeOffset.Now).ToUnixTimeMilliseconds();;
|
||||||
|
var newToken = new SessionToken
|
||||||
|
{
|
||||||
|
Id = GetHashFromUuid(loginRequest.GetUuid()),
|
||||||
|
UserId = user.Id,
|
||||||
|
CreatedDate = unixTime,
|
||||||
|
IsValid = true,
|
||||||
|
AccessToken = token
|
||||||
|
};
|
||||||
|
return newToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool SessionTokenIsValid(SessionToken token)
|
||||||
|
{
|
||||||
|
var nowMilliseconds = DateTimeOffset.Now.ToUnixTimeMilliseconds();
|
||||||
|
|
||||||
|
var isExpired = token.CreatedDate + token.ValidFor > nowMilliseconds;
|
||||||
|
|
||||||
|
if (isExpired)
|
||||||
|
{
|
||||||
|
token.IsValid = false;
|
||||||
|
_sessionCache.SaveAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
return token.IsValid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<(bool, string, ClaimsIdentity)> GetSessionTokenId(LoginRequest loginRequest)
|
||||||
{
|
{
|
||||||
var user = await _userManager.FindByEmailAsync(loginRequest.Email);
|
var user = await _userManager.FindByEmailAsync(loginRequest.Email);
|
||||||
|
|
||||||
if (user != null)
|
if (user == null) return (false, string.Empty, null);
|
||||||
{
|
|
||||||
if(await _userManager.CheckPasswordAsync(user, loginRequest.Password))
|
if (!await _userManager.CheckPasswordAsync(user, loginRequest.Password)) return (false, string.Empty, null);
|
||||||
{
|
|
||||||
// Identity
|
|
||||||
var identity = new ClaimsIdentity(CookieAuthenticationDefaults.AuthenticationScheme);
|
var identity = new ClaimsIdentity(CookieAuthenticationDefaults.AuthenticationScheme);
|
||||||
identity.AddClaim(new Claim(ClaimTypes.Email, user.Email));
|
identity.AddClaim(new Claim(ClaimTypes.Email, user.Email));
|
||||||
|
|
||||||
// Session
|
// Session
|
||||||
var sessionToken = _sessionManager.GetSessionToken(loginRequest, user);
|
var sessionToken = await GetSessionToken(loginRequest);
|
||||||
var sessionResponse = new SessionTokenUnique(sessionToken);
|
|
||||||
return (true, identity, sessionResponse);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (false, null, null);
|
if (sessionToken != null)
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public async Task<(bool, SessionTokenUnique, AccessToken)> GetToken(LoginRequest loginRequest)
|
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrEmpty(loginRequest.Email) && !string.IsNullOrEmpty(loginRequest.Password))
|
if (SessionTokenIsValid(sessionToken))
|
||||||
{
|
{
|
||||||
var user = await _userManager.FindByEmailAsync(loginRequest.Email);
|
return (true, sessionToken.Id, identity);
|
||||||
if (user != null)
|
|
||||||
{
|
|
||||||
if (await VerifyLoginByEmailAsync(loginRequest.Email,loginRequest.Password))
|
|
||||||
{
|
|
||||||
await _usersRepo.UpdateAsync(user, CancellationToken.None);
|
|
||||||
// Session
|
|
||||||
var sessionToken = _sessionManager.GetSessionToken(loginRequest, user);
|
|
||||||
var sessionResponse = new SessionTokenUnique(sessionToken);
|
|
||||||
|
|
||||||
var token = await _jwtFactory.GenerateEncodedToken(user.Id, user.UserName);
|
|
||||||
var completed = await _userManager.SetAuthenticationTokenAsync(user, "ApiUser", "ApiUser", token.Token);
|
|
||||||
|
|
||||||
return (completed == IdentityResult.Success, sessionResponse, token);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (false, null, null);
|
var (success, bearerToken) = await GenerateBearerToken(identity, user);
|
||||||
|
|
||||||
|
var newSessionToken = GetNewSessionToken(loginRequest, user, bearerToken);
|
||||||
|
await _sessionCache.AddSessionToken(newSessionToken);
|
||||||
|
|
||||||
|
return (success, newSessionToken.Id, identity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task<(bool, string)> GenerateBearerToken(ClaimsIdentity identity, ApplicationUser user)
|
||||||
|
{
|
||||||
|
var jwtToken = await _jwtFactory.GenerateEncodedToken(user.Id, user.UserName);
|
||||||
|
var completed = await _userManager.SetAuthenticationTokenAsync(user, SessionConstants.ApiNamePolicy,
|
||||||
|
SessionConstants.ApiNamePolicy, jwtToken.Token);
|
||||||
|
return (completed == IdentityResult.Success, jwtToken.Token);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public async Task<(bool, string)> GetBearerTokenBySessionTokenId(string sessionId)
|
||||||
|
{
|
||||||
|
if (sessionId.IsNullOrEmpty()) return (false, string.Empty);
|
||||||
|
var bearer = await _sessionCache.GetBearerByAccessTokenId(sessionId);
|
||||||
|
return (bearer != string.Empty, bearer);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public async Task<bool> VerifyLoginByEmailAsync(string email, string password)
|
public async Task<bool> VerifyLoginByEmailAsync(string email, string password)
|
||||||
{
|
{
|
||||||
|
@ -113,5 +151,5 @@ internal class AuthManager : IAuthManager
|
||||||
RegisterViewModel userToCreate = FromSignupToUser(userSignupDto);
|
RegisterViewModel userToCreate = FromSignupToUser(userSignupDto);
|
||||||
return await _userManager.CreateAsync(userToCreate.ToUser());
|
return await _userManager.CreateAsync(userToCreate.ToUser());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -80,18 +80,19 @@ namespace BlueWest.Cryptography
|
||||||
keyIndex = null;
|
keyIndex = null;
|
||||||
trimmedCipherText = cipherText;
|
trimmedCipherText = cipherText;
|
||||||
|
|
||||||
if (cipherText.Length <= 5 || cipherText[0] != '[') return;
|
if (cipherText.Length <= 5 ) return;
|
||||||
|
|
||||||
var cipherInfo = cipherText.Substring(1, cipherText.IndexOf(']') - 1).Split(",");
|
var cipherInfo = cipherText[0].ToString();
|
||||||
|
|
||||||
if (int.TryParse(cipherInfo[0], out var foundAlgorithm))
|
if (int.TryParse(cipherInfo, out int foundAlgorithm))
|
||||||
{
|
{
|
||||||
algorithm = foundAlgorithm;
|
algorithm = foundAlgorithm;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cipherInfo.Length == 2 && int.TryParse(cipherInfo[1], out var foundKeyIndex))
|
if (int.TryParse(cipherInfo, out int foundKeyIndex))
|
||||||
keyIndex = foundKeyIndex;
|
keyIndex = foundKeyIndex;
|
||||||
trimmedCipherText = cipherText.Substring(cipherText.IndexOf(']') + 1);
|
|
||||||
|
trimmedCipherText = cipherText.Substring(cipherText.IndexOf(cipherInfo, StringComparison.Ordinal) + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -39,7 +39,7 @@ internal class JwtIssuerOptions
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Set the timespan the token will be valid for (default is 120 min)
|
/// Set the timespan the token will be valid for (default is 120 min)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public TimeSpan ValidFor { get; set; } = TimeSpan.FromMinutes(120);
|
public TimeSpan ValidFor { get; set; } = SessionConstants.DefaultValidForSpan;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// "jti" (JWT ID) Claim (default ID is a GUID)
|
/// "jti" (JWT ID) Claim (default ID is a GUID)
|
||||||
|
|
|
@ -26,11 +26,11 @@ namespace BlueWest.Cryptography
|
||||||
|
|
||||||
if (storeSalt)
|
if (storeSalt)
|
||||||
{
|
{
|
||||||
hash = $"[{(int)HashAlgorithm.SHA3_512}]{salt}{asString}";
|
hash = $"{(int)HashAlgorithm.SHA3_512}{salt}{asString}";
|
||||||
return hash;
|
return hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
hash = $"[{(int)HashAlgorithm.SHA3_512}]{asString}";
|
hash = $"{(int)HashAlgorithm.SHA3_512}{asString}";
|
||||||
return hash;
|
return hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,26 +18,18 @@ public interface IAuthManager
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task<IdentityResult> CreateUserAsync(RegisterViewModel registerViewModel);
|
Task<IdentityResult> CreateUserAsync(RegisterViewModel registerViewModel);
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// VerifyLoginAsync
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="email"></param>
|
|
||||||
/// <param name="password"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task<bool> VerifyLoginByEmailAsync(string email, string password);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// GetToken
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="loginRequest"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task<(bool, SessionTokenUnique, AccessToken)> GetToken(LoginRequest loginRequest);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Does Login
|
/// Does Login
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="loginRequest"></param>
|
/// <param name="loginRequest"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task<(bool, ClaimsIdentity, SessionTokenUnique)> DoLogin(LoginRequest loginRequest);
|
public Task<(bool, string, ClaimsIdentity)> GetSessionTokenId(LoginRequest loginRequest);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a valid bearer token by the session id
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sessionId"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
Task<(bool, string)> GetBearerTokenBySessionTokenId(string sessionId);
|
||||||
|
|
||||||
}
|
}
|
|
@ -25,6 +25,15 @@ namespace BlueWest.WebApi.Context.Users
|
||||||
[Required]
|
[Required]
|
||||||
public string Uuid { get; set; }
|
public string Uuid { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets Uuid for this login request
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public string GetUuid()
|
||||||
|
{
|
||||||
|
return $"{Uuid}|{Email}";
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,18 +1,16 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using BlueWest.WebApi.EF;
|
|
||||||
using Microsoft.AspNetCore.Identity;
|
using Microsoft.AspNetCore.Identity;
|
||||||
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
|
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
namespace BlueWest.WebApi.Context.Users;
|
namespace BlueWest.WebApi.Context.Users
|
||||||
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Users Repository
|
/// Users Repository
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class UserRepository : UserStore<ApplicationUser,
|
public class UserRepository : UserStore<ApplicationUser,
|
||||||
ApplicationRole,
|
ApplicationRole,
|
||||||
ApplicationUserDbContext,
|
ApplicationUserDbContext,
|
||||||
string,
|
string,
|
||||||
|
@ -21,7 +19,7 @@ public class UserRepository : UserStore<ApplicationUser,
|
||||||
ApplicationUserLogin,
|
ApplicationUserLogin,
|
||||||
ApplicationUserToken,
|
ApplicationUserToken,
|
||||||
ApplicationRoleClaim>
|
ApplicationRoleClaim>
|
||||||
{
|
{
|
||||||
private readonly ApplicationUserDbContext _context;
|
private readonly ApplicationUserDbContext _context;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -66,4 +64,5 @@ public class UserRepository : UserStore<ApplicationUser,
|
||||||
return Task.FromResult(!string.IsNullOrEmpty(user.PasswordHash));
|
return Task.FromResult(!string.IsNullOrEmpty(user.PasswordHash));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -7,10 +7,15 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"AllowedHosts": "*",
|
"AllowedHosts": "*",
|
||||||
"ConnectionStrings": {
|
"ConnectionStringDocker": {
|
||||||
"DockerMySQL": "server=db;user=blueuser;password=dXjw127124dJ;database=bluedb;"
|
"MySQL": "server=db;user=blueuser;password=dXjw127124dJ;database=bluedb;",
|
||||||
|
"Redis": "redis://redisinstance:6379"
|
||||||
},
|
},
|
||||||
"REDIS_CONNECTION_STRING": "redis://localhost:6379",
|
"ConnectionStringNoDocker": {
|
||||||
|
"MySQL": "server=localhost;user=blueuser;password=dXjw127124dJ;database=bluedb;",
|
||||||
|
"Redis": "redis://localhost:6379"
|
||||||
|
},
|
||||||
|
"REDIS_CONNECTION_STRING": "redis://redis:6379",
|
||||||
"AuthSettings": {
|
"AuthSettings": {
|
||||||
"SecretKey": "iJWHDmHLpUA283sqsfhqGbMRdRj1PVkH"
|
"SecretKey": "iJWHDmHLpUA283sqsfhqGbMRdRj1PVkH"
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
{
|
{
|
||||||
|
"mode": "docker",
|
||||||
"database": "sqlite",
|
"database": "sqlite",
|
||||||
"environment": "dev"
|
"environment": "dev"
|
||||||
}
|
}
|
|
@ -1,8 +1,10 @@
|
||||||
using BlueWest.WebApi.Context.Users;
|
using BlueWest.WebApi.Context.Users;
|
||||||
using MapTo;
|
using MapTo;
|
||||||
|
using Redis.OM.Modeling;
|
||||||
|
|
||||||
namespace BlueWest.Data.Application
|
namespace BlueWest.Data.Application
|
||||||
{
|
{
|
||||||
|
[Document(StorageType = StorageType.Json, Prefixes = new []{"SessionToken"})]
|
||||||
[MapFrom(new []
|
[MapFrom(new []
|
||||||
{
|
{
|
||||||
typeof(SessionTokenUnique)
|
typeof(SessionTokenUnique)
|
||||||
|
@ -10,23 +12,18 @@ namespace BlueWest.Data.Application
|
||||||
public partial class SessionToken
|
public partial class SessionToken
|
||||||
{
|
{
|
||||||
[IgnoreMemberMapTo]
|
[IgnoreMemberMapTo]
|
||||||
public string Id { get; set; }
|
[Indexed] public string Id { get; set; }
|
||||||
|
|
||||||
public string Token { get; set; }
|
[Indexed] public int ValidFor { get; set;}
|
||||||
public TimeSpan ValidFor { get; set;}
|
|
||||||
|
|
||||||
public bool IsValid { get; set; }
|
[Indexed] public bool IsValid { get; set; }
|
||||||
|
|
||||||
public DateTime CreatedDate { get; set; }
|
[Indexed] public long CreatedDate { get; set; }
|
||||||
|
|
||||||
public ApplicationUser User { get; set; }
|
[Indexed] public string UserId { get; set; }
|
||||||
public string UserId { get; set; }
|
|
||||||
|
|
||||||
public ApplicationDevice ApplicationDevice { get; set; }
|
[Indexed] public string AccessToken { get; set; }
|
||||||
|
|
||||||
public SessionData SessionData { get; set; }
|
|
||||||
|
|
||||||
public string AccessToken { get; set; }
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,9 +7,8 @@ namespace BlueWest.Data.Application
|
||||||
{
|
{
|
||||||
public string Id { get; set; }
|
public string Id { get; set; }
|
||||||
|
|
||||||
public string Token { get; set; }
|
public int ValidFor { get; set;}
|
||||||
public TimeSpan ValidFor { get; set;}
|
public long CreatedDate { get; set; }
|
||||||
public DateTime CreatedDate { get; set; }
|
|
||||||
public string UserId { get; set; }
|
public string UserId { get; set; }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "docker", "docker", "{D7BF4A
|
||||||
ProjectSection(SolutionItems) = preProject
|
ProjectSection(SolutionItems) = preProject
|
||||||
docker-compose.yml = docker-compose.yml
|
docker-compose.yml = docker-compose.yml
|
||||||
BlueWest.Api\Dockerfile = BlueWest.Api\Dockerfile
|
BlueWest.Api\Dockerfile = BlueWest.Api\Dockerfile
|
||||||
|
docker-compose.db.only.yml = docker-compose.db.only.yml
|
||||||
EndProjectSection
|
EndProjectSection
|
||||||
EndProject
|
EndProject
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "README", "README", "{E9E3CEB0-D00C-46E3-B497-B4ED7B291190}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "README", "README", "{E9E3CEB0-D00C-46E3-B497-B4ED7B291190}"
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
version: '3'
|
||||||
|
services:
|
||||||
|
db:
|
||||||
|
container_name: BW1_DB_MYSQL
|
||||||
|
image: mysql/mysql-server:8.0
|
||||||
|
environment:
|
||||||
|
MYSQL_ROOT_HOST: db
|
||||||
|
MYSQL_USER_HOST: db
|
||||||
|
MYSQL_ROOT_PASSWORD: dXjw127124dJ
|
||||||
|
MYSQL_USER: blueuser
|
||||||
|
MYSQL_PASSWORD: dXjw127124dJ
|
||||||
|
MYSQL_DATABASE: bluedb
|
||||||
|
volumes:
|
||||||
|
- ./docker-entrypoint-initdb.d/:/docker-entrypoint-initdb.d/
|
||||||
|
phpmyadmin:
|
||||||
|
container_name: BW_PHPMYADMIN
|
||||||
|
image: phpmyadmin/phpmyadmin
|
||||||
|
ports:
|
||||||
|
- 80:80
|
||||||
|
environment:
|
||||||
|
MYSQL_USERNAME: 'blueuser'
|
||||||
|
MYSQL_ROOT_PASSWORD: 'dXjw127124dJ'
|
||||||
|
# ports:
|
||||||
|
# - "3308:3306"
|
||||||
|
redis:
|
||||||
|
image: "redis:alpine"
|
||||||
|
command: redis-server
|
||||||
|
ports:
|
||||||
|
- "6379:6379"
|
|
@ -1,7 +1,7 @@
|
||||||
version: '3'
|
version: '3'
|
||||||
services:
|
services:
|
||||||
db:
|
db:
|
||||||
container_name: BW1_DB_MYSQL
|
container_name: BW1_MYSQL
|
||||||
image: mysql/mysql-server:8.0
|
image: mysql/mysql-server:8.0
|
||||||
environment:
|
environment:
|
||||||
MYSQL_ROOT_HOST: db
|
MYSQL_ROOT_HOST: db
|
||||||
|
@ -12,16 +12,21 @@ services:
|
||||||
MYSQL_DATABASE: bluedb
|
MYSQL_DATABASE: bluedb
|
||||||
volumes:
|
volumes:
|
||||||
- ./docker-entrypoint-initdb.d/:/docker-entrypoint-initdb.d/
|
- ./docker-entrypoint-initdb.d/:/docker-entrypoint-initdb.d/
|
||||||
phpmyadmin:
|
# phpmyadmin:
|
||||||
container_name: BW_PHPMYADMIN
|
# container_name: BW_PHPMYADMIN
|
||||||
image: phpmyadmin/phpmyadmin
|
# image: phpmyadmin/phpmyadmin
|
||||||
ports:
|
# ports:
|
||||||
- 80:80
|
# - 80:80
|
||||||
environment:
|
# environment:
|
||||||
MYSQL_USERNAME: 'blueuser'
|
# MYSQL_USERNAME: 'blueuser'
|
||||||
MYSQL_ROOT_PASSWORD: 'dXjw127124dJ'
|
# MYSQL_ROOT_PASSWORD: 'dXjw127124dJ'
|
||||||
# ports:
|
# ports:
|
||||||
# - "3308:3306"
|
# - "3308:3306"
|
||||||
|
redisinstance:
|
||||||
|
image: "redislabs/redismod"
|
||||||
|
ports:
|
||||||
|
- "6379:6379"
|
||||||
|
container_name: BW1_REDIS
|
||||||
bapi120:
|
bapi120:
|
||||||
build:
|
build:
|
||||||
context: ./
|
context: ./
|
||||||
|
@ -33,13 +38,5 @@ services:
|
||||||
restart: always
|
restart: always
|
||||||
links:
|
links:
|
||||||
- db:db
|
- db:db
|
||||||
|
- redisinstance:redisinstance
|
||||||
container_name: BW1_API
|
container_name: BW1_API
|
||||||
|
|
||||||
redis:
|
|
||||||
image: "redis:alpine"
|
|
||||||
command: redis-server --requirepass Sup3rSecurePass0rd
|
|
||||||
ports:
|
|
||||||
- "6379:6379"
|
|
||||||
|
|
||||||
environment:
|
|
||||||
- REDIS_REPLICATION_MODE=master
|
|
||||||
|
|
Loading…
Reference in New Issue