Organization_Management #142
6119
Marco.Pms.DataAccess/Migrations/20250920041347_Removed_TenantId_From_MPIN_And_OTP.Designer.cs
generated
Normal file
6119
Marco.Pms.DataAccess/Migrations/20250920041347_Removed_TenantId_From_MPIN_And_OTP.Designer.cs
generated
Normal file
File diff suppressed because one or more lines are too long
@ -0,0 +1,85 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Marco.Pms.DataAccess.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class Removed_TenantId_From_MPIN_And_OTP : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_MPINDetails_Tenants_TenantId",
|
||||
table: "MPINDetails");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_OTPDetails_Tenants_TenantId",
|
||||
table: "OTPDetails");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_OTPDetails_TenantId",
|
||||
table: "OTPDetails");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_MPINDetails_TenantId",
|
||||
table: "MPINDetails");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "TenantId",
|
||||
table: "OTPDetails");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "TenantId",
|
||||
table: "MPINDetails");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<Guid>(
|
||||
name: "TenantId",
|
||||
table: "OTPDetails",
|
||||
type: "char(36)",
|
||||
nullable: false,
|
||||
defaultValue: new Guid("00000000-0000-0000-0000-000000000000"),
|
||||
collation: "ascii_general_ci");
|
||||
|
||||
migrationBuilder.AddColumn<Guid>(
|
||||
name: "TenantId",
|
||||
table: "MPINDetails",
|
||||
type: "char(36)",
|
||||
nullable: false,
|
||||
defaultValue: new Guid("00000000-0000-0000-0000-000000000000"),
|
||||
collation: "ascii_general_ci");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_OTPDetails_TenantId",
|
||||
table: "OTPDetails",
|
||||
column: "TenantId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_MPINDetails_TenantId",
|
||||
table: "MPINDetails",
|
||||
column: "TenantId");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_MPINDetails_Tenants_TenantId",
|
||||
table: "MPINDetails",
|
||||
column: "TenantId",
|
||||
principalTable: "Tenants",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_OTPDetails_Tenants_TenantId",
|
||||
table: "OTPDetails",
|
||||
column: "TenantId",
|
||||
principalTable: "Tenants",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
}
|
||||
}
|
||||
}
|
@ -284,9 +284,6 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<Guid>("TenantId")
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<DateTime>("TimeStamp")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
@ -295,8 +292,6 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("TenantId");
|
||||
|
||||
b.ToTable("MPINDetails");
|
||||
});
|
||||
|
||||
@ -316,9 +311,6 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<Guid>("TenantId")
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<DateTime>("TimeStamp")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
@ -327,8 +319,6 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("TenantId");
|
||||
|
||||
b.ToTable("OTPDetails");
|
||||
});
|
||||
|
||||
@ -4720,28 +4710,6 @@ namespace Marco.Pms.DataAccess.Migrations
|
||||
b.Navigation("UpdatedByEmployee");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Marco.Pms.Model.Authentication.MPINDetails", b =>
|
||||
{
|
||||
b.HasOne("Marco.Pms.Model.TenantModels.Tenant", "Tenant")
|
||||
.WithMany()
|
||||
.HasForeignKey("TenantId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Tenant");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Marco.Pms.Model.Authentication.OTPDetails", b =>
|
||||
{
|
||||
b.HasOne("Marco.Pms.Model.TenantModels.Tenant", "Tenant")
|
||||
.WithMany()
|
||||
.HasForeignKey("TenantId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Tenant");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Marco.Pms.Model.Authentication.RefreshToken", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", "User")
|
||||
|
@ -1,8 +1,6 @@
|
||||
using Marco.Pms.Model.Utilities;
|
||||
|
||||
namespace Marco.Pms.Model.Authentication
|
||||
namespace Marco.Pms.Model.Authentication
|
||||
{
|
||||
public class MPINDetails : TenantRelation
|
||||
public class MPINDetails
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public Guid UserId { get; set; }
|
||||
|
@ -1,8 +1,6 @@
|
||||
using Marco.Pms.Model.Utilities;
|
||||
|
||||
namespace Marco.Pms.Model.Authentication
|
||||
namespace Marco.Pms.Model.Authentication
|
||||
{
|
||||
public class OTPDetails : TenantRelation
|
||||
public class OTPDetails
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public Guid UserId { get; set; }
|
||||
|
@ -1,10 +1,12 @@
|
||||
using Marco.Pms.DataAccess.Data;
|
||||
using AutoMapper;
|
||||
using Marco.Pms.DataAccess.Data;
|
||||
using Marco.Pms.Model.Authentication;
|
||||
using Marco.Pms.Model.Dtos.Authentication;
|
||||
using Marco.Pms.Model.Dtos.Util;
|
||||
using Marco.Pms.Model.Employees;
|
||||
using Marco.Pms.Model.Entitlements;
|
||||
using Marco.Pms.Model.Utilities;
|
||||
using Marco.Pms.Model.ViewModels.Tenant;
|
||||
using MarcoBMS.Services.Helpers;
|
||||
using MarcoBMS.Services.Service;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
@ -22,33 +24,37 @@ namespace MarcoBMS.Services.Controllers
|
||||
[Route("api/[controller]")]
|
||||
public class AuthController : ControllerBase
|
||||
{
|
||||
private readonly IDbContextFactory<ApplicationDbContext> _dbContextFactory;
|
||||
private readonly IServiceScopeFactory _serviceScopeFactory;
|
||||
private readonly UserManager<ApplicationUser> _userManager;
|
||||
private readonly UserHelper _userHelper;
|
||||
private readonly ApplicationDbContext _context;
|
||||
private readonly JwtSettings _jwtSettings;
|
||||
private readonly RefreshTokenService _refreshTokenService;
|
||||
private readonly IEmailSender _emailSender;
|
||||
private readonly IConfiguration _configuration;
|
||||
private readonly EmployeeHelper _employeeHelper;
|
||||
private readonly ILoggingService _logger;
|
||||
//string tenentId = "1";
|
||||
public AuthController(UserManager<ApplicationUser> userManager, ApplicationDbContext context, JwtSettings jwtSettings, RefreshTokenService refreshTokenService,
|
||||
IEmailSender emailSender, IConfiguration configuration, EmployeeHelper employeeHelper, UserHelper userHelper, ILoggingService logger)
|
||||
|
||||
public AuthController(IDbContextFactory<ApplicationDbContext> dbContextFactory,
|
||||
IServiceScopeFactory serviceScopeFactory,
|
||||
UserManager<ApplicationUser> userManager,
|
||||
JwtSettings jwtSettings,
|
||||
IConfiguration configuration,
|
||||
ILoggingService logger)
|
||||
{
|
||||
_userManager = userManager;
|
||||
_jwtSettings = jwtSettings;
|
||||
_refreshTokenService = refreshTokenService;
|
||||
_emailSender = emailSender;
|
||||
_configuration = configuration;
|
||||
_employeeHelper = employeeHelper;
|
||||
_context = context;
|
||||
_userHelper = userHelper;
|
||||
_logger = logger;
|
||||
_dbContextFactory = dbContextFactory ?? throw new ArgumentNullException(nameof(dbContextFactory));
|
||||
_serviceScopeFactory = serviceScopeFactory ?? throw new ArgumentNullException(nameof(serviceScopeFactory));
|
||||
_userManager = userManager ?? throw new ArgumentNullException(nameof(userManager));
|
||||
_jwtSettings = jwtSettings ?? throw new ArgumentNullException(nameof(jwtSettings));
|
||||
_configuration = configuration ?? throw new ArgumentNullException(nameof(configuration));
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
}
|
||||
|
||||
// old login APIs
|
||||
|
||||
[HttpPost("login")]
|
||||
public async Task<IActionResult> Login([FromBody] LoginDto loginDto)
|
||||
{
|
||||
await using var _context = await _dbContextFactory.CreateDbContextAsync();
|
||||
using var scope = _serviceScopeFactory.CreateScope();
|
||||
var _refreshTokenService = scope.ServiceProvider.GetRequiredService<RefreshTokenService>();
|
||||
var _employeeHelper = scope.ServiceProvider.GetRequiredService<EmployeeHelper>();
|
||||
try
|
||||
{
|
||||
// Find user by email or phone number
|
||||
@ -118,6 +124,11 @@ namespace MarcoBMS.Services.Controllers
|
||||
[HttpPost("login-mobile")]
|
||||
public async Task<IActionResult> LoginMobile([FromBody] LoginDto loginDto)
|
||||
{
|
||||
await using var _context = await _dbContextFactory.CreateDbContextAsync();
|
||||
using var scope = _serviceScopeFactory.CreateScope();
|
||||
var _refreshTokenService = scope.ServiceProvider.GetRequiredService<RefreshTokenService>();
|
||||
var _employeeHelper = scope.ServiceProvider.GetRequiredService<EmployeeHelper>();
|
||||
|
||||
// Validate input DTO
|
||||
if (loginDto == null || string.IsNullOrWhiteSpace(loginDto.Username) || string.IsNullOrWhiteSpace(loginDto.Password))
|
||||
{
|
||||
@ -173,7 +184,7 @@ namespace MarcoBMS.Services.Controllers
|
||||
var refreshToken = await _refreshTokenService.CreateRefreshToken(user.Id, emp.TenantId.ToString(), _jwtSettings);
|
||||
|
||||
// Fetch MPIN Token
|
||||
var mpinToken = await _context.MPINDetails.FirstOrDefaultAsync(p => p.UserId == Guid.Parse(user.Id) && p.TenantId == emp.TenantId);
|
||||
var mpinToken = await _context.MPINDetails.FirstOrDefaultAsync(p => p.UserId == Guid.Parse(user.Id));
|
||||
|
||||
// Combine all tokens in response
|
||||
var responseData = new
|
||||
@ -190,6 +201,10 @@ namespace MarcoBMS.Services.Controllers
|
||||
[HttpPost("login-mpin")]
|
||||
public async Task<IActionResult> VerifyMPIN([FromBody] VerifyMPINDto verifyMPIN)
|
||||
{
|
||||
await using var _context = await _dbContextFactory.CreateDbContextAsync();
|
||||
using var scope = _serviceScopeFactory.CreateScope();
|
||||
var _refreshTokenService = scope.ServiceProvider.GetRequiredService<RefreshTokenService>();
|
||||
|
||||
try
|
||||
{
|
||||
// Validate the MPIN token and extract claims
|
||||
@ -240,7 +255,7 @@ namespace MarcoBMS.Services.Controllers
|
||||
|
||||
// Retrieve MPIN details
|
||||
var mpinDetails = await _context.MPINDetails
|
||||
.FirstOrDefaultAsync(p => p.UserId == Guid.Parse(requestEmployee.ApplicationUserId) && p.TenantId == tenantId);
|
||||
.FirstOrDefaultAsync(p => p.UserId == Guid.Parse(requestEmployee.ApplicationUserId));
|
||||
|
||||
if (mpinDetails == null)
|
||||
{
|
||||
@ -275,9 +290,265 @@ namespace MarcoBMS.Services.Controllers
|
||||
}
|
||||
}
|
||||
|
||||
// new login APIs
|
||||
|
||||
[HttpPost("login/v2")]
|
||||
public async Task<IActionResult> LoginAsync([FromBody] LoginDto loginDto)
|
||||
{
|
||||
await using var _context = await _dbContextFactory.CreateDbContextAsync();
|
||||
using var scope = _serviceScopeFactory.CreateScope();
|
||||
var _refreshTokenService = scope.ServiceProvider.GetRequiredService<RefreshTokenService>();
|
||||
try
|
||||
{
|
||||
// Retrieve employee details
|
||||
var emp = await _context.Employees.FirstOrDefaultAsync(e => e.Email == loginDto.Username && e.IsActive && e.HasApplicationAccess);
|
||||
if (emp == null)
|
||||
{
|
||||
_logger.LogWarning("Login failed: No employee record found for Email: {Email}", loginDto.Username ?? string.Empty);
|
||||
return Unauthorized(ApiResponse<object>.ErrorResponse("Invalid username or password.", "Invalid username or password.", 404));
|
||||
}
|
||||
|
||||
// Find user by email
|
||||
var user = await _context.ApplicationUsers
|
||||
.FirstOrDefaultAsync(u => u.Email == loginDto.Username || u.PhoneNumber == loginDto.Username);
|
||||
|
||||
if (user == null)
|
||||
{
|
||||
_logger.LogWarning("Login failed: User not found for input {Username}", loginDto.Username ?? string.Empty);
|
||||
return Unauthorized(ApiResponse<object>.ErrorResponse("Invalid username or password.", "Invalid username or password.", 401));
|
||||
}
|
||||
|
||||
// Check if the user is active
|
||||
if (!user.IsActive)
|
||||
{
|
||||
_logger.LogWarning("Login failed: Inactive user attempted login - UserId: {UserId}", user.Id);
|
||||
return Unauthorized(ApiResponse<object>.ErrorResponse("Invalid username or password.", "Invalid username or password.", 400));
|
||||
}
|
||||
|
||||
// Ensure the user's email is confirmed
|
||||
if (!user.EmailConfirmed)
|
||||
{
|
||||
_logger.LogWarning("Login failed: Email not confirmed for UserId: {UserId}", user.Id);
|
||||
return Unauthorized(ApiResponse<object>.ErrorResponse("Invalid username or password.", "Invalid username or password.", 400));
|
||||
}
|
||||
|
||||
// Validate the password
|
||||
if (!await _userManager.CheckPasswordAsync(user, loginDto.Password ?? string.Empty))
|
||||
{
|
||||
_logger.LogWarning("Login failed: Incorrect password for UserId: {UserId}", user.Id);
|
||||
return Unauthorized(ApiResponse<object>.ErrorResponse("Invalid username or password.", "Invalid username or password.", 401));
|
||||
}
|
||||
|
||||
// Ensure UserName exists for JWT
|
||||
if (string.IsNullOrWhiteSpace(user.UserName))
|
||||
{
|
||||
_logger.LogWarning("Login failed: Username not found for UserId: {UserId}", user.Id);
|
||||
return Unauthorized(ApiResponse<object>.ErrorResponse("Invalid username or password.", "Invalid username or password.", 404));
|
||||
}
|
||||
|
||||
// Generate tokens
|
||||
var token = _refreshTokenService.GenerateJwtTokenWithOrganization(user.UserName, emp.OrganizationId, _jwtSettings);
|
||||
var refreshToken = await _refreshTokenService.CreateRefreshTokenWithOrganization(user.Id, emp.OrganizationId, _jwtSettings);
|
||||
|
||||
//var token = _refreshTokenService.GenerateJwtToken(user.UserName, emp.TenantId ?? Guid.Empty, _jwtSettings);
|
||||
//var refreshToken = await _refreshTokenService.CreateRefreshToken(user.Id, emp.TenantId.ToString(), _jwtSettings);
|
||||
|
||||
_logger.LogInfo("User login successful - UserId: {UserId}", user.Id);
|
||||
return Ok(ApiResponse<object>.SuccessResponse(new
|
||||
{
|
||||
token,
|
||||
refreshToken
|
||||
}, "User logged in successfully.", 200));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Unexpected error during login");
|
||||
return StatusCode(500, ApiResponse<object>.ErrorResponse("Unexpected error", ex.Message, 500));
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost("app/login")]
|
||||
public async Task<IActionResult> LoginMobileAsync([FromBody] LoginDto loginDto)
|
||||
{
|
||||
await using var _context = await _dbContextFactory.CreateDbContextAsync();
|
||||
using var scope = _serviceScopeFactory.CreateScope();
|
||||
var _refreshTokenService = scope.ServiceProvider.GetRequiredService<RefreshTokenService>();
|
||||
var _employeeHelper = scope.ServiceProvider.GetRequiredService<EmployeeHelper>();
|
||||
|
||||
// Validate input DTO
|
||||
if (loginDto == null || string.IsNullOrWhiteSpace(loginDto.Username) || string.IsNullOrWhiteSpace(loginDto.Password))
|
||||
{
|
||||
return BadRequest(ApiResponse<object>.ErrorResponse("Username or password is missing.", "Invalid request", 400));
|
||||
}
|
||||
|
||||
// Find user by email or phone number
|
||||
var user = await _context.ApplicationUsers
|
||||
.FirstOrDefaultAsync(u => u.Email == loginDto.Username || u.PhoneNumber == loginDto.Username);
|
||||
|
||||
// If user not found, return unauthorized
|
||||
if (user == null)
|
||||
{
|
||||
return Unauthorized(ApiResponse<object>.ErrorResponse("Invalid username or password.", "Invalid username or password.", 401));
|
||||
}
|
||||
|
||||
// Check if user is inactive
|
||||
if (!user.IsActive)
|
||||
{
|
||||
return BadRequest(ApiResponse<object>.ErrorResponse("User is inactive", "User is inactive", 400));
|
||||
}
|
||||
|
||||
// Check if user email is not confirmed
|
||||
if (!user.EmailConfirmed)
|
||||
{
|
||||
return BadRequest(ApiResponse<object>.ErrorResponse("Your email is not verified. Please verify your email.", "Email not verified", 400));
|
||||
}
|
||||
|
||||
// Validate password using ASP.NET Identity
|
||||
var isPasswordValid = await _userManager.CheckPasswordAsync(user, loginDto.Password);
|
||||
if (!isPasswordValid)
|
||||
{
|
||||
return Unauthorized(ApiResponse<object>.ErrorResponse("Invalid username or password.", "Invalid credentials", 401));
|
||||
}
|
||||
|
||||
// Check if username is missing
|
||||
if (string.IsNullOrWhiteSpace(user.UserName))
|
||||
{
|
||||
return NotFound(ApiResponse<object>.ErrorResponse("UserName not found", "Username is missing", 404));
|
||||
}
|
||||
|
||||
// Get employee information for tenant context
|
||||
var emp = await _employeeHelper.GetEmployeeByApplicationUserID(user.Id);
|
||||
if (emp == null)
|
||||
{
|
||||
return NotFound(ApiResponse<object>.ErrorResponse("Employee not found", "Employee details missing", 404));
|
||||
}
|
||||
|
||||
// Generate JWT token
|
||||
var token = _refreshTokenService.GenerateJwtTokenWithOrganization(user.UserName, emp.OrganizationId, _jwtSettings);
|
||||
|
||||
// Generate Refresh Token and store in DB
|
||||
var refreshToken = await _refreshTokenService.CreateRefreshTokenWithOrganization(user.Id, emp.OrganizationId, _jwtSettings);
|
||||
|
||||
//// Generate JWT token
|
||||
//var token = _refreshTokenService.GenerateJwtToken(user.UserName, emp.TenantId ?? Guid.Empty, _jwtSettings);
|
||||
|
||||
//// Generate Refresh Token and store in DB
|
||||
//var refreshToken = await _refreshTokenService.CreateRefreshToken(user.Id, emp.TenantId.ToString(), _jwtSettings);
|
||||
|
||||
// Fetch MPIN Token
|
||||
var mpinToken = await _context.MPINDetails.FirstOrDefaultAsync(p => p.UserId == Guid.Parse(user.Id));
|
||||
|
||||
// Combine all tokens in response
|
||||
var responseData = new
|
||||
{
|
||||
token,
|
||||
refreshToken,
|
||||
mpinToken = mpinToken?.MPINToken
|
||||
};
|
||||
|
||||
// Return success response
|
||||
return Ok(ApiResponse<object>.SuccessResponse(responseData, "User logged in successfully.", 200));
|
||||
}
|
||||
|
||||
[HttpPost("login-mpin/v2")]
|
||||
public async Task<IActionResult> VerifyMPINAsync([FromBody] VerifyMPINDto verifyMPIN)
|
||||
{
|
||||
await using var _context = await _dbContextFactory.CreateDbContextAsync();
|
||||
using var scope = _serviceScopeFactory.CreateScope();
|
||||
var _refreshTokenService = scope.ServiceProvider.GetRequiredService<RefreshTokenService>();
|
||||
|
||||
try
|
||||
{
|
||||
// Validate the MPIN token and extract claims
|
||||
var claimsPrincipal = _refreshTokenService.ValidateToken(verifyMPIN.MPINToken, _jwtSettings);
|
||||
if (claimsPrincipal?.Identity == null || !claimsPrincipal.Identity.IsAuthenticated)
|
||||
{
|
||||
_logger.LogWarning("Invalid or unauthenticated MPIN token");
|
||||
return Unauthorized(ApiResponse<object>.ErrorResponse("Invalid MPIN token", "Unauthorized", 401));
|
||||
}
|
||||
|
||||
string? tokenType = claimsPrincipal.FindFirst("token_type")?.Value;
|
||||
string? tokenUserId = claimsPrincipal.FindFirst(ClaimTypes.NameIdentifier)?.Value;
|
||||
|
||||
// Validate essential claims
|
||||
if (string.IsNullOrWhiteSpace(tokenType) || string.IsNullOrWhiteSpace(tokenUserId))
|
||||
{
|
||||
_logger.LogWarning("MPIN token claims are incomplete");
|
||||
return Unauthorized(ApiResponse<object>.ErrorResponse("Invalid token claims", "MPIN token does not match your identity", 401));
|
||||
}
|
||||
|
||||
// Fetch employee by ID and tenant
|
||||
var requestEmployee = await _context.Employees
|
||||
.Include(e => e.ApplicationUser)
|
||||
.FirstOrDefaultAsync(e => e.Id == verifyMPIN.EmployeeId && e.HasApplicationAccess && e.ApplicationUserId == tokenUserId && e.IsActive);
|
||||
|
||||
if (requestEmployee == null || string.IsNullOrWhiteSpace(requestEmployee.ApplicationUserId))
|
||||
{
|
||||
_logger.LogWarning("Employee not found or invalid for verification - EmployeeId: {EmployeeId}", verifyMPIN.EmployeeId);
|
||||
return BadRequest(ApiResponse<object>.ErrorResponse("Invalid request", "Provided invalid employee information", 400));
|
||||
}
|
||||
|
||||
// Validate that the token belongs to the same employee making the request
|
||||
if (requestEmployee.ApplicationUserId != tokenUserId || tokenType != "mpin")
|
||||
{
|
||||
_logger.LogWarning("Token identity does not match employee info - EmployeeId: {EmployeeId}", requestEmployee.Id);
|
||||
return Unauthorized(ApiResponse<object>.ErrorResponse("Unauthorized", "MPIN token does not match your identity", 401));
|
||||
}
|
||||
|
||||
// Ensure MPIN input is valid
|
||||
if (string.IsNullOrWhiteSpace(verifyMPIN.MPIN))
|
||||
{
|
||||
_logger.LogWarning("MPIN not provided for EmployeeId: {EmployeeId}", requestEmployee.Id);
|
||||
return BadRequest(ApiResponse<object>.ErrorResponse("Invalid request", "MPIN not provided", 400));
|
||||
}
|
||||
|
||||
// Retrieve MPIN details
|
||||
var mpinDetails = await _context.MPINDetails
|
||||
.FirstOrDefaultAsync(p => p.UserId == Guid.Parse(requestEmployee.ApplicationUserId));
|
||||
|
||||
if (mpinDetails == null)
|
||||
{
|
||||
_logger.LogWarning("MPIN not set for EmployeeId: {EmployeeId}", requestEmployee.Id);
|
||||
return BadRequest(ApiResponse<object>.ErrorResponse("MPIN not set", "You have not set an MPIN", 400));
|
||||
}
|
||||
|
||||
// Compare hashed MPIN
|
||||
var providedMPINHash = ComputeSha256Hash(verifyMPIN.MPIN);
|
||||
if (providedMPINHash != mpinDetails.MPIN)
|
||||
{
|
||||
_logger.LogWarning("MPIN mismatch for EmployeeId: {EmployeeId}", requestEmployee.Id);
|
||||
return Unauthorized(ApiResponse<object>.ErrorResponse("MPIN mismatch", "MPIN did not match", 401));
|
||||
}
|
||||
|
||||
// Generate new tokens
|
||||
var jwtToken = _refreshTokenService.GenerateJwtTokenWithOrganization(requestEmployee.ApplicationUser?.UserName, requestEmployee.OrganizationId, _jwtSettings);
|
||||
var refreshToken = await _refreshTokenService.CreateRefreshTokenWithOrganization(requestEmployee.ApplicationUserId, requestEmployee.OrganizationId, _jwtSettings);
|
||||
|
||||
//var jwtToken = _refreshTokenService.GenerateJwtToken(requestEmployee.Email, tenantId, _jwtSettings);
|
||||
//var refreshToken = await _refreshTokenService.CreateRefreshToken(requestEmployee.ApplicationUserId, tenantId.ToString(), _jwtSettings);
|
||||
|
||||
_logger.LogInfo("MPIN verification successful - EmployeeId: {EmployeeId}", requestEmployee.Id);
|
||||
|
||||
return Ok(ApiResponse<object>.SuccessResponse(new
|
||||
{
|
||||
token = jwtToken,
|
||||
refreshToken
|
||||
}, "User logged in successfully.", 200));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Unexpected error occurred while verifying MPIN");
|
||||
return StatusCode(500, ApiResponse<object>.ErrorResponse("Unexpected error", ex.Message, 500));
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost("logout")]
|
||||
public async Task<IActionResult> Logout([FromBody] LogoutDto logoutDto)
|
||||
{
|
||||
await using var _context = await _dbContextFactory.CreateDbContextAsync();
|
||||
using var scope = _serviceScopeFactory.CreateScope();
|
||||
var _refreshTokenService = scope.ServiceProvider.GetRequiredService<RefreshTokenService>();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(logoutDto.RefreshToken))
|
||||
{
|
||||
_logger.LogWarning("Logout failed: Refresh token is missing");
|
||||
@ -315,6 +586,11 @@ namespace MarcoBMS.Services.Controllers
|
||||
[HttpPost("refresh-token")]
|
||||
public async Task<IActionResult> RefreshToken([FromBody] RefreshTokenDto refreshTokenDto)
|
||||
{
|
||||
await using var _context = await _dbContextFactory.CreateDbContextAsync();
|
||||
using var scope = _serviceScopeFactory.CreateScope();
|
||||
var _refreshTokenService = scope.ServiceProvider.GetRequiredService<RefreshTokenService>();
|
||||
var _employeeHelper = scope.ServiceProvider.GetRequiredService<EmployeeHelper>();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(refreshTokenDto.RefreshToken))
|
||||
{
|
||||
_logger.LogWarning("Refresh token is missing from the request body.");
|
||||
@ -323,6 +599,18 @@ namespace MarcoBMS.Services.Controllers
|
||||
|
||||
try
|
||||
{
|
||||
// Validate the MPIN token and extract claims
|
||||
var claimsPrincipal = _refreshTokenService.ValidateToken(refreshTokenDto.RefreshToken, _jwtSettings);
|
||||
if (claimsPrincipal?.Identity == null || !claimsPrincipal.Identity.IsAuthenticated)
|
||||
{
|
||||
_logger.LogWarning("Invalid or unauthenticated MPIN token");
|
||||
return Unauthorized(ApiResponse<object>.ErrorResponse("Invalid MPIN token", "Unauthorized", 401));
|
||||
}
|
||||
|
||||
string? tokenTenantId = claimsPrincipal.FindFirst("TenantId")?.Value ?? string.Empty;
|
||||
|
||||
var tenantId = Guid.Parse(tokenTenantId);
|
||||
|
||||
// Step 1: Fetch and validate the refresh token
|
||||
var refreshToken = await _refreshTokenService.GetRefreshToken(refreshTokenDto.RefreshToken);
|
||||
if (refreshToken == null)
|
||||
@ -358,8 +646,8 @@ namespace MarcoBMS.Services.Controllers
|
||||
// Step 4: Fetch employee and generate new tokens
|
||||
var emp = await _employeeHelper.GetEmployeeByApplicationUserID(user.Id);
|
||||
|
||||
var newJwtToken = _refreshTokenService.GenerateJwtToken(user.UserName, emp.TenantId ?? Guid.Empty, _jwtSettings);
|
||||
var newRefreshToken = await _refreshTokenService.CreateRefreshToken(user.Id, emp.TenantId.ToString(), _jwtSettings);
|
||||
var newJwtToken = _refreshTokenService.GenerateJwtToken(user.UserName, tenantId, _jwtSettings);
|
||||
var newRefreshToken = await _refreshTokenService.CreateRefreshToken(user.Id, tenantId.ToString(), _jwtSettings);
|
||||
|
||||
_logger.LogInfo("New access and refresh token issued for user: {UserId}", user.Id);
|
||||
|
||||
@ -378,6 +666,10 @@ namespace MarcoBMS.Services.Controllers
|
||||
[HttpPost("forgot-password")]
|
||||
public async Task<IActionResult> ForgotPassword([FromBody] ForgotPasswordDto forgotPasswordDto)
|
||||
{
|
||||
await using var _context = await _dbContextFactory.CreateDbContextAsync();
|
||||
using var scope = _serviceScopeFactory.CreateScope();
|
||||
var _emailSender = scope.ServiceProvider.GetRequiredService<IEmailSender>();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(forgotPasswordDto.Email))
|
||||
{
|
||||
_logger.LogWarning("ForgotPassword request received without email.");
|
||||
@ -414,6 +706,11 @@ namespace MarcoBMS.Services.Controllers
|
||||
[HttpPost("reset-password")]
|
||||
public async Task<IActionResult> ResetPassword([FromBody] ResetPasswordDto model)
|
||||
{
|
||||
await using var _context = await _dbContextFactory.CreateDbContextAsync();
|
||||
using var scope = _serviceScopeFactory.CreateScope();
|
||||
var _emailSender = scope.ServiceProvider.GetRequiredService<IEmailSender>();
|
||||
var _employeeHelper = scope.ServiceProvider.GetRequiredService<EmployeeHelper>();
|
||||
|
||||
_logger.LogInfo("Password reset request received for email: {Email}", model.Email ?? string.Empty);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(model.Email) || string.IsNullOrWhiteSpace(model.Token) || string.IsNullOrWhiteSpace(model.NewPassword))
|
||||
@ -491,6 +788,10 @@ namespace MarcoBMS.Services.Controllers
|
||||
[HttpPost("send-otp")]
|
||||
public async Task<IActionResult> SendOtpEmail([FromBody] GenerateOTPDto generateOTP)
|
||||
{
|
||||
await using var _context = await _dbContextFactory.CreateDbContextAsync();
|
||||
using var scope = _serviceScopeFactory.CreateScope();
|
||||
var _emailSender = scope.ServiceProvider.GetRequiredService<IEmailSender>();
|
||||
|
||||
try
|
||||
{
|
||||
// Validate input email
|
||||
@ -511,16 +812,15 @@ namespace MarcoBMS.Services.Controllers
|
||||
.FirstOrDefaultAsync(e => e.ApplicationUserId == requestedUser.Id);
|
||||
|
||||
// Generate a random 4-digit OTP
|
||||
string otp = new Random().Next(1000, 9999).ToString();
|
||||
string otp = GenerateSecureOtp();
|
||||
|
||||
// Store OTP in database
|
||||
var otpDetails = new OTPDetails
|
||||
{
|
||||
UserId = Guid.Parse(requestedUser.Id),
|
||||
OTP = otp,
|
||||
ExpriesInSec = 300, // 10 minutes
|
||||
TimeStamp = DateTime.UtcNow,
|
||||
TenantId = Guid.Parse("b3466e83-7e11-464c-b93a-daf047838b26")
|
||||
ExpriesInSec = 300, // 5 minutes
|
||||
TimeStamp = DateTime.UtcNow
|
||||
};
|
||||
|
||||
_context.OTPDetails.Add(otpDetails);
|
||||
@ -555,6 +855,10 @@ namespace MarcoBMS.Services.Controllers
|
||||
[HttpPost("login-otp")]
|
||||
public async Task<IActionResult> LoginWithOTP([FromBody] VerifyOTPDto verifyOTP)
|
||||
{
|
||||
await using var _context = await _dbContextFactory.CreateDbContextAsync();
|
||||
using var scope = _serviceScopeFactory.CreateScope();
|
||||
var _refreshTokenService = scope.ServiceProvider.GetRequiredService<RefreshTokenService>();
|
||||
|
||||
try
|
||||
{
|
||||
// Validate input
|
||||
@ -582,7 +886,7 @@ namespace MarcoBMS.Services.Controllers
|
||||
|
||||
// Fetch most recent OTP
|
||||
var otpDetails = await _context.OTPDetails
|
||||
.Where(o => o.UserId == userId && o.TenantId == requestEmployee.TenantId)
|
||||
.Where(o => o.UserId == userId)
|
||||
.OrderByDescending(o => o.TimeStamp)
|
||||
.FirstOrDefaultAsync();
|
||||
|
||||
@ -608,21 +912,24 @@ namespace MarcoBMS.Services.Controllers
|
||||
}
|
||||
|
||||
// Generate access and refresh tokens
|
||||
var accessToken = _refreshTokenService.GenerateJwtToken(
|
||||
requestEmployee.ApplicationUser?.UserName,
|
||||
requestEmployee.TenantId ?? Guid.Empty,
|
||||
_jwtSettings
|
||||
);
|
||||
var accessToken = _refreshTokenService.GenerateJwtTokenWithOrganization(requestEmployee.ApplicationUser?.UserName, requestEmployee.OrganizationId, _jwtSettings);
|
||||
var refreshToken = await _refreshTokenService.CreateRefreshTokenWithOrganization(requestEmployee.ApplicationUserId, requestEmployee.OrganizationId, _jwtSettings);
|
||||
|
||||
var refreshToken = await _refreshTokenService.CreateRefreshToken(
|
||||
requestEmployee.ApplicationUserId,
|
||||
requestEmployee.TenantId.ToString(),
|
||||
_jwtSettings
|
||||
);
|
||||
//var accessToken = _refreshTokenService.GenerateJwtToken(
|
||||
// requestEmployee.ApplicationUser?.UserName,
|
||||
// requestEmployee.TenantId ?? Guid.Empty,
|
||||
// _jwtSettings
|
||||
//);
|
||||
|
||||
//var refreshToken = await _refreshTokenService.CreateRefreshToken(
|
||||
// requestEmployee.ApplicationUserId,
|
||||
// requestEmployee.TenantId.ToString(),
|
||||
// _jwtSettings
|
||||
//);
|
||||
|
||||
// Fetch MPIN token if exists
|
||||
var mpinDetails = await _context.MPINDetails
|
||||
.FirstOrDefaultAsync(p => p.UserId == userId && p.TenantId == requestEmployee.TenantId);
|
||||
.FirstOrDefaultAsync(p => p.UserId == userId);
|
||||
|
||||
// Build and return response
|
||||
var response = new
|
||||
@ -646,8 +953,9 @@ namespace MarcoBMS.Services.Controllers
|
||||
[HttpPost("sendmail")]
|
||||
public async Task<IActionResult> SendEmail([FromBody] EmailDot emailDot)
|
||||
{
|
||||
|
||||
|
||||
await using var _context = await _dbContextFactory.CreateDbContextAsync();
|
||||
using var scope = _serviceScopeFactory.CreateScope();
|
||||
var _emailSender = scope.ServiceProvider.GetRequiredService<IEmailSender>();
|
||||
|
||||
var user = await _userManager.FindByEmailAsync(emailDot.ToEmail ?? string.Empty);
|
||||
if (user == null)
|
||||
@ -682,8 +990,15 @@ namespace MarcoBMS.Services.Controllers
|
||||
[HttpPost("change-password")]
|
||||
public async Task<IActionResult> ChangePassword([FromBody] ChangePasswordDto changePassword)
|
||||
{
|
||||
await using var _context = await _dbContextFactory.CreateDbContextAsync();
|
||||
using var scope = _serviceScopeFactory.CreateScope();
|
||||
|
||||
try
|
||||
{
|
||||
var _userHelper = scope.ServiceProvider.GetRequiredService<UserHelper>();
|
||||
var _emailSender = scope.ServiceProvider.GetRequiredService<IEmailSender>();
|
||||
var _employeeHelper = scope.ServiceProvider.GetRequiredService<EmployeeHelper>();
|
||||
|
||||
// Get the currently logged-in user
|
||||
var loggedUser = await _userHelper.GetCurrentUserAsync();
|
||||
|
||||
@ -741,13 +1056,18 @@ namespace MarcoBMS.Services.Controllers
|
||||
[HttpPost("generate-mpin")]
|
||||
public async Task<IActionResult> GenerateMPIN([FromBody] GenerateMPINDto generateMPINDto)
|
||||
{
|
||||
Guid tenantId = _userHelper.GetTenantId();
|
||||
await using var _context = await _dbContextFactory.CreateDbContextAsync();
|
||||
using var scope = _serviceScopeFactory.CreateScope();
|
||||
|
||||
var _userHelper = scope.ServiceProvider.GetRequiredService<UserHelper>();
|
||||
var _refreshTokenService = scope.ServiceProvider.GetRequiredService<RefreshTokenService>();
|
||||
|
||||
Employee loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
|
||||
|
||||
// Get the employee for whom MPIN is being generated
|
||||
var requestEmployee = await _context.Employees
|
||||
.Include(e => e.ApplicationUser)
|
||||
.FirstOrDefaultAsync(e => e.Id == generateMPINDto.EmployeeId && e.TenantId == tenantId);
|
||||
.FirstOrDefaultAsync(e => e.Id == generateMPINDto.EmployeeId);
|
||||
|
||||
// Validate employee and MPIN input
|
||||
if (requestEmployee == null || string.IsNullOrWhiteSpace(generateMPINDto.MPIN) || generateMPINDto.MPIN.Length != 4 || !generateMPINDto.MPIN.All(char.IsDigit))
|
||||
@ -767,13 +1087,13 @@ namespace MarcoBMS.Services.Controllers
|
||||
string mpinHash = ComputeSha256Hash(generateMPINDto.MPIN);
|
||||
string mpinToken = _refreshTokenService.CreateMPINToken(
|
||||
requestEmployee.ApplicationUserId,
|
||||
requestEmployee.TenantId.ToString(),
|
||||
requestEmployee.OrganizationId.ToString(),
|
||||
_jwtSettings
|
||||
);
|
||||
|
||||
// Prepare MPIN entity
|
||||
Guid userId = Guid.Parse(requestEmployee.ApplicationUserId ?? string.Empty);
|
||||
var existingMPIN = await _context.MPINDetails.FirstOrDefaultAsync(p => p.UserId == userId && p.TenantId == tenantId);
|
||||
var existingMPIN = await _context.MPINDetails.FirstOrDefaultAsync(p => p.UserId == userId);
|
||||
|
||||
if (existingMPIN == null)
|
||||
{
|
||||
@ -783,8 +1103,7 @@ namespace MarcoBMS.Services.Controllers
|
||||
UserId = userId,
|
||||
MPIN = mpinHash,
|
||||
MPINToken = mpinToken,
|
||||
TimeStamp = DateTime.UtcNow,
|
||||
TenantId = tenantId
|
||||
TimeStamp = DateTime.UtcNow
|
||||
};
|
||||
|
||||
_context.MPINDetails.Add(mPINDetails);
|
||||
@ -806,6 +1125,130 @@ namespace MarcoBMS.Services.Controllers
|
||||
return Ok(ApiResponse<object>.SuccessResponse(mpinToken, "MPIN updated successfully", 200));
|
||||
}
|
||||
}
|
||||
|
||||
[Authorize]
|
||||
[HttpGet("get/user/tenants")]
|
||||
public async Task<IActionResult> GetTenantListByEmployeeAsync()
|
||||
{
|
||||
// Create DbContext asynchronously to ensure DB connection is established fresh
|
||||
await using var _context = await _dbContextFactory.CreateDbContextAsync();
|
||||
|
||||
// Create a service scope to resolve scoped services like IHttpContextAccessor, IMapper, ILogger
|
||||
using var scope = _serviceScopeFactory.CreateScope();
|
||||
var _httpContextAccessor = scope.ServiceProvider.GetRequiredService<IHttpContextAccessor>();
|
||||
var _mapper = scope.ServiceProvider.GetRequiredService<IMapper>();
|
||||
var _userHelper = scope.ServiceProvider.GetRequiredService<UserHelper>();
|
||||
|
||||
_logger.LogDebug("Starting GetTenantAsync method.");
|
||||
|
||||
// Extract OrganizationId from current user's claims
|
||||
string stringOrganizationId = _httpContextAccessor.HttpContext?.User.FindFirst("OrganizationId")?.Value ?? string.Empty;
|
||||
Employee loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
|
||||
|
||||
if (string.IsNullOrEmpty(stringOrganizationId))
|
||||
{
|
||||
_logger.LogWarning("OrganizationId claim missing in user token.");
|
||||
return BadRequest(ApiResponse<object>.ErrorResponse("OrganizationId claim is missing", 400));
|
||||
}
|
||||
|
||||
if (!Guid.TryParse(stringOrganizationId, out var organizationId))
|
||||
{
|
||||
_logger.LogWarning("Invalid OrganizationId format: {OrganizationId}", stringOrganizationId);
|
||||
return BadRequest(ApiResponse<object>.ErrorResponse("Invalid OrganizationId format", 400));
|
||||
}
|
||||
|
||||
_logger.LogInfo("Fetching TenantOrgMappings for OrganizationId: {OrganizationId}", organizationId);
|
||||
|
||||
// Retrieve all TenantOrgMappings that match the organizationId and have a related Tenant
|
||||
var tenantOrganizationMapping = await _context.TenantOrgMappings
|
||||
.Include(to => to.Tenant)
|
||||
.Where(to => to.OrganizationId == organizationId && to.Tenant != null)
|
||||
.ToListAsync();
|
||||
|
||||
var tenantList = tenantOrganizationMapping.Select(to => to.Tenant!).ToList();
|
||||
|
||||
// Additionally fetch the Tenant record associated directly with this OrganizationId if any
|
||||
var tenant = await _context.Tenants.FirstOrDefaultAsync(t => t.OrganizationId == organizationId);
|
||||
if (tenant != null)
|
||||
{
|
||||
tenantList.Add(tenant);
|
||||
}
|
||||
|
||||
|
||||
tenantList = tenantList.Distinct().ToList();
|
||||
|
||||
// Map the tenant entities to TenantListVM view models
|
||||
var response = _mapper.Map<List<TenantListVM>>(tenantList);
|
||||
|
||||
_logger.LogInfo("Fetched {Count} tenants for OrganizationId: {OrganizationId}", tenantList.Count, organizationId);
|
||||
_logger.LogDebug("GetTenantAsync method completed successfully.");
|
||||
|
||||
return Ok(ApiResponse<object>.SuccessResponse(response, "Successfully fetched the list of tenant", 200));
|
||||
}
|
||||
|
||||
[Authorize]
|
||||
[HttpPost("select-tenant/{tenantId}")]
|
||||
public async Task<IActionResult> SelectTenantAsync(Guid tenantId)
|
||||
{
|
||||
// Create DbContext asynchronously for fresh connection
|
||||
await using var _context = await _dbContextFactory.CreateDbContextAsync();
|
||||
|
||||
// Create a scope to get scoped services
|
||||
using var scope = _serviceScopeFactory.CreateScope();
|
||||
var _httpContextAccessor = scope.ServiceProvider.GetRequiredService<IHttpContextAccessor>();
|
||||
var _userHelper = scope.ServiceProvider.GetRequiredService<UserHelper>();
|
||||
var _refreshTokenService = scope.ServiceProvider.GetRequiredService<RefreshTokenService>();
|
||||
|
||||
_logger.LogDebug("Starting SelectTenantAsync for tenantId: {TenantId}", tenantId);
|
||||
|
||||
// Get the current logged-in employee
|
||||
Employee loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
|
||||
|
||||
// Extract OrganizationId from user claims
|
||||
string stringOrganizationId = _httpContextAccessor.HttpContext?.User.FindFirst("OrganizationId")?.Value ?? string.Empty;
|
||||
|
||||
if (string.IsNullOrEmpty(stringOrganizationId))
|
||||
{
|
||||
_logger.LogWarning("OrganizationId claim is missing.");
|
||||
return BadRequest(ApiResponse<object>.ErrorResponse("OrganizationId claim is missing", 400));
|
||||
}
|
||||
|
||||
if (!Guid.TryParse(stringOrganizationId, out var organizationId))
|
||||
{
|
||||
_logger.LogWarning("Invalid OrganizationId format: {OrganizationId}", stringOrganizationId);
|
||||
return BadRequest(ApiResponse<object>.ErrorResponse("Invalid OrganizationId format", 400));
|
||||
}
|
||||
|
||||
// Find TenantOrgMapping for given tenantId and organizationId to validate access
|
||||
var tenantOrganization = await _context.TenantOrgMappings
|
||||
.FirstOrDefaultAsync(to => to.TenantId == tenantId && to.OrganizationId == organizationId);
|
||||
|
||||
if (tenantOrganization == null)
|
||||
{
|
||||
_logger.LogWarning("Tenant Organization Mapping not found for TenantId: {TenantId} and OrganizationId: {OrganizationId}", tenantId, organizationId);
|
||||
return NotFound(ApiResponse<object>.ErrorResponse("Tenant Organization Mapping not found", "Tenant Organization Mapping not found in database", 404));
|
||||
}
|
||||
|
||||
// Optional: Blacklist the JWT access token
|
||||
string jwtToken = HttpContext.Request.Headers["Authorization"].ToString().Replace("Bearer ", "");
|
||||
if (!string.IsNullOrWhiteSpace(jwtToken))
|
||||
{
|
||||
await _refreshTokenService.BlacklistJwtTokenAsync(jwtToken);
|
||||
_logger.LogInfo("JWT access token blacklisted successfully");
|
||||
}
|
||||
|
||||
// Generate JWT token scoped to selected tenant and logged-in employee
|
||||
var token = _refreshTokenService.GenerateJwtToken(loggedInEmployee.Email, tenantOrganization.TenantId, _jwtSettings);
|
||||
|
||||
// Generate and store refresh token
|
||||
var refreshToken = await _refreshTokenService.CreateRefreshToken(loggedInEmployee.ApplicationUserId, tenantOrganization.TenantId.ToString(), _jwtSettings);
|
||||
|
||||
_logger.LogInfo("Tenant selected and tokens generated for TenantId: {TenantId} and Employee: {EmployeeEmail}", tenantId, loggedInEmployee.Email ?? string.Empty);
|
||||
|
||||
// Return success response including tokens
|
||||
return Ok(ApiResponse<object>.SuccessResponse(new { Token = token, RefreshToken = refreshToken }, "Tenant is selected", 200));
|
||||
}
|
||||
|
||||
private static string ComputeSha256Hash(string rawData)
|
||||
{
|
||||
using (SHA256 sha256 = SHA256.Create())
|
||||
@ -822,5 +1265,20 @@ namespace MarcoBMS.Services.Controllers
|
||||
}
|
||||
}
|
||||
|
||||
private static string GenerateSecureOtp()
|
||||
{
|
||||
var randomNumber = new byte[2]; // 2 bytes can store values up to 65535
|
||||
using (var rng = RandomNumberGenerator.Create())
|
||||
{
|
||||
rng.GetBytes(randomNumber);
|
||||
}
|
||||
|
||||
// Convert to int and restrict to 4 digit range (0-9999)
|
||||
int randomValue = BitConverter.ToUInt16(randomNumber, 0) % 10000;
|
||||
|
||||
// Format with leading zeros if necessary to ensure 4 digits
|
||||
return randomValue.ToString("D4");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -24,6 +24,79 @@ namespace MarcoBMS.Services.Service
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public string GenerateJwtTokenWithOrganization(string username, Guid organizationId, JwtSettings _jwtSettings)
|
||||
{
|
||||
|
||||
// Custom claims
|
||||
var claims = new List<Claim>
|
||||
{
|
||||
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
|
||||
new Claim(JwtRegisteredClaimNames.Sub, username),
|
||||
new Claim("OrganizationId", organizationId.ToString()), // Add TenantId claim
|
||||
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()) };
|
||||
|
||||
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_jwtSettings.Key));
|
||||
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
|
||||
|
||||
var token = new JwtSecurityToken(
|
||||
issuer: _jwtSettings.Issuer,
|
||||
audience: _jwtSettings.Audience,
|
||||
claims: claims,
|
||||
expires: DateTime.UtcNow.AddMinutes(_jwtSettings.ExpiresInMinutes),
|
||||
signingCredentials: creds);
|
||||
|
||||
return new JwtSecurityTokenHandler().WriteToken(token);
|
||||
}
|
||||
public async Task<string> CreateRefreshTokenWithOrganization(string userId, Guid organizationId, JwtSettings jwtSettings)
|
||||
{
|
||||
try
|
||||
{
|
||||
var claims = new[]
|
||||
{
|
||||
new Claim(ClaimTypes.NameIdentifier, userId),
|
||||
new Claim("OrganizationId", organizationId.ToString()),
|
||||
new Claim("token_type", "refresh")
|
||||
};
|
||||
|
||||
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSettings.Key));
|
||||
var credentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256Signature);
|
||||
|
||||
var tokenDescriptor = new SecurityTokenDescriptor
|
||||
{
|
||||
Subject = new ClaimsIdentity(claims),
|
||||
Expires = DateTime.UtcNow.AddDays(jwtSettings.RefreshTokenExpiresInDays),
|
||||
Issuer = jwtSettings.Issuer,
|
||||
Audience = jwtSettings.Audience,
|
||||
SigningCredentials = credentials
|
||||
};
|
||||
|
||||
var tokenHandler = new JwtSecurityTokenHandler();
|
||||
var refreshTokenString = tokenHandler.WriteToken(tokenHandler.CreateToken(tokenDescriptor));
|
||||
|
||||
var refreshToken = new RefreshToken
|
||||
{
|
||||
Token = refreshTokenString,
|
||||
UserId = userId,
|
||||
ExpiryDate = DateTime.UtcNow.AddDays(jwtSettings.RefreshTokenExpiresInDays),
|
||||
IsRevoked = false
|
||||
};
|
||||
|
||||
// Check if the record exists
|
||||
var existingEntity = await _context.RefreshTokens.AnyAsync(c => c.UserId == userId);
|
||||
if (existingEntity) { _context.RefreshTokens.Update(refreshToken); }
|
||||
else
|
||||
{
|
||||
_context.RefreshTokens.Add(refreshToken);
|
||||
}
|
||||
await _context.SaveChangesAsync();
|
||||
return refreshTokenString;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error occured while creating new JWT token for user {UserId}", userId);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
public string GenerateJwtToken(string username, Guid tenantId, JwtSettings _jwtSettings)
|
||||
{
|
||||
|
||||
@ -47,7 +120,6 @@ namespace MarcoBMS.Services.Service
|
||||
|
||||
return new JwtSecurityTokenHandler().WriteToken(token);
|
||||
}
|
||||
|
||||
public async Task<string> CreateRefreshToken(string userId, string tenantId, JwtSettings jwtSettings)
|
||||
{
|
||||
try
|
||||
@ -98,7 +170,7 @@ namespace MarcoBMS.Services.Service
|
||||
throw;
|
||||
}
|
||||
}
|
||||
public string CreateMPINToken(string userId, string tenantId, JwtSettings jwtSettings)
|
||||
public string CreateMPINToken(string userId, string organizationId, JwtSettings jwtSettings)
|
||||
{
|
||||
try
|
||||
{
|
||||
@ -106,7 +178,7 @@ namespace MarcoBMS.Services.Service
|
||||
var claims = new[]
|
||||
{
|
||||
new Claim(ClaimTypes.NameIdentifier, userId),
|
||||
new Claim("TenantId", tenantId),
|
||||
new Claim("OrganizationId", organizationId),
|
||||
new Claim("token_type", "mpin")
|
||||
};
|
||||
|
||||
@ -125,30 +197,23 @@ namespace MarcoBMS.Services.Service
|
||||
var tokenHandler = new JwtSecurityTokenHandler();
|
||||
var MPINToken = tokenHandler.WriteToken(tokenHandler.CreateToken(tokenDescriptor));
|
||||
return MPINToken;
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error creating MPIN token for userId: {UserId}, tenantId: {TenantId}", userId, tenantId);
|
||||
_logger.LogError(ex, "Error creating MPIN token for userId: {UserId}, organizationId: {OrganizationId}", userId, organizationId);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<RefreshToken> GetRefreshToken(string token)
|
||||
{
|
||||
return await _context.RefreshTokens.FirstOrDefaultAsync(rt => rt.Token == token && !rt.IsRevoked && !rt.IsUsed) ?? new RefreshToken();
|
||||
}
|
||||
|
||||
public async Task MarkRefreshTokenAsUsed(RefreshToken refreshToken)
|
||||
{
|
||||
refreshToken.IsUsed = true;
|
||||
_context.RefreshTokens.Update(refreshToken);
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task RevokeRefreshToken(RefreshToken refreshToken)
|
||||
{
|
||||
refreshToken.IsRevoked = true;
|
||||
|
Loading…
x
Reference in New Issue
Block a user