forked from baron/baron-sso
RP 로그인 이력 통합
This commit is contained in:
@@ -2976,6 +2976,11 @@ type consentClientInfo struct {
|
||||
ConsentAt time.Time
|
||||
}
|
||||
|
||||
type loginClientInfo struct {
|
||||
ClientID string
|
||||
Name string
|
||||
}
|
||||
|
||||
func (h *AuthHandler) GetAuthTimeline(c *fiber.Ctx) error {
|
||||
if h.AuditRepo == nil && h.OathkeeperRepo == nil {
|
||||
return c.Status(fiber.StatusServiceUnavailable).JSON(fiber.Map{"error": "Audit service unavailable"})
|
||||
@@ -3123,7 +3128,8 @@ func (h *AuthHandler) GetAuthTimeline(c *fiber.Ctx) error {
|
||||
for batch := 0; batch < maxBatches && len(oathkeeperLogs) < fetchLimit; batch++ {
|
||||
logs, err := h.OathkeeperRepo.FindPageBySubject(c.Context(), subject, fetchLimit, currentCursor)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Failed to retrieve oathkeeper logs"})
|
||||
slog.Warn("Failed to retrieve oathkeeper logs", "error", err)
|
||||
break
|
||||
}
|
||||
if len(logs) == 0 {
|
||||
break
|
||||
@@ -3159,9 +3165,53 @@ func (h *AuthHandler) GetAuthTimeline(c *fiber.Ctx) error {
|
||||
}
|
||||
}
|
||||
|
||||
loginChallengeCache := make(map[string]loginClientInfo)
|
||||
resolveLoginClient := func(challenge string) (loginClientInfo, bool) {
|
||||
challenge = strings.TrimSpace(challenge)
|
||||
if challenge == "" || h.Hydra == nil {
|
||||
return loginClientInfo{}, false
|
||||
}
|
||||
if cached, ok := loginChallengeCache[challenge]; ok {
|
||||
return cached, cached.ClientID != ""
|
||||
}
|
||||
loginReq, err := h.Hydra.GetLoginRequest(c.Context(), challenge)
|
||||
if err != nil || loginReq == nil {
|
||||
loginChallengeCache[challenge] = loginClientInfo{}
|
||||
return loginClientInfo{}, false
|
||||
}
|
||||
clientID := strings.TrimSpace(loginReq.Client.ClientID)
|
||||
if clientID == "" {
|
||||
loginChallengeCache[challenge] = loginClientInfo{}
|
||||
return loginClientInfo{}, false
|
||||
}
|
||||
name := strings.TrimSpace(loginReq.Client.ClientName)
|
||||
if name == "" {
|
||||
name = clientID
|
||||
}
|
||||
info := loginClientInfo{
|
||||
ClientID: clientID,
|
||||
Name: name,
|
||||
}
|
||||
loginChallengeCache[challenge] = info
|
||||
return info, true
|
||||
}
|
||||
|
||||
items := make([]authTimelineItem, 0, len(authLogs)+len(oathkeeperLogs))
|
||||
for i := range authLogs {
|
||||
log := authLogs[i]
|
||||
appName := "Baron 통합로그인"
|
||||
clientID := ""
|
||||
path := strings.ToLower(extractAuditPath(log))
|
||||
if strings.Contains(path, "/api/v1/auth/oidc/login/accept") {
|
||||
appName = "OIDC 로그인"
|
||||
loginChallenge := extractLoginChallengeFromAuditDetails(log.Details)
|
||||
if loginChallenge != "" {
|
||||
if info, ok := resolveLoginClient(loginChallenge); ok {
|
||||
appName = info.Name
|
||||
clientID = info.ClientID
|
||||
}
|
||||
}
|
||||
}
|
||||
item := authTimelineItem{
|
||||
EventID: log.EventID,
|
||||
Timestamp: log.Timestamp,
|
||||
@@ -3174,7 +3224,8 @@ func (h *AuthHandler) GetAuthTimeline(c *fiber.Ctx) error {
|
||||
UserAgent: log.UserAgent,
|
||||
Details: log.Details,
|
||||
Source: "backend",
|
||||
AppName: "Baron 통합로그인",
|
||||
AppName: appName,
|
||||
ClientID: clientID,
|
||||
}
|
||||
items = append(items, item)
|
||||
}
|
||||
@@ -3432,6 +3483,10 @@ func (h *AuthHandler) AcceptConsentRequest(c *fiber.Ctx) error {
|
||||
if err != nil || identity == nil {
|
||||
return fiber.NewError(fiber.StatusInternalServerError, "Failed to load identity")
|
||||
}
|
||||
c.Locals("user_id", consentRequest.Subject)
|
||||
if loginID := pickLoginIDFromTraits(identity.Traits); loginID != "" {
|
||||
c.Locals("login_id", loginID)
|
||||
}
|
||||
sessionClaims := buildOidcClaimsFromTraits(identity.Traits, consentRequest.RequestedScope)
|
||||
|
||||
acceptResp, err := h.Hydra.AcceptConsentRequest(c.Context(), req.ConsentChallenge, consentRequest, sessionClaims)
|
||||
@@ -3458,6 +3513,21 @@ func (h *AuthHandler) AcceptOidcLoginRequest(c *fiber.Ctx) error {
|
||||
if err != nil || subject == "" {
|
||||
return fiber.NewError(fiber.StatusUnauthorized, "Authentication required")
|
||||
}
|
||||
c.Locals("user_id", subject)
|
||||
if sessionID, ok := c.Locals("session_id").(string); ok && sessionID != "" {
|
||||
c.Locals("approved_session_id", sessionID)
|
||||
} else if token := h.getBearerToken(c); token != "" {
|
||||
if derivedID := extractSessionIDFromJWT(token); derivedID != "" {
|
||||
c.Locals("approved_session_id", derivedID)
|
||||
}
|
||||
}
|
||||
if h.KratosAdmin != nil {
|
||||
if identity, err := h.KratosAdmin.GetIdentity(c.Context(), subject); err == nil && identity != nil {
|
||||
if loginID := pickLoginIDFromTraits(identity.Traits); loginID != "" {
|
||||
c.Locals("login_id", loginID)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
acceptResp, err := h.Hydra.AcceptLoginRequest(c.Context(), req.LoginChallenge, subject)
|
||||
if err != nil {
|
||||
@@ -3715,6 +3785,9 @@ func shouldSkipAuthTimeline(log domain.AuditLog) bool {
|
||||
if path != "" && strings.Contains(path, "/api/v1/auth/enchanted-link/init") {
|
||||
return true
|
||||
}
|
||||
if path != "" && strings.Contains(path, "/api/v1/auth/consent/accept") {
|
||||
return true
|
||||
}
|
||||
if path != "" && (strings.Contains(path, "/api/v1/auth/magic-link/verify") ||
|
||||
strings.Contains(path, "/api/v1/auth/login/code/verify")) {
|
||||
sessionID := log.SessionID
|
||||
@@ -3922,6 +3995,8 @@ func deriveAuthMethod(log domain.AuditLog) string {
|
||||
return "QR"
|
||||
case strings.Contains(path, "/api/v1/auth/qr/poll"):
|
||||
return "QR"
|
||||
case strings.Contains(path, "/api/v1/auth/oidc/login/accept"):
|
||||
return "OIDC 로그인"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
@@ -4001,7 +4076,37 @@ func extractLoginIDFromAuditDetails(details string) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func extractLoginChallengeFromAuditDetails(details string) string {
|
||||
if details == "" {
|
||||
return ""
|
||||
}
|
||||
payload, err := parseAuditDetails(details)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
if raw, ok := payload["login_challenge"].(string); ok && raw != "" {
|
||||
return raw
|
||||
}
|
||||
if raw, ok := payload["loginChallenge"].(string); ok && raw != "" {
|
||||
return raw
|
||||
}
|
||||
body := extractRequestBody(payload)
|
||||
if body == nil {
|
||||
return ""
|
||||
}
|
||||
if raw, ok := body["login_challenge"].(string); ok && raw != "" {
|
||||
return raw
|
||||
}
|
||||
if raw, ok := body["loginChallenge"].(string); ok && raw != "" {
|
||||
return raw
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func extractClientIDFromOathkeeperLog(log domain.OathkeeperAccessLog) string {
|
||||
if value := strings.TrimSpace(log.ClientID); value != "" {
|
||||
return value
|
||||
}
|
||||
if value := strings.TrimSpace(log.RP); value != "" {
|
||||
return value
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user