Просмотр исходного кода

ghi log thêm đk check web trình duyệt và app

student 1 месяц назад
Родитель
Сommit
1940f3efad

+ 2 - 2
Kitty_Fall/Kitty_Fall/Kitty_Fall.Website/Controllers/LoginController.cs

@@ -90,7 +90,7 @@ namespace Kitty_Fall.Website.Controllers
             {
                 errorCode = result.ErrorCode,
                 message = result.Message,
-                redirectUrl = result.ErrorCode == CommonErrorCode.SUCCESS ? Url.Action("Index", "Home") ?? "/" : null
+                redirectUrl = result.ErrorCode == CommonErrorCode.SUCCESS ? NormalizeReturnUrl(returnUrl) : null
             });
         }
 
@@ -242,7 +242,7 @@ namespace Kitty_Fall.Website.Controllers
         {
             LoginSessionHelper.ClearLoginSession(HttpContext);
             ClearPendingOtp();
-            return Json(new { errorCode = CommonErrorCode.SUCCESS, redirectUrl = "mytel://back" });
+            return Json(new { errorCode = CommonErrorCode.SUCCESS, redirectUrl = AccessChannelHelper.GetLoginRedirectUrl(HttpContext) });
         }
 
         private void ClearPendingOtp()

+ 55 - 0
Kitty_Fall/Kitty_Fall/Kitty_Fall.Website/Helpers/AccessChannelHelper.cs

@@ -0,0 +1,55 @@
+namespace Kitty_Fall.Website.Helpers;
+
+/// <summary>
+/// Phân biệt truy cập từ app MyID (webview, có header phone-number) với trình duyệt thường.
+/// App chỉ chắc chắn gắn header ở lần mở đầu, nên khi thấy header sẽ ghi cookie lâu dài
+/// để các request sau (kể cả khi session đã hết hạn) vẫn nhận ra là app.
+/// </summary>
+public static class AccessChannelHelper
+{
+    public const string MyIdBackUrl = "mytel://back";
+    private const string ChannelCookieName = "kf_channel";
+    private const string MyIdChannel = "myid";
+
+    private static readonly string[] MyIdPhoneHeaders =
+        { "phone-number", "MSISDN", "msisdn", "phone_number", "phoneNumber" };
+
+    public static bool HasMyIdHeader(HttpRequest request)
+    {
+        return MyIdPhoneHeaders.Any(name => !string.IsNullOrWhiteSpace(request.Headers[name].ToString()));
+    }
+
+    public static bool IsMyIdApp(HttpContext context)
+    {
+        return HasMyIdHeader(context.Request)
+            || string.Equals(context.Request.Cookies[ChannelCookieName], MyIdChannel, StringComparison.Ordinal);
+    }
+
+    public static void MarkMyIdApp(HttpContext context)
+    {
+        if (string.Equals(context.Request.Cookies[ChannelCookieName], MyIdChannel, StringComparison.Ordinal)) return;
+        if (context.Response.HasStarted) return;
+
+        context.Response.Cookies.Append(ChannelCookieName, MyIdChannel, new CookieOptions
+        {
+            HttpOnly = true,
+            IsEssential = true,
+            Secure = context.Request.IsHttps,
+            SameSite = SameSiteMode.Lax,
+            Expires = DateTimeOffset.UtcNow.AddDays(30)
+        });
+    }
+
+    /// <summary>
+    /// URL chuyển hướng khi chưa đăng nhập: app → về MyID, trình duyệt → trang OTP /Login.
+    /// </summary>
+    public static string GetLoginRedirectUrl(HttpContext context, string? returnUrl = null)
+    {
+        if (IsMyIdApp(context)) return MyIdBackUrl;
+
+        var loginUrl = $"{context.Request.PathBase}/Login";
+        return string.IsNullOrWhiteSpace(returnUrl)
+            ? loginUrl
+            : loginUrl + "?returnUrl=" + Uri.EscapeDataString(returnUrl);
+    }
+}

+ 21 - 2
Kitty_Fall/Kitty_Fall/Kitty_Fall.Website/Helpers/LoginRequiredMiddleware.cs

@@ -29,18 +29,24 @@ public class LoginRequiredMiddleware
         context.Response.Headers.CacheControl = "no-store";
         if (IsAjaxRequest(context.Request))
         {
+            // Ajax: quay lại trang đang đứng (Referer) sau khi đăng nhập trên trình duyệt.
+            var redirectUrl = AccessChannelHelper.GetLoginRedirectUrl(context, GetLocalReferer(context.Request));
             context.Response.StatusCode = StatusCodes.Status401Unauthorized;
             context.Response.Headers["X-Login-Required"] = "true";
+            context.Response.Headers["X-Login-Redirect"] = redirectUrl;
             await context.Response.WriteAsJsonAsync(new
             {
                 success = false,
                 errorCode = Common.CommonErrorCode.LoginRequired,
-                redirectUrl = "mytel://back"
+                redirectUrl
             });
             return;
         }
 
