mirror of
https://github.com/opelly27/WinStudentGoalTracker.git
synced 2026-05-20 06:27:37 +00:00
moved database primary keys to guids
This commit is contained in:
@@ -5,14 +5,14 @@ namespace WinStudentGoalTracker.BaseClasses;
|
|||||||
|
|
||||||
public class BaseController : ControllerBase
|
public class BaseController : ControllerBase
|
||||||
{
|
{
|
||||||
protected (int userId, ActionResult? error) GetUserIdFromClaims()
|
protected (Guid userId, ActionResult? error) GetUserIdFromClaims()
|
||||||
{
|
{
|
||||||
var userIdClaim = User.FindFirst("user_id")?.Value
|
var userIdClaim = User.FindFirst("user_id")?.Value
|
||||||
?? User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
|
?? User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(userIdClaim) || !int.TryParse(userIdClaim, out var userId))
|
if (string.IsNullOrWhiteSpace(userIdClaim) || !Guid.TryParse(userIdClaim, out var userId))
|
||||||
{
|
{
|
||||||
return (0, Unauthorized("Missing or invalid user_id claim."));
|
return (Guid.Empty, Unauthorized("Missing or invalid user_id claim."));
|
||||||
}
|
}
|
||||||
|
|
||||||
return (userId, null);
|
return (userId, null);
|
||||||
|
|||||||
@@ -87,6 +87,7 @@ public class AuthController : BaseController
|
|||||||
|
|
||||||
// Store refresh token in database (30 days expiration)
|
// Store refresh token in database (30 days expiration)
|
||||||
var refreshTokenId = await _authRepo.CreateRefreshTokenAsync(
|
var refreshTokenId = await _authRepo.CreateRefreshTokenAsync(
|
||||||
|
Guid.NewGuid(),
|
||||||
user.IdUser,
|
user.IdUser,
|
||||||
refreshTokenHash,
|
refreshTokenHash,
|
||||||
refreshTokenSalt,
|
refreshTokenSalt,
|
||||||
@@ -151,7 +152,7 @@ public class AuthController : BaseController
|
|||||||
var tokenIdStr = refreshTokenDto.RefreshToken[..dotIndex];
|
var tokenIdStr = refreshTokenDto.RefreshToken[..dotIndex];
|
||||||
var secretToken = refreshTokenDto.RefreshToken[(dotIndex + 1)..];
|
var secretToken = refreshTokenDto.RefreshToken[(dotIndex + 1)..];
|
||||||
|
|
||||||
if (!int.TryParse(tokenIdStr, out int tokenId))
|
if (!Guid.TryParse(tokenIdStr, out Guid tokenId))
|
||||||
{
|
{
|
||||||
return BadRequest(new ResponseResult<TokenRefreshResponse>
|
return BadRequest(new ResponseResult<TokenRefreshResponse>
|
||||||
{
|
{
|
||||||
@@ -221,6 +222,7 @@ public class AuthController : BaseController
|
|||||||
|
|
||||||
var newRefreshTokenId = await _authRepo.ReplaceRefreshTokenAsync(
|
var newRefreshTokenId = await _authRepo.ReplaceRefreshTokenAsync(
|
||||||
matchedToken.IdRefreshToken,
|
matchedToken.IdRefreshToken,
|
||||||
|
Guid.NewGuid(),
|
||||||
tokenUser.IdUser,
|
tokenUser.IdUser,
|
||||||
newRefreshTokenHash,
|
newRefreshTokenHash,
|
||||||
newRefreshTokenSalt,
|
newRefreshTokenSalt,
|
||||||
@@ -272,7 +274,7 @@ public class AuthController : BaseController
|
|||||||
if (error != null) return error;
|
if (error != null) return error;
|
||||||
|
|
||||||
var dotIndex = logoutDto.RefreshToken.IndexOf('.');
|
var dotIndex = logoutDto.RefreshToken.IndexOf('.');
|
||||||
if (dotIndex < 1 || !int.TryParse(logoutDto.RefreshToken[..dotIndex], out int tokenId))
|
if (dotIndex < 1 || !Guid.TryParse(logoutDto.RefreshToken[..dotIndex], out Guid tokenId))
|
||||||
{
|
{
|
||||||
return BadRequest(new ResponseResult<object>
|
return BadRequest(new ResponseResult<object>
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -29,10 +29,10 @@ public class StudentController : BaseController
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("{idStudent:int}")]
|
[HttpGet("{idStudent:guid}")]
|
||||||
[ProducesResponseType(typeof(ResponseResult<StudentResponse>), StatusCodes.Status200OK)]
|
[ProducesResponseType(typeof(ResponseResult<StudentResponse>), StatusCodes.Status200OK)]
|
||||||
[ProducesResponseType(typeof(ResponseResult<StudentResponse>), StatusCodes.Status404NotFound)]
|
[ProducesResponseType(typeof(ResponseResult<StudentResponse>), StatusCodes.Status404NotFound)]
|
||||||
public async Task<ActionResult<ResponseResult<StudentResponse>>> GetById(int idStudent)
|
public async Task<ActionResult<ResponseResult<StudentResponse>>> GetById(Guid idStudent)
|
||||||
{
|
{
|
||||||
var student = await _studentRepository.GetByIdAsync(idStudent);
|
var student = await _studentRepository.GetByIdAsync(idStudent);
|
||||||
if (student is null)
|
if (student is null)
|
||||||
@@ -84,10 +84,10 @@ public class StudentController : BaseController
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPut("{idStudent:int}")]
|
[HttpPut("{idStudent:guid}")]
|
||||||
[ProducesResponseType(typeof(ResponseResult<StudentResponse>), StatusCodes.Status200OK)]
|
[ProducesResponseType(typeof(ResponseResult<StudentResponse>), StatusCodes.Status200OK)]
|
||||||
[ProducesResponseType(typeof(ResponseResult<StudentResponse>), StatusCodes.Status404NotFound)]
|
[ProducesResponseType(typeof(ResponseResult<StudentResponse>), StatusCodes.Status404NotFound)]
|
||||||
public async Task<ActionResult<ResponseResult<StudentResponse>>> Update(int idStudent, [FromBody] UpdateStudentDto request)
|
public async Task<ActionResult<ResponseResult<StudentResponse>>> Update(Guid idStudent, [FromBody] UpdateStudentDto request)
|
||||||
{
|
{
|
||||||
var existing = await _studentRepository.GetByIdAsync(idStudent);
|
var existing = await _studentRepository.GetByIdAsync(idStudent);
|
||||||
if (existing is null)
|
if (existing is null)
|
||||||
@@ -118,10 +118,10 @@ public class StudentController : BaseController
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpDelete("{idStudent:int}")]
|
[HttpDelete("{idStudent:guid}")]
|
||||||
[ProducesResponseType(typeof(ResponseResult<object>), StatusCodes.Status200OK)]
|
[ProducesResponseType(typeof(ResponseResult<object>), StatusCodes.Status200OK)]
|
||||||
[ProducesResponseType(typeof(ResponseResult<object>), StatusCodes.Status404NotFound)]
|
[ProducesResponseType(typeof(ResponseResult<object>), StatusCodes.Status404NotFound)]
|
||||||
public async Task<ActionResult<ResponseResult<object>>> Delete(int idStudent)
|
public async Task<ActionResult<ResponseResult<object>>> Delete(Guid idStudent)
|
||||||
{
|
{
|
||||||
var deleted = await _studentRepository.DeleteAsync(idStudent);
|
var deleted = await _studentRepository.DeleteAsync(idStudent);
|
||||||
if (!deleted)
|
if (!deleted)
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ namespace WinStudentGoalTracker.DataAccess;
|
|||||||
|
|
||||||
public class CreateStudentDto
|
public class CreateStudentDto
|
||||||
{
|
{
|
||||||
public required int IdStudent { get; set; }
|
public required Guid IdStudent { get; set; }
|
||||||
public int? IdProgram { get; set; }
|
public Guid? IdProgram { get; set; }
|
||||||
public string? Identifier { get; set; }
|
public string? Identifier { get; set; }
|
||||||
public int? ProgramYear { get; set; }
|
public int? ProgramYear { get; set; }
|
||||||
public DateTime? EnrollmentDate { get; set; }
|
public DateTime? EnrollmentDate { get; set; }
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ namespace WinStudentGoalTracker.DataAccess;
|
|||||||
|
|
||||||
public class UpdateStudentDto
|
public class UpdateStudentDto
|
||||||
{
|
{
|
||||||
public int? IdProgram { get; set; }
|
public Guid? IdProgram { get; set; }
|
||||||
public string? Identifier { get; set; }
|
public string? Identifier { get; set; }
|
||||||
public int? ProgramYear { get; set; }
|
public int? ProgramYear { get; set; }
|
||||||
public DateTime? EnrollmentDate { get; set; }
|
public DateTime? EnrollmentDate { get; set; }
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ namespace WinStudentGoalTracker.DataAccess;
|
|||||||
|
|
||||||
public class dbRefreshToken
|
public class dbRefreshToken
|
||||||
{
|
{
|
||||||
public int IdRefreshToken { get; set; }
|
public Guid IdRefreshToken { get; set; }
|
||||||
public int IdUser { get; set; }
|
public Guid IdUser { get; set; }
|
||||||
public required string TokenHash { get; set; }
|
public required string TokenHash { get; set; }
|
||||||
public required string TokenSalt { get; set; }
|
public required string TokenSalt { get; set; }
|
||||||
public DateTime ExpiresAt { get; set; }
|
public DateTime ExpiresAt { get; set; }
|
||||||
@@ -11,7 +11,7 @@ public class dbRefreshToken
|
|||||||
public DateTime? RevokedAt { get; set; }
|
public DateTime? RevokedAt { get; set; }
|
||||||
public string? DeviceInfo { get; set; }
|
public string? DeviceInfo { get; set; }
|
||||||
public string? UserAgent { get; set; }
|
public string? UserAgent { get; set; }
|
||||||
public int? ReplacedByTokenId { get; set; }
|
public Guid? ReplacedByTokenId { get; set; }
|
||||||
public DateTime CreatedAt { get; set; }
|
public DateTime CreatedAt { get; set; }
|
||||||
public DateTime UpdatedAt { get; set; }
|
public DateTime UpdatedAt { get; set; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ namespace WinStudentGoalTracker.DataAccess;
|
|||||||
|
|
||||||
public class dbStudent
|
public class dbStudent
|
||||||
{
|
{
|
||||||
public required int IdStudent { get; set; }
|
public required Guid IdStudent { get; set; }
|
||||||
public int? IdProgram { get; set; }
|
public Guid? IdProgram { get; set; }
|
||||||
public string? Identifier { get; set; }
|
public string? Identifier { get; set; }
|
||||||
public int? ProgramYear { get; set; }
|
public int? ProgramYear { get; set; }
|
||||||
public DateTime? EnrollmentDate { get; set; }
|
public DateTime? EnrollmentDate { get; set; }
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ namespace WinStudentGoalTracker.DataAccess;
|
|||||||
|
|
||||||
public class dbUser
|
public class dbUser
|
||||||
{
|
{
|
||||||
public required int IdUser { get; set; }
|
public required Guid IdUser { get; set; }
|
||||||
public int? IdRole { get; set; }
|
public Guid? IdRole { get; set; }
|
||||||
public string? Email { get; set; }
|
public string? Email { get; set; }
|
||||||
public string? Name { get; set; }
|
public string? Name { get; set; }
|
||||||
public string? PasswordHash { get; set; }
|
public string? PasswordHash { get; set; }
|
||||||
|
|||||||
@@ -8,8 +8,9 @@ public class AuthRepository
|
|||||||
{
|
{
|
||||||
private IDbConnection Connection => new MySqlConnection(DatabaseManager.ConnectionString);
|
private IDbConnection Connection => new MySqlConnection(DatabaseManager.ConnectionString);
|
||||||
|
|
||||||
public async Task<int?> CreateRefreshTokenAsync(
|
public async Task<Guid?> CreateRefreshTokenAsync(
|
||||||
int userId,
|
Guid refreshTokenId,
|
||||||
|
Guid userId,
|
||||||
string tokenHash,
|
string tokenHash,
|
||||||
string tokenSalt,
|
string tokenSalt,
|
||||||
int expiresInSeconds,
|
int expiresInSeconds,
|
||||||
@@ -17,11 +18,12 @@ public class AuthRepository
|
|||||||
string? userAgent)
|
string? userAgent)
|
||||||
{
|
{
|
||||||
using var db = Connection;
|
using var db = Connection;
|
||||||
return await db.QuerySingleOrDefaultAsync<int?>(
|
var result = await db.QuerySingleOrDefaultAsync<string?>(
|
||||||
"sp_RefreshToken_Create",
|
"sp_RefreshToken_Create",
|
||||||
new
|
new
|
||||||
{
|
{
|
||||||
p_id_user = userId,
|
p_id_refresh_token = refreshTokenId.ToString(),
|
||||||
|
p_id_user = userId.ToString(),
|
||||||
p_token_hash = tokenHash,
|
p_token_hash = tokenHash,
|
||||||
p_token_salt = tokenSalt,
|
p_token_salt = tokenSalt,
|
||||||
p_expires_in_seconds = expiresInSeconds,
|
p_expires_in_seconds = expiresInSeconds,
|
||||||
@@ -29,30 +31,32 @@ public class AuthRepository
|
|||||||
p_user_agent = userAgent
|
p_user_agent = userAgent
|
||||||
},
|
},
|
||||||
commandType: CommandType.StoredProcedure);
|
commandType: CommandType.StoredProcedure);
|
||||||
|
return result != null ? Guid.Parse(result) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<dbRefreshToken?> GetRefreshTokenByIdAsync(int refreshTokenId)
|
public async Task<dbRefreshToken?> GetRefreshTokenByIdAsync(Guid refreshTokenId)
|
||||||
{
|
{
|
||||||
using var db = Connection;
|
using var db = Connection;
|
||||||
return await db.QuerySingleOrDefaultAsync<dbRefreshToken>(
|
return await db.QuerySingleOrDefaultAsync<dbRefreshToken>(
|
||||||
"sp_RefreshToken_GetById",
|
"sp_RefreshToken_GetById",
|
||||||
new { p_id_refresh_token = refreshTokenId },
|
new { p_id_refresh_token = refreshTokenId.ToString() },
|
||||||
commandType: CommandType.StoredProcedure);
|
commandType: CommandType.StoredProcedure);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<bool> RevokeRefreshTokenAsync(int refreshTokenId)
|
public async Task<bool> RevokeRefreshTokenAsync(Guid refreshTokenId)
|
||||||
{
|
{
|
||||||
using var db = Connection;
|
using var db = Connection;
|
||||||
var rowsAffected = await db.QuerySingleOrDefaultAsync<int>(
|
var rowsAffected = await db.QuerySingleOrDefaultAsync<int>(
|
||||||
"sp_RefreshToken_Revoke",
|
"sp_RefreshToken_Revoke",
|
||||||
new { p_id_refresh_token = refreshTokenId },
|
new { p_id_refresh_token = refreshTokenId.ToString() },
|
||||||
commandType: CommandType.StoredProcedure);
|
commandType: CommandType.StoredProcedure);
|
||||||
return rowsAffected > 0;
|
return rowsAffected > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<int?> ReplaceRefreshTokenAsync(
|
public async Task<Guid?> ReplaceRefreshTokenAsync(
|
||||||
int oldTokenId,
|
Guid oldTokenId,
|
||||||
int userId,
|
Guid newTokenId,
|
||||||
|
Guid userId,
|
||||||
string tokenHash,
|
string tokenHash,
|
||||||
string tokenSalt,
|
string tokenSalt,
|
||||||
int expiresInSeconds,
|
int expiresInSeconds,
|
||||||
@@ -60,12 +64,13 @@ public class AuthRepository
|
|||||||
string? userAgent)
|
string? userAgent)
|
||||||
{
|
{
|
||||||
using var db = Connection;
|
using var db = Connection;
|
||||||
return await db.QuerySingleOrDefaultAsync<int?>(
|
var result = await db.QuerySingleOrDefaultAsync<string?>(
|
||||||
"sp_RefreshToken_Replace",
|
"sp_RefreshToken_Replace",
|
||||||
new
|
new
|
||||||
{
|
{
|
||||||
p_old_token_id = oldTokenId,
|
p_old_token_id = oldTokenId.ToString(),
|
||||||
p_id_user = userId,
|
p_id_refresh_token = newTokenId.ToString(),
|
||||||
|
p_id_user = userId.ToString(),
|
||||||
p_token_hash = tokenHash,
|
p_token_hash = tokenHash,
|
||||||
p_token_salt = tokenSalt,
|
p_token_salt = tokenSalt,
|
||||||
p_expires_in_seconds = expiresInSeconds,
|
p_expires_in_seconds = expiresInSeconds,
|
||||||
@@ -73,5 +78,6 @@ public class AuthRepository
|
|||||||
p_user_agent = userAgent
|
p_user_agent = userAgent
|
||||||
},
|
},
|
||||||
commandType: CommandType.StoredProcedure);
|
commandType: CommandType.StoredProcedure);
|
||||||
|
return result != null ? Guid.Parse(result) : null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,12 +16,12 @@ public class StudentRepository
|
|||||||
commandType: CommandType.StoredProcedure);
|
commandType: CommandType.StoredProcedure);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<dbStudent?> GetByIdAsync(int idStudent)
|
public async Task<dbStudent?> GetByIdAsync(Guid idStudent)
|
||||||
{
|
{
|
||||||
using var db = Connection;
|
using var db = Connection;
|
||||||
return await db.QuerySingleOrDefaultAsync<dbStudent>(
|
return await db.QuerySingleOrDefaultAsync<dbStudent>(
|
||||||
"sp_Student_GetById",
|
"sp_Student_GetById",
|
||||||
new { p_id_student = idStudent },
|
new { p_id_student = idStudent.ToString() },
|
||||||
commandType: CommandType.StoredProcedure);
|
commandType: CommandType.StoredProcedure);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -32,8 +32,8 @@ public class StudentRepository
|
|||||||
"sp_Student_Insert",
|
"sp_Student_Insert",
|
||||||
new
|
new
|
||||||
{
|
{
|
||||||
p_id_student = dto.IdStudent,
|
p_id_student = dto.IdStudent.ToString(),
|
||||||
p_id_program = dto.IdProgram,
|
p_id_program = dto.IdProgram?.ToString(),
|
||||||
p_identifier = dto.Identifier,
|
p_identifier = dto.Identifier,
|
||||||
p_program_year = dto.ProgramYear,
|
p_program_year = dto.ProgramYear,
|
||||||
p_enrollment_date = dto.EnrollmentDate,
|
p_enrollment_date = dto.EnrollmentDate,
|
||||||
@@ -42,15 +42,15 @@ public class StudentRepository
|
|||||||
commandType: CommandType.StoredProcedure);
|
commandType: CommandType.StoredProcedure);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<bool> UpdateAsync(int idStudent, UpdateStudentDto dto)
|
public async Task<bool> UpdateAsync(Guid idStudent, UpdateStudentDto dto)
|
||||||
{
|
{
|
||||||
using var db = Connection;
|
using var db = Connection;
|
||||||
var rowsAffected = await db.ExecuteScalarAsync<int>(
|
var rowsAffected = await db.ExecuteScalarAsync<int>(
|
||||||
"sp_Student_Update",
|
"sp_Student_Update",
|
||||||
new
|
new
|
||||||
{
|
{
|
||||||
p_id_student = idStudent,
|
p_id_student = idStudent.ToString(),
|
||||||
p_id_program = dto.IdProgram,
|
p_id_program = dto.IdProgram?.ToString(),
|
||||||
p_identifier = dto.Identifier,
|
p_identifier = dto.Identifier,
|
||||||
p_program_year = dto.ProgramYear,
|
p_program_year = dto.ProgramYear,
|
||||||
p_enrollment_date = dto.EnrollmentDate,
|
p_enrollment_date = dto.EnrollmentDate,
|
||||||
@@ -60,12 +60,12 @@ public class StudentRepository
|
|||||||
return rowsAffected > 0;
|
return rowsAffected > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<bool> DeleteAsync(int idStudent)
|
public async Task<bool> DeleteAsync(Guid idStudent)
|
||||||
{
|
{
|
||||||
using var db = Connection;
|
using var db = Connection;
|
||||||
var rowsAffected = await db.ExecuteScalarAsync<int>(
|
var rowsAffected = await db.ExecuteScalarAsync<int>(
|
||||||
"sp_Student_Delete",
|
"sp_Student_Delete",
|
||||||
new { p_id_student = idStudent },
|
new { p_id_student = idStudent.ToString() },
|
||||||
commandType: CommandType.StoredProcedure);
|
commandType: CommandType.StoredProcedure);
|
||||||
return rowsAffected > 0;
|
return rowsAffected > 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,12 +17,12 @@ public class UserRepository
|
|||||||
commandType: CommandType.StoredProcedure);
|
commandType: CommandType.StoredProcedure);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<dbUser?> GetByIdAsync(int idUser)
|
public async Task<dbUser?> GetByIdAsync(Guid idUser)
|
||||||
{
|
{
|
||||||
using var db = Connection;
|
using var db = Connection;
|
||||||
return await db.QuerySingleOrDefaultAsync<dbUser>(
|
return await db.QuerySingleOrDefaultAsync<dbUser>(
|
||||||
"sp_User_GetById",
|
"sp_User_GetById",
|
||||||
new { p_id_user = idUser },
|
new { p_id_user = idUser.ToString() },
|
||||||
commandType: CommandType.StoredProcedure);
|
commandType: CommandType.StoredProcedure);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ namespace WinStudentGoalTracker.Models;
|
|||||||
|
|
||||||
public class LoginResponse
|
public class LoginResponse
|
||||||
{
|
{
|
||||||
public int UserId { get; set; }
|
public Guid UserId { get; set; }
|
||||||
public required string Email { get; set; }
|
public required string Email { get; set; }
|
||||||
public required string Jwt { get; set; }
|
public required string Jwt { get; set; }
|
||||||
public required string RefreshToken { get; set; }
|
public required string RefreshToken { get; set; }
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ namespace WinStudentGoalTracker.Models;
|
|||||||
|
|
||||||
public class StudentResponse
|
public class StudentResponse
|
||||||
{
|
{
|
||||||
public int IdStudent { get; set; }
|
public Guid IdStudent { get; set; }
|
||||||
public int? IdProgram { get; set; }
|
public Guid? IdProgram { get; set; }
|
||||||
public string? Identifier { get; set; }
|
public string? Identifier { get; set; }
|
||||||
public int? ProgramYear { get; set; }
|
public int? ProgramYear { get; set; }
|
||||||
public DateTime? EnrollmentDate { get; set; }
|
public DateTime? EnrollmentDate { get; set; }
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ public class TokenService
|
|||||||
_config = config;
|
_config = config;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string GenerateToken(int userId, string email, string? roleName)
|
public string GenerateToken(Guid userId, string email, string? roleName)
|
||||||
{
|
{
|
||||||
var claims = new List<Claim>
|
var claims = new List<Claim>
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user