From e7a1f79806044239848a74fcdd2f18e1ec175ee2 Mon Sep 17 00:00:00 2001 From: alireza Date: Thu, 3 Sep 2026 07:49:30 +0330 Subject: [PATCH] fix: improve auth flow and verification --- .../Controllers/AuthController.cs | 18 ++++++ .../Interfaces/IAuthService.cs | 1 + .../Services/Auth/AuthService.cs | 55 +++++++++++++++++-- 3 files changed, 68 insertions(+), 6 deletions(-) diff --git a/src/GreenHome.Api/Controllers/AuthController.cs b/src/GreenHome.Api/Controllers/AuthController.cs index 00df733..dbe2b90 100644 --- a/src/GreenHome.Api/Controllers/AuthController.cs +++ b/src/GreenHome.Api/Controllers/AuthController.cs @@ -52,5 +52,23 @@ public class AuthController : ControllerBase var canResend = await authService.CanResendCodeAsync(mobile, cancellationToken); return Ok(new { canResend }); } + + [HttpGet("me")] + public async Task> Me(CancellationToken cancellationToken) + { + var authHeader = Request.Headers.Authorization.ToString(); + var token = authHeader.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase) + ? authHeader.Substring("Bearer ".Length).Trim() + : null; + + if (string.IsNullOrEmpty(token)) + return Unauthorized(); + + var user = await authService.GetUserByTokenAsync(token, cancellationToken); + if (user == null) + return Unauthorized(); + + return Ok(user); + } } diff --git a/src/GreenHome.Application/Interfaces/IAuthService.cs b/src/GreenHome.Application/Interfaces/IAuthService.cs index fb7962a..5e0409a 100644 --- a/src/GreenHome.Application/Interfaces/IAuthService.cs +++ b/src/GreenHome.Application/Interfaces/IAuthService.cs @@ -5,4 +5,5 @@ public interface IAuthService Task SendVerificationCodeAsync(SendCodeRequest request, CancellationToken cancellationToken); Task VerifyCodeAsync(VerifyCodeRequest request, CancellationToken cancellationToken); Task CanResendCodeAsync(string mobile, CancellationToken cancellationToken); + Task GetUserByTokenAsync(string token, CancellationToken cancellationToken); } diff --git a/src/GreenHome.Infrastructure/Services/Auth/AuthService.cs b/src/GreenHome.Infrastructure/Services/Auth/AuthService.cs index 7907be5..6d9ccab 100644 --- a/src/GreenHome.Infrastructure/Services/Auth/AuthService.cs +++ b/src/GreenHome.Infrastructure/Services/Auth/AuthService.cs @@ -2,6 +2,7 @@ using GreenHome.Application; using GreenHome.Infrastructure.Data; using GreenHome.Sms.Ippanel; using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; using System.Security.Cryptography; using System.Text; @@ -13,17 +14,20 @@ public sealed class AuthService : IAuthService private readonly GreenHomeDbContext _dbContext; private readonly ISmsService _smsService; private readonly ILogger _logger; + private readonly string _tokenSecret; private const int CodeExpirationMinutes = 5; private const int ResendCooldownSeconds = 120; public AuthService( GreenHomeDbContext dbContext, ISmsService smsService, + IConfiguration configuration, ILogger logger) { _dbContext = dbContext; _smsService = smsService; _logger = logger; + _tokenSecret = configuration["Auth:TokenSecret"] ?? "greenhome-dev-secret-change-me"; } public async Task SendVerificationCodeAsync( @@ -162,7 +166,7 @@ public sealed class AuthService : IAuthService await _dbContext.SaveChangesAsync(cancellationToken); - var token = GenerateToken(user.Id, mobile); + var token = GenerateToken(user.Id); return new VerifyCodeResponse { @@ -201,12 +205,51 @@ public sealed class AuthService : IAuthService return random.Next(1000, 9999).ToString(); } - private static string GenerateToken(int userId, string mobile) + public async Task GetUserByTokenAsync(string token, CancellationToken cancellationToken) { - var data = $"{userId}:{mobile}:{DateTime.UtcNow:yyyyMMddHHmmss}"; - var bytes = Encoding.UTF8.GetBytes(data); - var hash = SHA256.HashData(bytes); - return Convert.ToBase64String(hash); + if (string.IsNullOrWhiteSpace(token)) + return null; + + var parts = token.Split('.'); + if (parts.Length != 3) + return null; + + if (!int.TryParse(parts[0], out var userId) || !long.TryParse(parts[1], out var expiresAt)) + return null; + + if (expiresAt < DateTimeOffset.UtcNow.ToUnixTimeSeconds()) + return null; + + var payload = $"{parts[0]}.{parts[1]}"; + var expectedSignature = Convert.ToBase64String( + HMACSHA256.HashData(Encoding.UTF8.GetBytes(_tokenSecret), Encoding.UTF8.GetBytes(payload))); + + if (!CryptographicOperations.FixedTimeEquals( + Encoding.UTF8.GetBytes(expectedSignature), + Encoding.UTF8.GetBytes(parts[2]))) + return null; + + var user = await _dbContext.Users.FirstOrDefaultAsync(u => u.Id == userId, cancellationToken); + if (user == null) + return null; + + return new UserDto + { + Id = user.Id, + Mobile = user.Mobile, + Name = user.Name, + Family = user.Family, + Role = user.Role + }; + } + + private string GenerateToken(int userId) + { + var expiresAt = DateTimeOffset.UtcNow.AddDays(30).ToUnixTimeSeconds(); + var payload = $"{userId}.{expiresAt}"; + var signature = Convert.ToBase64String( + HMACSHA256.HashData(Encoding.UTF8.GetBytes(_tokenSecret), Encoding.UTF8.GetBytes(payload))); + return $"{payload}.{signature}"; } private static string NormalizeMobile(string mobile)