-        context.Response.Redirect("mytel://back");
+        var returnUrl = context.Request.Method == HttpMethods.Get
+            ? $"{context.Request.PathBase}{context.Request.Path}{context.Request.QueryString}"
+            : null;
+        context.Response.Redirect(AccessChannelHelper.GetLoginRedirectUrl(context, returnUrl));
     }
 
     private static bool ShouldSkip(HttpContext context)
@@ -59,6 +65,19 @@ public class LoginRequiredMiddleware
         return false;
     }
 
+    private static string? GetLocalReferer(HttpRequest request)
+    {
+        var referer = request.Headers.Referer.ToString();
+        if (string.IsNullOrWhiteSpace(referer)
+            || !Uri.TryCreate(referer, UriKind.Absolute, out var uri)
+            || !string.Equals(uri.Host, request.Host.Host, StringComparison.OrdinalIgnoreCase))
+        {
+            return null;
+        }
+
+        return uri.PathAndQuery;
+    }
+
     private static bool IsAjaxRequest(HttpRequest request)
     {
         return string.Equals(request.Headers["X-Requested-With"], "XMLHttpRequest", StringComparison.OrdinalIgnoreCase)

+ 5 - 0
Kitty_Fall/Kitty_Fall/Kitty_Fall.Website/Helpers/MyIdAutoLoginMiddleware.cs

@@ -23,6 +23,11 @@ public class MyIdAutoLoginMiddleware
     {
         try
         {
+            if (AccessChannelHelper.HasMyIdHeader(context.Request))
+            {
+                AccessChannelHelper.MarkMyIdApp(context);
+            }
+
             var sessionMsisdn = context.Session.GetComplexData<string>("msisdn");
             var requestMsisdn = ReadValidatedHeaderMsisdn(context);
             var hasApiToken = !string.IsNullOrWhiteSpace(context.Session.GetString("apiToken"));

+ 7 - 3
Kitty_Fall/Kitty_Fall/Kitty_Fall.Website/Views/Home/Index.cshtml

@@ -19,6 +19,7 @@
     // Khong chan modal theo regInfos vi session subscription co the chua kip dong bo.
     var autoOpenBuyTurn = (ViewBag.AutoOpenBuyTurn as bool?) == true;
     var turnPackages = ViewBag.TurnPackages as List<Dictionary<string, object?>> ?? new List<Dictionary<string, object?>>();
+    var isMyIdApp = Kitty_Fall.Website.Helpers.AccessChannelHelper.IsMyIdApp(Context);
 }
 
 <main class="kitty-app" aria-label="Kitty Fall home screen">
@@ -27,9 +28,12 @@
 
     <header class="topbar">
         <div class="profile">
-            <button class="home-back-button" type="button" aria-label="Back" onclick="backToMyId()">
-                <span></span>
-            </button>
+            @if (isMyIdApp)
+            {
+                <button class="home-back-button" type="button" aria-label="Back" onclick="backToMyId()">
+                    <span></span>
+                </button>
+            }
             <img class="profile-avatar" src="@avatarUrl" alt="Player avatar">
             <div class="profile-copy">
                 <strong>@Lang.Hello</strong>

+ 2 - 1
Kitty_Fall/Kitty_Fall/Kitty_Fall.Website/Views/Login/Index.cshtml

@@ -194,7 +194,8 @@
                 data: {
                     msisdn: msisdn,
                     transactionId: transactionId,
-                    otp: otp
+                    otp: otp,
+                    returnUrl: @Json.Serialize((string?)ViewBag.ReturnUrl ?? "")
                 },
                 success: function(res) {
                     if (typeof res === "string") {

+ 5 - 4
Kitty_Fall/Kitty_Fall/Kitty_Fall.Website/wwwroot/js/session-redirect.js

@@ -1,10 +1,11 @@
 (function () {
     let returningToMyId = false;
 
-    function handleExpiredSession(status, loginRequired) {
+    function handleExpiredSession(status, loginRequired, loginRedirect) {
         if (status === 401 && loginRequired === "true" && !returningToMyId) {
             returningToMyId = true;
-            window.location.href = "mytel://back";
+            // Server trả mytel://back khi vào từ app MyID, /Login khi vào từ trình duyệt.
+            window.location.href = loginRedirect || "mytel://back";
         }
     }
 
@@ -12,7 +13,7 @@
     const originalSend = XMLHttpRequest.prototype.send;
     XMLHttpRequest.prototype.send = function () {
         this.addEventListener("load", function () {
-            handleExpiredSession(this.status, this.getResponseHeader("X-Login-Required"));
+            handleExpiredSession(this.status, this.getResponseHeader("X-Login-Required"), this.getResponseHeader("X-Login-Redirect"));
         }, { once: true });
         return originalSend.apply(this, arguments);
     };
@@ -21,7 +22,7 @@
     if (originalFetch) {
         window.fetch = async function () {
             const response = await originalFetch.apply(this, arguments);
-            handleExpiredSession(response.status, response.headers.get("X-Login-Required"));
+            handleExpiredSession(response.status, response.headers.get("X-Login-Required"), response.headers.get("X-Login-Redirect"));
             return response;
         };
     }