From 8f9b40c303474af29ca248a6596bf8be61e11bcf Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Tue, 24 Jun 2025 12:49:24 +0700 Subject: [PATCH] =?UTF-8?q?=E0=B8=9B=E0=B8=A3=E0=B8=B1=E0=B8=9A=E0=B8=81?= =?UTF-8?q?=E0=B8=B2=E0=B8=A3=E0=B9=81=E0=B8=81=E0=B8=B0=20keycloakid=20?= =?UTF-8?q?=E0=B8=A1=E0=B8=B2=E0=B8=88=E0=B8=B2=E0=B8=81=20token=20?= =?UTF-8?q?=E0=B9=80=E0=B8=99=E0=B8=B7=E0=B9=88=E0=B8=AD=E0=B8=87=E0=B8=88?= =?UTF-8?q?=E0=B8=B2=E0=B8=81=20middleware=20=E0=B8=97=E0=B8=B3=E0=B8=87?= =?UTF-8?q?=E0=B8=B2=E0=B8=99=E0=B8=81=E0=B9=88=E0=B8=AD=E0=B8=99=20auth?= =?UTF-8?q?=20middleware=20=E0=B8=88=E0=B8=B6=E0=B8=87=E0=B8=97=E0=B8=B3?= =?UTF-8?q?=E0=B9=83=E0=B8=AB=E0=B9=89=E0=B8=A2=E0=B8=B1=E0=B8=87=E0=B9=84?= =?UTF-8?q?=E0=B8=A1=E0=B9=88=E0=B8=A1=E0=B8=B5=20context.User=20=E0=B9=80?= =?UTF-8?q?=E0=B8=A5=E0=B8=A2=E0=B9=80=E0=B8=AD=E0=B8=B2=E0=B9=84=E0=B8=9B?= =?UTF-8?q?=E0=B8=AB=E0=B8=B2=20keycloakId=20=E0=B9=84=E0=B8=A1=E0=B9=84?= =?UTF-8?q?=E0=B8=94=E0=B9=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...ombinedErrorHandlerAndLoggingMiddleware.cs | 116 ++++++++++++++++-- 1 file changed, 106 insertions(+), 10 deletions(-) diff --git a/BMA.EHR.Domain/Middlewares/CombinedErrorHandlerAndLoggingMiddleware.cs b/BMA.EHR.Domain/Middlewares/CombinedErrorHandlerAndLoggingMiddleware.cs index e806531f..cd27ee75 100644 --- a/BMA.EHR.Domain/Middlewares/CombinedErrorHandlerAndLoggingMiddleware.cs +++ b/BMA.EHR.Domain/Middlewares/CombinedErrorHandlerAndLoggingMiddleware.cs @@ -22,7 +22,6 @@ namespace BMA.EHR.Domain.Middlewares private string Uri = ""; private string IndexFormat = ""; private string SystemName = ""; - private string APIKey = ""; public CombinedErrorHandlerAndLoggingMiddleware(RequestDelegate next, IConfiguration configuration) { @@ -50,7 +49,7 @@ namespace BMA.EHR.Domain.Middlewares // อ่าน Request Body string requestBody = await ReadRequestBodyAsync(context); - if (requestBody != "") + if (!string.IsNullOrEmpty(requestBody)) { requestBodyJson = await FormatRequestBody(context, requestBody); } @@ -62,13 +61,26 @@ namespace BMA.EHR.Domain.Middlewares // เปลี่ยน stream ของ Response เพื่อให้สามารถอ่านได้ context.Response.Body = memoryStream; - var keycloakId = context.User?.FindFirst(ClaimTypes.NameIdentifier)?.Value ?? Guid.Empty.ToString("D"); + string keycloakId = Guid.Empty.ToString("D"); var token = context.Request.Headers["Authorization"]; GetProfileByKeycloakIdLocal? pf = null; + // ลองดึง keycloakId จาก JWT token ก่อน (ถ้ามี) try { - pf = await GetProfileByKeycloakIdAsync(Guid.Parse(keycloakId), token); + keycloakId = await ExtractKeycloakIdFromToken(token); + } + catch (Exception ex) + { + Console.WriteLine($"Error extracting keycloakId from token: {ex.Message}"); + } + + try + { + if (Guid.TryParse(keycloakId, out var parsedId) && parsedId != Guid.Empty) + { + pf = await GetProfileByKeycloakIdAsync(parsedId, token); + } } catch (Exception ex) { @@ -80,8 +92,32 @@ namespace BMA.EHR.Domain.Middlewares await _next(context); Console.WriteLine($"Request completed with status: {context.Response.StatusCode}"); + // หลังจาก Authentication middleware ทำงานแล้ว ลองดึง claims อีกครั้ง + if (context.User?.Identity?.IsAuthenticated == true) + { + var authenticatedKeycloakId = context.User.FindFirst(ClaimTypes.NameIdentifier)?.Value + ?? context.User.FindFirst("sub")?.Value; + if (!string.IsNullOrEmpty(authenticatedKeycloakId) && authenticatedKeycloakId != keycloakId) + { + keycloakId = authenticatedKeycloakId; + Console.WriteLine($"Updated keycloakId from authenticated user: {keycloakId}"); + + // อัพเดต profile ด้วย keycloakId ที่ถูกต้อง + try + { + if (Guid.TryParse(keycloakId, out var parsedId)) + { + pf = await GetProfileByKeycloakIdAsync(parsedId, token); + } + } + catch (Exception ex) + { + Console.WriteLine($"Error updating profile after authentication: {ex.Message}"); + } + } + } + // จัดการ response format หลังจาก request ผ่าน pipeline แล้ว - // รวมถึงการจัดการ 401/403 ที่มาจาก Authentication middleware await FormatResponse(context, memoryStream); } catch (ObjectDisposedException) @@ -324,10 +360,10 @@ namespace BMA.EHR.Domain.Middlewares memoryStream.Seek(0, SeekOrigin.Begin); var responseBody = new StreamReader(memoryStream).ReadToEnd(); - if (responseBody != "") + if (!string.IsNullOrEmpty(responseBody)) { var contentType = context.Response.ContentType; - var isFileResponse = !contentType.StartsWith("application/json") && ( + var isFileResponse = !contentType.StartsWith("application/json") && !contentType.StartsWith("text/html") && ( contentType.StartsWith("application/") || contentType.StartsWith("image/") || contentType.StartsWith("audio/") || @@ -401,7 +437,7 @@ namespace BMA.EHR.Domain.Middlewares exception = caughtException?.ToString() }; - client.IndexDocument(logData); + await client.IndexDocumentAsync(logData); } catch (Exception ex) { @@ -453,6 +489,67 @@ namespace BMA.EHR.Domain.Middlewares return msg; } + private async Task ExtractKeycloakIdFromToken(string? authorizationHeader) + { + try + { + if (string.IsNullOrEmpty(authorizationHeader) || !authorizationHeader.StartsWith("Bearer ")) + { + return Guid.Empty.ToString("D"); + } + + var token = authorizationHeader.Replace("Bearer ", ""); + + // แยก JWT token เพื่อดึง payload (แบบง่าย โดยไม่ verify signature) + var parts = token.Split('.'); + if (parts.Length != 3) + { + return Guid.Empty.ToString("D"); + } + + // Decode Base64 payload + var payload = parts[1]; + + // เพิ่ม padding ถ้าจำเป็น + var padLength = 4 - (payload.Length % 4); + if (padLength != 4) + { + payload += new string('=', padLength); + } + + var payloadBytes = Convert.FromBase64String(payload); + var payloadJson = System.Text.Encoding.UTF8.GetString(payloadBytes); + + Console.WriteLine($"JWT Payload: {payloadJson}"); + + // Parse JSON และดึง sub (subject) claim + var jsonDoc = JsonDocument.Parse(payloadJson); + + // ลองหา keycloak ID ใน claims ต่างๆ + string? keycloakId = null; + + if (jsonDoc.RootElement.TryGetProperty("sub", out var subElement)) + { + keycloakId = subElement.GetString(); + } + else if (jsonDoc.RootElement.TryGetProperty("nameid", out var nameidElement)) + { + keycloakId = nameidElement.GetString(); + } + else if (jsonDoc.RootElement.TryGetProperty("user_id", out var userIdElement)) + { + keycloakId = userIdElement.GetString(); + } + + return keycloakId ?? Guid.Empty.ToString("D"); + } + catch (Exception ex) + { + Console.WriteLine($"Error extracting keycloak ID from token: {ex.Message}"); + return Guid.Empty.ToString("D"); + } + } + protected async Task GetExternalAPIAsync(string apiPath, string accessToken, string apiKey) { try @@ -509,7 +606,7 @@ namespace BMA.EHR.Domain.Middlewares } } - // Model classes (ถ้ายังไม่มีใน namespace นี้) + // Model classes public class GetProfileByKeycloakIdLocal { public Guid Id { get; set; } @@ -546,5 +643,4 @@ namespace BMA.EHR.Domain.Middlewares public GetProfileByKeycloakIdLocal? Result { get; set; } } - } \ No newline at end of file