using ColaFlow.Shared.Kernel.Common;
using ColaFlow.Modules.Identity.Domain.Aggregates.Users;
namespace ColaFlow.Modules.Identity.Domain.Entities;
///
/// Password reset token entity with enhanced security.
/// Lifetime: 1 hour (short expiration for security).
/// Single-use only (cannot be reused).
///
public sealed class PasswordResetToken : Entity
{
public UserId UserId { get; private set; } = null!;
public string TokenHash { get; private set; } = string.Empty;
public DateTime ExpiresAt { get; private set; }
public DateTime? UsedAt { get; private set; }
public string? IpAddress { get; private set; }
public string? UserAgent { get; private set; }
public DateTime CreatedAt { get; private set; }
// Private constructor for EF Core
private PasswordResetToken() : base()
{
}
///
/// Factory method to create new password reset token.
///
/// User ID requesting password reset
/// SHA-256 hash of the reset token
/// Expiration time (typically 1 hour from creation)
/// IP address of the requester
/// User agent of the requester
/// New password reset token instance
public static PasswordResetToken Create(
UserId userId,
string tokenHash,
DateTime expiresAt,
string? ipAddress = null,
string? userAgent = null)
{
return new PasswordResetToken
{
Id = Guid.NewGuid(),
UserId = userId,
TokenHash = tokenHash,
ExpiresAt = expiresAt,
IpAddress = ipAddress,
UserAgent = userAgent,
CreatedAt = DateTime.UtcNow
};
}
///
/// Check if token has expired.
///
public bool IsExpired => DateTime.UtcNow > ExpiresAt;
///
/// Check if token has been used.
///
public bool IsUsed => UsedAt.HasValue;
///
/// Check if token is valid (not expired and not used).
///
public bool IsValid => !IsExpired && !IsUsed;
///
/// Mark the token as used.
/// Can only be used once for security.
///
/// Thrown if token is not valid
public void MarkAsUsed()
{
if (!IsValid)
throw new InvalidOperationException("Token is not valid for password reset");
UsedAt = DateTime.UtcNow;
}
}