Changed the Login Flow for auth controller and added the get tenant list and select tenant

This commit is contained in:
ashutosh.nehete 2025-09-20 11:40:04 +05:30
parent ee1cb73fe5
commit bcc416f47e
7 changed files with 6788 additions and 97 deletions

File diff suppressed because one or more lines are too long

View File

@ -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);
}
}
}

View File

@ -284,9 +284,6 @@ namespace Marco.Pms.DataAccess.Migrations
.IsRequired() .IsRequired()
.HasColumnType("longtext"); .HasColumnType("longtext");
b.Property<Guid>("TenantId")
.HasColumnType("char(36)");
b.Property<DateTime>("TimeStamp") b.Property<DateTime>("TimeStamp")
.HasColumnType("datetime(6)"); .HasColumnType("datetime(6)");
@ -295,8 +292,6 @@ namespace Marco.Pms.DataAccess.Migrations
b.HasKey("Id"); b.HasKey("Id");
b.HasIndex("TenantId");
b.ToTable("MPINDetails"); b.ToTable("MPINDetails");
}); });
@ -316,9 +311,6 @@ namespace Marco.Pms.DataAccess.Migrations
.IsRequired() .IsRequired()
.HasColumnType("longtext"); .HasColumnType("longtext");
b.Property<Guid>("TenantId")
.HasColumnType("char(36)");
b.Property<DateTime>("TimeStamp") b.Property<DateTime>("TimeStamp")
.HasColumnType("datetime(6)"); .HasColumnType("datetime(6)");
@ -327,8 +319,6 @@ namespace Marco.Pms.DataAccess.Migrations
b.HasKey("Id"); b.HasKey("Id");
b.HasIndex("TenantId");
b.ToTable("OTPDetails"); b.ToTable("OTPDetails");
}); });
@ -4720,28 +4710,6 @@ namespace Marco.Pms.DataAccess.Migrations
b.Navigation("UpdatedByEmployee"); 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 => modelBuilder.Entity("Marco.Pms.Model.Authentication.RefreshToken", b =>
{ {
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", "User") b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", "User")

View File

@ -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 Id { get; set; }
public Guid UserId { get; set; } public Guid UserId { get; set; }

View File

@ -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 Id { get; set; }
public Guid UserId { get; set; } public Guid UserId { get; set; }

View File

@ -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.Authentication;
using Marco.Pms.Model.Dtos.Authentication; using Marco.Pms.Model.Dtos.Authentication;
using Marco.Pms.Model.Dtos.Util; using Marco.Pms.Model.Dtos.Util;
using Marco.Pms.Model.Employees; using Marco.Pms.Model.Employees;
using Marco.Pms.Model.Entitlements; using Marco.Pms.Model.Entitlements;
using Marco.Pms.Model.Utilities; using Marco.Pms.Model.Utilities;
using Marco.Pms.Model.ViewModels.Tenant;
using MarcoBMS.Services.Helpers; using MarcoBMS.Services.Helpers;
using MarcoBMS.Services.Service; using MarcoBMS.Services.Service;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
@ -22,33 +24,37 @@ namespace MarcoBMS.Services.Controllers
[Route("api/[controller]")] [Route("api/[controller]")]
public class AuthController : ControllerBase public class AuthController : ControllerBase
{ {
private readonly IDbContextFactory<ApplicationDbContext> _dbContextFactory;
private readonly IServiceScopeFactory _serviceScopeFactory;
private readonly UserManager<ApplicationUser> _userManager; private readonly UserManager<ApplicationUser> _userManager;
private readonly UserHelper _userHelper;
private readonly ApplicationDbContext _context;
private readonly JwtSettings _jwtSettings; private readonly JwtSettings _jwtSettings;
private readonly RefreshTokenService _refreshTokenService;
private readonly IEmailSender _emailSender;
private readonly IConfiguration _configuration; private readonly IConfiguration _configuration;
private readonly EmployeeHelper _employeeHelper;
private readonly ILoggingService _logger; private readonly ILoggingService _logger;
//string tenentId = "1";
public AuthController(UserManager<ApplicationUser> userManager, ApplicationDbContext context, JwtSettings jwtSettings, RefreshTokenService refreshTokenService, public AuthController(IDbContextFactory<ApplicationDbContext> dbContextFactory,
IEmailSender emailSender, IConfiguration configuration, EmployeeHelper employeeHelper, UserHelper userHelper, ILoggingService logger) IServiceScopeFactory serviceScopeFactory,
UserManager<ApplicationUser> userManager,
JwtSettings jwtSettings,
IConfiguration configuration,
ILoggingService logger)
{ {
_userManager = userManager; _dbContextFactory = dbContextFactory ?? throw new ArgumentNullException(nameof(dbContextFactory));
_jwtSettings = jwtSettings; _serviceScopeFactory = serviceScopeFactory ?? throw new ArgumentNullException(nameof(serviceScopeFactory));
_refreshTokenService = refreshTokenService; _userManager = userManager ?? throw new ArgumentNullException(nameof(userManager));
_emailSender = emailSender; _jwtSettings = jwtSettings ?? throw new ArgumentNullException(nameof(jwtSettings));
_configuration = configuration; _configuration = configuration ?? throw new ArgumentNullException(nameof(configuration));
_employeeHelper = employeeHelper; _logger = logger ?? throw new ArgumentNullException(nameof(logger));
_context = context;
_userHelper = userHelper;
_logger = logger;
} }
// old login APIs
[HttpPost("login")] [HttpPost("login")]
public async Task<IActionResult> Login([FromBody] LoginDto loginDto) 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 try
{ {
// Find user by email or phone number // Find user by email or phone number
@ -118,6 +124,11 @@ namespace MarcoBMS.Services.Controllers
[HttpPost("login-mobile")] [HttpPost("login-mobile")]
public async Task<IActionResult> LoginMobile([FromBody] LoginDto loginDto) 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 // Validate input DTO
if (loginDto == null || string.IsNullOrWhiteSpace(loginDto.Username) || string.IsNullOrWhiteSpace(loginDto.Password)) 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); var refreshToken = await _refreshTokenService.CreateRefreshToken(user.Id, emp.TenantId.ToString(), _jwtSettings);
// Fetch MPIN Token // 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 // Combine all tokens in response
var responseData = new var responseData = new
@ -190,6 +201,10 @@ namespace MarcoBMS.Services.Controllers
[HttpPost("login-mpin")] [HttpPost("login-mpin")]
public async Task<IActionResult> VerifyMPIN([FromBody] VerifyMPINDto verifyMPIN) 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 try
{ {
// Validate the MPIN token and extract claims // Validate the MPIN token and extract claims
@ -240,7 +255,7 @@ namespace MarcoBMS.Services.Controllers
// Retrieve MPIN details // Retrieve MPIN details
var mpinDetails = await _context.MPINDetails 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) 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")] [HttpPost("logout")]
public async Task<IActionResult> Logout([FromBody] LogoutDto logoutDto) 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)) if (string.IsNullOrWhiteSpace(logoutDto.RefreshToken))
{ {
_logger.LogWarning("Logout failed: Refresh token is missing"); _logger.LogWarning("Logout failed: Refresh token is missing");
@ -315,6 +586,11 @@ namespace MarcoBMS.Services.Controllers
[HttpPost("refresh-token")] [HttpPost("refresh-token")]
public async Task<IActionResult> RefreshToken([FromBody] RefreshTokenDto refreshTokenDto) 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)) if (string.IsNullOrWhiteSpace(refreshTokenDto.RefreshToken))
{ {
_logger.LogWarning("Refresh token is missing from the request body."); _logger.LogWarning("Refresh token is missing from the request body.");
@ -323,6 +599,18 @@ namespace MarcoBMS.Services.Controllers
try 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 // Step 1: Fetch and validate the refresh token
var refreshToken = await _refreshTokenService.GetRefreshToken(refreshTokenDto.RefreshToken); var refreshToken = await _refreshTokenService.GetRefreshToken(refreshTokenDto.RefreshToken);
if (refreshToken == null) if (refreshToken == null)
@ -358,8 +646,8 @@ namespace MarcoBMS.Services.Controllers
// Step 4: Fetch employee and generate new tokens // Step 4: Fetch employee and generate new tokens
var emp = await _employeeHelper.GetEmployeeByApplicationUserID(user.Id); var emp = await _employeeHelper.GetEmployeeByApplicationUserID(user.Id);
var newJwtToken = _refreshTokenService.GenerateJwtToken(user.UserName, emp.TenantId ?? Guid.Empty, _jwtSettings); var newJwtToken = _refreshTokenService.GenerateJwtToken(user.UserName, tenantId, _jwtSettings);
var newRefreshToken = await _refreshTokenService.CreateRefreshToken(user.Id, emp.TenantId.ToString(), _jwtSettings); var newRefreshToken = await _refreshTokenService.CreateRefreshToken(user.Id, tenantId.ToString(), _jwtSettings);
_logger.LogInfo("New access and refresh token issued for user: {UserId}", user.Id); _logger.LogInfo("New access and refresh token issued for user: {UserId}", user.Id);
@ -378,6 +666,10 @@ namespace MarcoBMS.Services.Controllers
[HttpPost("forgot-password")] [HttpPost("forgot-password")]
public async Task<IActionResult> ForgotPassword([FromBody] ForgotPasswordDto forgotPasswordDto) 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)) if (string.IsNullOrWhiteSpace(forgotPasswordDto.Email))
{ {
_logger.LogWarning("ForgotPassword request received without email."); _logger.LogWarning("ForgotPassword request received without email.");
@ -414,6 +706,11 @@ namespace MarcoBMS.Services.Controllers
[HttpPost("reset-password")] [HttpPost("reset-password")]
public async Task<IActionResult> ResetPassword([FromBody] ResetPasswordDto model) 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); _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)) if (string.IsNullOrWhiteSpace(model.Email) || string.IsNullOrWhiteSpace(model.Token) || string.IsNullOrWhiteSpace(model.NewPassword))
@ -491,6 +788,10 @@ namespace MarcoBMS.Services.Controllers
[HttpPost("send-otp")] [HttpPost("send-otp")]
public async Task<IActionResult> SendOtpEmail([FromBody] GenerateOTPDto generateOTP) 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 try
{ {
// Validate input email // Validate input email
@ -511,16 +812,15 @@ namespace MarcoBMS.Services.Controllers
.FirstOrDefaultAsync(e => e.ApplicationUserId == requestedUser.Id); .FirstOrDefaultAsync(e => e.ApplicationUserId == requestedUser.Id);
// Generate a random 4-digit OTP // Generate a random 4-digit OTP
string otp = new Random().Next(1000, 9999).ToString(); string otp = GenerateSecureOtp();
// Store OTP in database // Store OTP in database
var otpDetails = new OTPDetails var otpDetails = new OTPDetails
{ {
UserId = Guid.Parse(requestedUser.Id), UserId = Guid.Parse(requestedUser.Id),
OTP = otp, OTP = otp,
ExpriesInSec = 300, // 10 minutes ExpriesInSec = 300, // 5 minutes
TimeStamp = DateTime.UtcNow, TimeStamp = DateTime.UtcNow
TenantId = Guid.Parse("b3466e83-7e11-464c-b93a-daf047838b26")
}; };
_context.OTPDetails.Add(otpDetails); _context.OTPDetails.Add(otpDetails);
@ -555,6 +855,10 @@ namespace MarcoBMS.Services.Controllers
[HttpPost("login-otp")] [HttpPost("login-otp")]
public async Task<IActionResult> LoginWithOTP([FromBody] VerifyOTPDto verifyOTP) 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 try
{ {
// Validate input // Validate input
@ -582,7 +886,7 @@ namespace MarcoBMS.Services.Controllers
// Fetch most recent OTP // Fetch most recent OTP
var otpDetails = await _context.OTPDetails var otpDetails = await _context.OTPDetails
.Where(o => o.UserId == userId && o.TenantId == requestEmployee.TenantId) .Where(o => o.UserId == userId)
.OrderByDescending(o => o.TimeStamp) .OrderByDescending(o => o.TimeStamp)
.FirstOrDefaultAsync(); .FirstOrDefaultAsync();
@ -608,21 +912,24 @@ namespace MarcoBMS.Services.Controllers
} }
// Generate access and refresh tokens // Generate access and refresh tokens
var accessToken = _refreshTokenService.GenerateJwtToken( var accessToken = _refreshTokenService.GenerateJwtTokenWithOrganization(requestEmployee.ApplicationUser?.UserName, requestEmployee.OrganizationId, _jwtSettings);
requestEmployee.ApplicationUser?.UserName, var refreshToken = await _refreshTokenService.CreateRefreshTokenWithOrganization(requestEmployee.ApplicationUserId, requestEmployee.OrganizationId, _jwtSettings);
requestEmployee.TenantId ?? Guid.Empty,
_jwtSettings
);
var refreshToken = await _refreshTokenService.CreateRefreshToken( //var accessToken = _refreshTokenService.GenerateJwtToken(
requestEmployee.ApplicationUserId, // requestEmployee.ApplicationUser?.UserName,
requestEmployee.TenantId.ToString(), // requestEmployee.TenantId ?? Guid.Empty,
_jwtSettings // _jwtSettings
); //);
//var refreshToken = await _refreshTokenService.CreateRefreshToken(
// requestEmployee.ApplicationUserId,
// requestEmployee.TenantId.ToString(),
// _jwtSettings
//);
// Fetch MPIN token if exists // Fetch MPIN token if exists
var mpinDetails = await _context.MPINDetails var mpinDetails = await _context.MPINDetails
.FirstOrDefaultAsync(p => p.UserId == userId && p.TenantId == requestEmployee.TenantId); .FirstOrDefaultAsync(p => p.UserId == userId);
// Build and return response // Build and return response
var response = new var response = new
@ -646,8 +953,9 @@ namespace MarcoBMS.Services.Controllers
[HttpPost("sendmail")] [HttpPost("sendmail")]
public async Task<IActionResult> SendEmail([FromBody] EmailDot emailDot) 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); var user = await _userManager.FindByEmailAsync(emailDot.ToEmail ?? string.Empty);
if (user == null) if (user == null)
@ -682,8 +990,15 @@ namespace MarcoBMS.Services.Controllers
[HttpPost("change-password")] [HttpPost("change-password")]
public async Task<IActionResult> ChangePassword([FromBody] ChangePasswordDto changePassword) public async Task<IActionResult> ChangePassword([FromBody] ChangePasswordDto changePassword)
{ {
await using var _context = await _dbContextFactory.CreateDbContextAsync();
using var scope = _serviceScopeFactory.CreateScope();
try 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 // Get the currently logged-in user
var loggedUser = await _userHelper.GetCurrentUserAsync(); var loggedUser = await _userHelper.GetCurrentUserAsync();
@ -741,13 +1056,18 @@ namespace MarcoBMS.Services.Controllers
[HttpPost("generate-mpin")] [HttpPost("generate-mpin")]
public async Task<IActionResult> GenerateMPIN([FromBody] GenerateMPINDto generateMPINDto) 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(); Employee loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
// Get the employee for whom MPIN is being generated // Get the employee for whom MPIN is being generated
var requestEmployee = await _context.Employees var requestEmployee = await _context.Employees
.Include(e => e.ApplicationUser) .Include(e => e.ApplicationUser)
.FirstOrDefaultAsync(e => e.Id == generateMPINDto.EmployeeId && e.TenantId == tenantId); .FirstOrDefaultAsync(e => e.Id == generateMPINDto.EmployeeId);
// Validate employee and MPIN input // Validate employee and MPIN input
if (requestEmployee == null || string.IsNullOrWhiteSpace(generateMPINDto.MPIN) || generateMPINDto.MPIN.Length != 4 || !generateMPINDto.MPIN.All(char.IsDigit)) 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 mpinHash = ComputeSha256Hash(generateMPINDto.MPIN);
string mpinToken = _refreshTokenService.CreateMPINToken( string mpinToken = _refreshTokenService.CreateMPINToken(
requestEmployee.ApplicationUserId, requestEmployee.ApplicationUserId,
requestEmployee.TenantId.ToString(), requestEmployee.OrganizationId.ToString(),
_jwtSettings _jwtSettings
); );
// Prepare MPIN entity // Prepare MPIN entity
Guid userId = Guid.Parse(requestEmployee.ApplicationUserId ?? string.Empty); 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) if (existingMPIN == null)
{ {
@ -783,8 +1103,7 @@ namespace MarcoBMS.Services.Controllers
UserId = userId, UserId = userId,
MPIN = mpinHash, MPIN = mpinHash,
MPINToken = mpinToken, MPINToken = mpinToken,
TimeStamp = DateTime.UtcNow, TimeStamp = DateTime.UtcNow
TenantId = tenantId
}; };
_context.MPINDetails.Add(mPINDetails); _context.MPINDetails.Add(mPINDetails);
@ -806,6 +1125,130 @@ namespace MarcoBMS.Services.Controllers
return Ok(ApiResponse<object>.SuccessResponse(mpinToken, "MPIN updated successfully", 200)); 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) private static string ComputeSha256Hash(string rawData)
{ {
using (SHA256 sha256 = SHA256.Create()) 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");
}
} }
} }

View File

@ -24,6 +24,79 @@ namespace MarcoBMS.Services.Service
_logger = logger; _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) public string GenerateJwtToken(string username, Guid tenantId, JwtSettings _jwtSettings)
{ {
@ -47,7 +120,6 @@ namespace MarcoBMS.Services.Service
return new JwtSecurityTokenHandler().WriteToken(token); return new JwtSecurityTokenHandler().WriteToken(token);
} }
public async Task<string> CreateRefreshToken(string userId, string tenantId, JwtSettings jwtSettings) public async Task<string> CreateRefreshToken(string userId, string tenantId, JwtSettings jwtSettings)
{ {
try try
@ -98,7 +170,7 @@ namespace MarcoBMS.Services.Service
throw; throw;
} }
} }
public string CreateMPINToken(string userId, string tenantId, JwtSettings jwtSettings) public string CreateMPINToken(string userId, string organizationId, JwtSettings jwtSettings)
{ {
try try
{ {
@ -106,7 +178,7 @@ namespace MarcoBMS.Services.Service
var claims = new[] var claims = new[]
{ {
new Claim(ClaimTypes.NameIdentifier, userId), new Claim(ClaimTypes.NameIdentifier, userId),
new Claim("TenantId", tenantId), new Claim("OrganizationId", organizationId),
new Claim("token_type", "mpin") new Claim("token_type", "mpin")
}; };
@ -125,30 +197,23 @@ namespace MarcoBMS.Services.Service
var tokenHandler = new JwtSecurityTokenHandler(); var tokenHandler = new JwtSecurityTokenHandler();
var MPINToken = tokenHandler.WriteToken(tokenHandler.CreateToken(tokenDescriptor)); var MPINToken = tokenHandler.WriteToken(tokenHandler.CreateToken(tokenDescriptor));
return MPINToken; return MPINToken;
} }
catch (Exception ex) 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; throw;
} }
} }
public async Task<RefreshToken> GetRefreshToken(string token) public async Task<RefreshToken> GetRefreshToken(string token)
{ {
return await _context.RefreshTokens.FirstOrDefaultAsync(rt => rt.Token == token && !rt.IsRevoked && !rt.IsUsed) ?? new RefreshToken(); return await _context.RefreshTokens.FirstOrDefaultAsync(rt => rt.Token == token && !rt.IsRevoked && !rt.IsUsed) ?? new RefreshToken();
} }
public async Task MarkRefreshTokenAsUsed(RefreshToken refreshToken) public async Task MarkRefreshTokenAsUsed(RefreshToken refreshToken)
{ {
refreshToken.IsUsed = true; refreshToken.IsUsed = true;
_context.RefreshTokens.Update(refreshToken); _context.RefreshTokens.Update(refreshToken);
await _context.SaveChangesAsync(); await _context.SaveChangesAsync();
} }
public async Task RevokeRefreshToken(RefreshToken refreshToken) public async Task RevokeRefreshToken(RefreshToken refreshToken)
{ {
refreshToken.IsRevoked = true; refreshToken.IsRevoked = true;