690 lines
29 KiB
C#
690 lines
29 KiB
C#
using Fitness_core_BE.DTOs.Auth;
|
|
using Fitness_core_BE.Helpers;
|
|
using Fitness_core_BE.Infra.UoW;
|
|
using Fitness_core_BE.Interfaces;
|
|
using Fitness_core_BE.Interfaces.Services;
|
|
using Fitness_core_BE.Models;
|
|
using System.Net.Http;
|
|
using System.Text;
|
|
using System.Text.Json;
|
|
|
|
namespace Fitness_core_BE.Services.Auth
|
|
{
|
|
public class AuthService(IHttpClientFactory httpClientFactory, ICustomerRepository customerRepository, IUnitOfWork unitOfWork, IConfiguration configuration) : IAuthService
|
|
{
|
|
|
|
private readonly HttpClient _httpClient = httpClientFactory.CreateClient();
|
|
private readonly ICustomerRepository _cusRepo = customerRepository;
|
|
private readonly IUnitOfWork _UoW = unitOfWork;
|
|
private readonly string _baseUrl = configuration["AuthService:BaseUrl"] ?? "http://127.0.0.1:5602";
|
|
|
|
private string AuthAltTargetUrl => _baseUrl + "/api/alt";
|
|
private string AuthUserTargetUrl => _baseUrl + "/api/user";
|
|
private string AuthRecoveryTargetUrl => _baseUrl + "/api/recovery";
|
|
|
|
|
|
|
|
public async Task<AuthResponse> SendOtp(object payload, HttpContext httpContext)
|
|
{
|
|
var targetUrl = AuthAltTargetUrl;
|
|
var request = new
|
|
{
|
|
functionName = "sendOtp",
|
|
payload,
|
|
reference = "send-otp"
|
|
};
|
|
|
|
var json = JsonSerializer.Serialize(request);
|
|
|
|
var forwardRequest = new HttpRequestMessage(HttpMethod.Post, targetUrl)
|
|
{
|
|
Content = new StringContent(json, Encoding.UTF8, "application/json")
|
|
};
|
|
|
|
try
|
|
{
|
|
var response = await _httpClient.SendAsync(forwardRequest);
|
|
var responseContent = await response.Content.ReadAsStringAsync();
|
|
|
|
var authResponse = JsonSerializer.Deserialize<AuthResponse>(responseContent, new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
|
|
|
|
return authResponse ?? new AuthResponse { StatusCode = 500, Success = false, Message = "Empty response from auth service", Data = null };
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
return new AuthResponse { StatusCode = 500, Success = false, Message = $"Error communicating with auth service: {ex.Message}", Data = null };
|
|
}
|
|
}
|
|
|
|
public async Task<AuthResponse> LoginUser(object payload, HttpContext httpContext)
|
|
{
|
|
// Web clients explicitly opt in to a persistent 30-day cookie. Without
|
|
// this, closing the browser ends the session even though Auth retains the
|
|
// server-side refresh session for security/audit purposes.
|
|
var loginData = DeserializePayload.Deserialize(payload);
|
|
var rememberMe = loginData?.TryGetValue("rememberMe", out var rememberValue) == true
|
|
&& rememberValue.ValueKind is System.Text.Json.JsonValueKind.True;
|
|
var targetUrl = AuthUserTargetUrl;
|
|
|
|
var request = new
|
|
{
|
|
functionName = "loginUser",
|
|
payload,
|
|
reference = "LoginUser"
|
|
};
|
|
|
|
var json = JsonSerializer.Serialize(request);
|
|
|
|
var forwardRequest = new HttpRequestMessage(HttpMethod.Post, targetUrl)
|
|
{
|
|
Content = new StringContent(json, Encoding.UTF8, "application/json")
|
|
};
|
|
|
|
try
|
|
{
|
|
var response = await _httpClient.SendAsync(forwardRequest);
|
|
var responseContent = await response.Content.ReadAsStringAsync();
|
|
var authResponse = JsonSerializer.Deserialize<AuthResponse>( responseContent, new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
|
|
|
|
if (authResponse == null)
|
|
return new AuthResponse { StatusCode = 500, Success = false, Message = "Empty response from auth service", Data = null };
|
|
|
|
if (authResponse.Success && authResponse.Data is JsonElement dataElement)
|
|
{
|
|
SetAuthCookiesFromData(httpContext, dataElement, rememberMe);
|
|
}
|
|
|
|
return authResponse;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
throw new Exception($"Auth service error: {ex.Message}");
|
|
}
|
|
}
|
|
|
|
/// <summary>Sets the access_token (and refresh_token if present) cookies from an auth-service data payload.</summary>
|
|
private void SetAuthCookiesFromData(HttpContext httpContext, JsonElement dataElement, bool persistent = true)
|
|
{
|
|
string? accessToken = null;
|
|
if (dataElement.TryGetProperty("accessToken", out var tokenProp) ||
|
|
dataElement.TryGetProperty("AccessToken", out tokenProp))
|
|
accessToken = tokenProp.GetString();
|
|
if (!string.IsNullOrEmpty(accessToken))
|
|
SetAccessTokenCookieInternal(httpContext, accessToken, persistent);
|
|
|
|
string? refreshToken = null;
|
|
if (dataElement.TryGetProperty("refreshToken", out var refreshProp) ||
|
|
dataElement.TryGetProperty("RefreshToken", out refreshProp))
|
|
refreshToken = refreshProp.GetString();
|
|
if (!string.IsNullOrEmpty(refreshToken))
|
|
SetRefreshTokenCookie(httpContext, refreshToken, persistent);
|
|
}
|
|
|
|
public void SetAccessTokenCookie(HttpContext httpContext, string token)
|
|
=> SetAccessTokenCookieInternal(httpContext, token, persistent: true);
|
|
|
|
private void SetAccessTokenCookieInternal(HttpContext httpContext, string token, bool persistent)
|
|
{
|
|
var requestHost = httpContext.Request.Host.Host;
|
|
var forwardedProto = httpContext.Request.Headers["X-Forwarded-Proto"].FirstOrDefault();
|
|
var isHttpsRequest =
|
|
httpContext.Request.IsHttps ||
|
|
string.Equals(forwardedProto, "https", StringComparison.OrdinalIgnoreCase);
|
|
|
|
httpContext.Response.Cookies.Append("access_token", token, new CookieOptions
|
|
{
|
|
HttpOnly = true,
|
|
Secure = isHttpsRequest,
|
|
SameSite = isHttpsRequest ? SameSiteMode.None : SameSiteMode.Lax,
|
|
Expires = persistent ? DateTimeOffset.UtcNow.AddDays(30) : null,
|
|
Path = "/",
|
|
Domain = IsLocalHost(requestHost) ? null : "hexafit.hexdive.com"
|
|
});
|
|
}
|
|
|
|
public void SetRefreshTokenCookie(HttpContext httpContext, string refreshToken, bool persistent = true)
|
|
{
|
|
var requestHost = httpContext.Request.Host.Host;
|
|
var forwardedProto = httpContext.Request.Headers["X-Forwarded-Proto"].FirstOrDefault();
|
|
var isHttpsRequest =
|
|
httpContext.Request.IsHttps ||
|
|
string.Equals(forwardedProto, "https", StringComparison.OrdinalIgnoreCase);
|
|
|
|
httpContext.Response.Cookies.Append("refresh_token", refreshToken, new CookieOptions
|
|
{
|
|
HttpOnly = true,
|
|
Secure = isHttpsRequest,
|
|
SameSite = isHttpsRequest ? SameSiteMode.None : SameSiteMode.Lax,
|
|
Expires = persistent ? DateTimeOffset.UtcNow.AddDays(30) : null,
|
|
Path = "/",
|
|
Domain = IsLocalHost(requestHost) ? null : "hexafit.hexdive.com"
|
|
});
|
|
}
|
|
|
|
public void ClearAuthCookies(HttpContext httpContext)
|
|
{
|
|
var requestHost = httpContext.Request.Host.Host;
|
|
var domain = IsLocalHost(requestHost) ? null : "hexafit.hexdive.com";
|
|
var options = new CookieOptions { Path = "/", Domain = domain };
|
|
httpContext.Response.Cookies.Delete("access_token", options);
|
|
httpContext.Response.Cookies.Delete("refresh_token", options);
|
|
}
|
|
|
|
public async Task<AuthResponse> RefreshToken(object payload, HttpContext httpContext)
|
|
{
|
|
var targetUrl = AuthUserTargetUrl;
|
|
|
|
// The refresh token lives in an httpOnly cookie so the browser can trigger a
|
|
// silent refresh without ever holding it in JS. Fall back to the request payload
|
|
// (e.g. mobile clients that pass it explicitly).
|
|
string? refreshToken = null;
|
|
try
|
|
{
|
|
var data = DeserializePayload.Deserialize(payload);
|
|
if (data.TryGetValue("refreshToken", out var rt) && rt.ValueKind == JsonValueKind.String)
|
|
refreshToken = rt.GetString();
|
|
}
|
|
catch { /* payload may be empty on cookie-driven refresh */ }
|
|
|
|
if (string.IsNullOrEmpty(refreshToken))
|
|
refreshToken = httpContext.Request.Cookies["refresh_token"];
|
|
|
|
if (string.IsNullOrEmpty(refreshToken))
|
|
{
|
|
ClearAuthCookies(httpContext);
|
|
return new AuthResponse { StatusCode = 401, Success = false, Message = "Missing refresh token", Data = null };
|
|
}
|
|
|
|
var deviceName = httpContext.Request.Headers["User-Agent"].ToString();
|
|
var request = new
|
|
{
|
|
functionName = "refreshToken",
|
|
payload = new { refreshToken, deviceName },
|
|
reference = "RefreshToken"
|
|
};
|
|
|
|
var json = JsonSerializer.Serialize(request);
|
|
|
|
var forwardRequest = new HttpRequestMessage(HttpMethod.Post, targetUrl)
|
|
{
|
|
Content = new StringContent(json, Encoding.UTF8, "application/json")
|
|
};
|
|
|
|
if (!string.IsNullOrWhiteSpace(deviceName))
|
|
forwardRequest.Headers.TryAddWithoutValidation("User-Agent", deviceName);
|
|
|
|
var forwardedFor = httpContext.Connection.RemoteIpAddress?.ToString();
|
|
if (!string.IsNullOrWhiteSpace(forwardedFor))
|
|
forwardRequest.Headers.TryAddWithoutValidation("X-Forwarded-For", forwardedFor);
|
|
|
|
try
|
|
{
|
|
var response = await _httpClient.SendAsync(forwardRequest);
|
|
var responseContent = await response.Content.ReadAsStringAsync();
|
|
var authResponse = JsonSerializer.Deserialize<AuthResponse>(responseContent, new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
|
|
|
|
if (authResponse == null)
|
|
return new AuthResponse { StatusCode = 500, Success = false, Message = "Empty response from auth service", Data = null };
|
|
|
|
if (authResponse.Success && authResponse.Data is JsonElement dataElement)
|
|
{
|
|
// Core-Hex-Auth rotates the refresh token; persist both new cookies.
|
|
SetAuthCookiesFromData(httpContext, dataElement);
|
|
return authResponse;
|
|
}
|
|
|
|
// Refresh token invalid/expired — clear cookies and report 401 so the
|
|
// frontend redirects to login rather than surfacing a 500.
|
|
ClearAuthCookies(httpContext);
|
|
return new AuthResponse { StatusCode = 401, Success = false, Message = "Session expired. Please log in again.", Data = null };
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
return new AuthResponse { StatusCode = 500, Success = false, Message = $"Error communicating with auth service: {ex.Message}", Data = null };
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//customer ta only use? why
|
|
public async Task<AuthResponse> VerifyOTPForCus(object payload, HttpContext httpContext)
|
|
{
|
|
|
|
var data = DeserializePayload.Deserialize(payload);
|
|
|
|
|
|
|
|
var targetUrl = AuthRecoveryTargetUrl;
|
|
var request = new
|
|
{
|
|
functionName = "verifyOTP",
|
|
payload,
|
|
reference = "verify-otp"
|
|
};
|
|
|
|
var json = JsonSerializer.Serialize(request);
|
|
|
|
var forwardRequest = new HttpRequestMessage(HttpMethod.Post, targetUrl)
|
|
{
|
|
Content = new StringContent(json, Encoding.UTF8, "application/json")
|
|
};
|
|
|
|
try
|
|
{
|
|
var response = await _httpClient.SendAsync(forwardRequest);
|
|
var responseContent = await response.Content.ReadAsStringAsync();
|
|
|
|
var authResponse = JsonSerializer.Deserialize<AuthResponse>(responseContent, new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
|
|
|
|
if (authResponse == null)
|
|
throw new Exception("Empty response from auth service");
|
|
|
|
string identifireType = DataExtractor.GetRequiredString(data, "identifireType");
|
|
var identifier = DataExtractor.GetRequiredString(data, "identifier");
|
|
|
|
Guid userId = DataExtractor.GetUserIdFromContext(httpContext);
|
|
|
|
await _UoW.BeginAsync();
|
|
try
|
|
{
|
|
bool result = await _cusRepo.VerifyCustomer(userId, identifireType, identifier);
|
|
if (!result)
|
|
throw new Exception("verfifying faled");
|
|
await _UoW.CommitAsync();
|
|
}
|
|
catch(Exception ex)
|
|
{
|
|
await _UoW.RollbackAsync();
|
|
throw new Exception($"{ex.Message}");
|
|
}
|
|
|
|
return authResponse;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
return new AuthResponse { StatusCode = 500, Success = false, Message = $"Error communicating with auth service: {ex.Message}", Data = null };
|
|
}
|
|
}
|
|
|
|
|
|
public async Task<AuthResponse> VerifyOtpForLogin(object payload, HttpContext httpContext)
|
|
{
|
|
var targetUrl = AuthUserTargetUrl;
|
|
var request = new
|
|
{
|
|
functionName = "VerifyOtpForLogin",
|
|
payload,
|
|
reference = "verify-otp-for-login"
|
|
};
|
|
|
|
var json = JsonSerializer.Serialize(request);
|
|
|
|
var forwardRequest = new HttpRequestMessage(HttpMethod.Post, targetUrl)
|
|
{
|
|
Content = new StringContent(json, Encoding.UTF8, "application/json")
|
|
};
|
|
|
|
try
|
|
{
|
|
var response = await _httpClient.SendAsync(forwardRequest);
|
|
var responseContent = await response.Content.ReadAsStringAsync();
|
|
|
|
var authResponse = JsonSerializer.Deserialize<AuthResponse>(responseContent, new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
|
|
|
|
return authResponse ?? new AuthResponse { StatusCode = 500, Success = false, Message = "Empty response from auth service", Data = null };
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
return new AuthResponse { StatusCode = 500, Success = false, Message = $"Error communicating with auth service: {ex.Message}", Data = null };
|
|
}
|
|
}
|
|
|
|
|
|
|
|
public async Task<AuthResponse> IsAvailable(object payload, HttpContext httpContext)
|
|
{
|
|
var targetUrl = AuthAltTargetUrl;
|
|
var request = new
|
|
{
|
|
functionName = "IsAvailable",
|
|
payload,
|
|
reference = "is-available"
|
|
};
|
|
|
|
var json = JsonSerializer.Serialize(request);
|
|
|
|
var forwardRequest = new HttpRequestMessage(HttpMethod.Post, targetUrl)
|
|
{
|
|
Content = new StringContent(json, Encoding.UTF8, "application/json")
|
|
};
|
|
|
|
try
|
|
{
|
|
var response = await _httpClient.SendAsync(forwardRequest);
|
|
var responseContent = await response.Content.ReadAsStringAsync();
|
|
|
|
var authResponse = JsonSerializer.Deserialize<AuthResponse>(responseContent, new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
|
|
|
|
return authResponse ?? new AuthResponse { StatusCode = 500, Success = false, Message = "Empty response from auth service", Data = null };
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
return new AuthResponse { StatusCode = 500, Success = false, Message = $"Error communicating with auth service: {ex.Message}", Data = null };
|
|
}
|
|
}
|
|
|
|
|
|
public async Task<AuthResponse> RegisterUser(object payload, HttpContext httpContext)
|
|
{
|
|
var targetUrl = AuthUserTargetUrl;
|
|
var request = new
|
|
{
|
|
functionName = "registerUser",
|
|
payload,
|
|
reference = "register-user"
|
|
};
|
|
|
|
var json = JsonSerializer.Serialize(request);
|
|
|
|
var forwardRequest = new HttpRequestMessage(HttpMethod.Post, targetUrl)
|
|
{
|
|
Content = new StringContent(json, Encoding.UTF8, "application/json")
|
|
};
|
|
|
|
try
|
|
{
|
|
var response = await _httpClient.SendAsync(forwardRequest);
|
|
var responseContent = await response.Content.ReadAsStringAsync();
|
|
|
|
var authResponse = JsonSerializer.Deserialize<AuthResponse>(responseContent, new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
|
|
|
|
return authResponse ?? new AuthResponse { StatusCode = 500, Success = false, Message = "Empty response from auth service", Data = null };
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
return new AuthResponse { StatusCode = 500, Success = false, Message = $"Error communicating with auth service: {ex.Message}", Data = null };
|
|
}
|
|
}
|
|
|
|
|
|
public async Task<AuthResponse> GetUserDetails(object payload, HttpContext httpContext)
|
|
{
|
|
var targetUrl = AuthUserTargetUrl;
|
|
var request = new
|
|
{
|
|
functionName = "getUserDetails",
|
|
payload,
|
|
reference = "user-details"
|
|
};
|
|
|
|
var json = JsonSerializer.Serialize(request);
|
|
|
|
var forwardRequest = new HttpRequestMessage(HttpMethod.Post, targetUrl)
|
|
{
|
|
Content = new StringContent(json, Encoding.UTF8, "application/json")
|
|
};
|
|
|
|
var authHeader = httpContext.Request.Headers["Authorization"].FirstOrDefault();
|
|
if (!string.IsNullOrEmpty(authHeader))
|
|
{
|
|
forwardRequest.Headers.Authorization =
|
|
new System.Net.Http.Headers.AuthenticationHeaderValue(
|
|
"Bearer",
|
|
authHeader.Replace("Bearer ", "")
|
|
);
|
|
}
|
|
|
|
try
|
|
{
|
|
var response = await _httpClient.SendAsync(forwardRequest);
|
|
var responseContent = await response.Content.ReadAsStringAsync();
|
|
|
|
var authResponse = JsonSerializer.Deserialize<AuthResponse>(responseContent, new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
|
|
|
|
return authResponse ?? new AuthResponse { StatusCode = 500, Success = false, Message = "Empty response from auth service", Data = null };
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
return new AuthResponse { StatusCode = 500, Success = false, Message = $"Error communicating with auth service: {ex.Message}", Data = null };
|
|
}
|
|
}
|
|
|
|
|
|
public async Task<AuthResponse> UpdateUser(object payload, HttpContext httpContext)
|
|
{
|
|
var targetUrl = AuthUserTargetUrl;
|
|
var request = new
|
|
{
|
|
functionName = "UpdateUser",
|
|
payload,
|
|
reference = "user-details"
|
|
};
|
|
|
|
var json = JsonSerializer.Serialize(request);
|
|
|
|
var forwardRequest = new HttpRequestMessage(HttpMethod.Post, targetUrl)
|
|
{
|
|
Content = new StringContent(json, Encoding.UTF8, "application/json")
|
|
};
|
|
|
|
// Web clients authenticate via the access_token cookie, not an Authorization
|
|
// header — forward whichever is present so the auth service can read the userId.
|
|
ApplyAuthHeader(forwardRequest, httpContext);
|
|
|
|
try
|
|
{
|
|
var response = await _httpClient.SendAsync(forwardRequest);
|
|
var responseContent = await response.Content.ReadAsStringAsync();
|
|
|
|
var authResponse = JsonSerializer.Deserialize<AuthResponse>(responseContent, new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
|
|
|
|
return authResponse ?? new AuthResponse { StatusCode = 500, Success = false, Message = "Empty response from auth service", Data = null };
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
return new AuthResponse { StatusCode = 500, Success = false, Message = $"Error communicating with auth service: {ex.Message}", Data = null };
|
|
}
|
|
}
|
|
|
|
public async Task<AuthResponse> AdminUpdateUser(object payload, HttpContext httpContext)
|
|
{
|
|
var targetUrl = AuthUserTargetUrl;
|
|
var request = new
|
|
{
|
|
functionName = "adminUpdateUser",
|
|
payload,
|
|
reference = "admin-update-user"
|
|
};
|
|
|
|
var json = JsonSerializer.Serialize(request);
|
|
|
|
var forwardRequest = new HttpRequestMessage(HttpMethod.Post, targetUrl)
|
|
{
|
|
Content = new StringContent(json, Encoding.UTF8, "application/json")
|
|
};
|
|
|
|
try
|
|
{
|
|
var response = await _httpClient.SendAsync(forwardRequest);
|
|
var responseContent = await response.Content.ReadAsStringAsync();
|
|
|
|
var authResponse = JsonSerializer.Deserialize<AuthResponse>(responseContent, new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
|
|
|
|
return authResponse ?? new AuthResponse { StatusCode = 500, Success = false, Message = "Empty response from auth service", Data = null };
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
return new AuthResponse { StatusCode = 500, Success = false, Message = $"Error communicating with auth service: {ex.Message}", Data = null };
|
|
}
|
|
}
|
|
|
|
/// <summary>Forwards a recovery-manager function (forgotPassword / resetPassword) — no auth required.</summary>
|
|
private async Task<AuthResponse> ForwardToRecovery(string functionName, object payload)
|
|
{
|
|
var request = new { functionName, payload, reference = functionName };
|
|
var json = JsonSerializer.Serialize(request);
|
|
var forwardRequest = new HttpRequestMessage(HttpMethod.Post, AuthRecoveryTargetUrl)
|
|
{
|
|
Content = new StringContent(json, Encoding.UTF8, "application/json")
|
|
};
|
|
|
|
try
|
|
{
|
|
var response = await _httpClient.SendAsync(forwardRequest);
|
|
var responseContent = await response.Content.ReadAsStringAsync();
|
|
var authResponse = JsonSerializer.Deserialize<AuthResponse>(responseContent, new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
|
|
return authResponse ?? new AuthResponse { StatusCode = 500, Success = false, Message = "Empty response from auth service", Data = null };
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
return new AuthResponse { StatusCode = 500, Success = false, Message = $"Error communicating with auth service: {ex.Message}", Data = null };
|
|
}
|
|
}
|
|
|
|
public Task<AuthResponse> ForgotPassword(object payload, HttpContext httpContext)
|
|
=> ForwardToRecovery("forgotPassword", payload);
|
|
|
|
public Task<AuthResponse> ResetPassword(object payload, HttpContext httpContext)
|
|
=> ForwardToRecovery("resetPassword", payload);
|
|
|
|
public Task<AuthResponse> ResetPasswordWithToken(object payload, HttpContext httpContext)
|
|
=> ForwardToRecovery("resetPasswordWithToken", payload);
|
|
|
|
// Confirms the caller's own password (used to unlock sensitive views). Returns { valid }.
|
|
public async Task<AuthResponse> VerifyPassword(object payload, HttpContext httpContext)
|
|
{
|
|
var request = new { functionName = "VerifyPassword", payload, reference = "verify-password" };
|
|
var json = JsonSerializer.Serialize(request);
|
|
var forwardRequest = new HttpRequestMessage(HttpMethod.Post, AuthUserTargetUrl)
|
|
{
|
|
Content = new StringContent(json, Encoding.UTF8, "application/json")
|
|
};
|
|
ApplyAuthHeader(forwardRequest, httpContext);
|
|
|
|
try
|
|
{
|
|
var response = await _httpClient.SendAsync(forwardRequest);
|
|
var responseContent = await response.Content.ReadAsStringAsync();
|
|
var authResponse = JsonSerializer.Deserialize<AuthResponse>(responseContent, new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
|
|
return authResponse ?? new AuthResponse { StatusCode = 500, Success = false, Message = "Empty response from auth service", Data = null };
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
return new AuthResponse { StatusCode = 500, Success = false, Message = $"Error communicating with auth service: {ex.Message}", Data = null };
|
|
}
|
|
}
|
|
|
|
/// <summary>Sets the outgoing Bearer token from the Authorization header, the MobileAuthToken header, or the access_token cookie.</summary>
|
|
private static void ApplyAuthHeader(HttpRequestMessage req, HttpContext ctx)
|
|
{
|
|
string? token = null;
|
|
|
|
var authHeader = ctx.Request.Headers["Authorization"].FirstOrDefault();
|
|
if (!string.IsNullOrWhiteSpace(authHeader))
|
|
token = authHeader.Replace("Bearer ", "").Trim();
|
|
|
|
if (string.IsNullOrWhiteSpace(token))
|
|
{
|
|
var mobile = ctx.Request.Headers["MobileAuthToken"].FirstOrDefault();
|
|
if (!string.IsNullOrWhiteSpace(mobile) && mobile.StartsWith("Bearer "))
|
|
token = mobile.Substring("Bearer ".Length).Trim();
|
|
}
|
|
|
|
if (string.IsNullOrWhiteSpace(token))
|
|
token = ctx.Request.Cookies["access_token"];
|
|
|
|
if (!string.IsNullOrWhiteSpace(token))
|
|
req.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
|
|
}
|
|
|
|
|
|
|
|
|
|
// new otp verfy for client
|
|
public async Task<AuthResponse> VerifyOTP(object payload, HttpContext httpContext)
|
|
{
|
|
|
|
var data = DeserializePayload.Deserialize(payload);
|
|
|
|
|
|
|
|
var targetUrl = AuthRecoveryTargetUrl;
|
|
var request = new
|
|
{
|
|
functionName = "verifyOTP",
|
|
payload,
|
|
reference = "verify-otp"
|
|
};
|
|
|
|
var json = JsonSerializer.Serialize(request);
|
|
|
|
var forwardRequest = new HttpRequestMessage(HttpMethod.Post, targetUrl)
|
|
{
|
|
Content = new StringContent(json, Encoding.UTF8, "application/json")
|
|
};
|
|
|
|
try
|
|
{
|
|
var response = await _httpClient.SendAsync(forwardRequest);
|
|
var responseContent = await response.Content.ReadAsStringAsync();
|
|
|
|
var authResponse = JsonSerializer.Deserialize<AuthResponse>(responseContent, new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
|
|
|
|
if (authResponse == null)
|
|
throw new Exception("Empty response from auth service");
|
|
|
|
return authResponse;
|
|
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
return new AuthResponse { StatusCode = 500, Success = false, Message = $"Error communicating with auth service: {ex.Message}", Data = null };
|
|
}
|
|
}
|
|
|
|
//Logout user by clearing cookie
|
|
public async Task<AuthResponse> LogOut(object payload, HttpContext httpContext)
|
|
{
|
|
var targetUrl = AuthUserTargetUrl;
|
|
var request = new
|
|
{
|
|
functionName = "LogoutUser",
|
|
payload,
|
|
reference = "log-out"
|
|
};
|
|
|
|
var json = JsonSerializer.Serialize(request);
|
|
|
|
var forwardRequest = new HttpRequestMessage(HttpMethod.Post, targetUrl)
|
|
{
|
|
Content = new StringContent(json, Encoding.UTF8, "application/json")
|
|
};
|
|
|
|
// Always clear cookies locally, even if the remote revoke fails — the user
|
|
// clicked logout and their browser session must end.
|
|
ClearAuthCookies(httpContext);
|
|
|
|
try
|
|
{
|
|
var response = await _httpClient.SendAsync(forwardRequest);
|
|
var responseContent = await response.Content.ReadAsStringAsync();
|
|
|
|
var authResponse = JsonSerializer.Deserialize<AuthResponse>(responseContent, new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
|
|
|
|
return authResponse ?? new AuthResponse { StatusCode = 200, Success = true, Message = "Logged out", Data = null };
|
|
}
|
|
catch
|
|
{
|
|
return new AuthResponse { StatusCode = 200, Success = true, Message = "Logged out", Data = null };
|
|
}
|
|
}
|
|
|
|
private static bool IsLocalHost(string? host)
|
|
{
|
|
return string.Equals(host, "localhost", StringComparison.OrdinalIgnoreCase) ||
|
|
string.Equals(host, "127.0.0.1", StringComparison.OrdinalIgnoreCase);
|
|
}
|
|
|
|
}
|
|
}
|