1
0
forked from baron/baron-sso

headless password login 접속 이력 반영

This commit is contained in:
2026-04-14 10:49:11 +09:00
parent 772e3ed5e3
commit 92f8e9a61a
3 changed files with 301 additions and 3 deletions

View File

@@ -683,3 +683,158 @@ func TestGetAuthTimeline_FillsSessionIDFromOathkeeperRaw(t *testing.T) {
assert.Equal(t, "oathkeeper", body.Items[0].Source)
}
}
func TestGetAuthTimeline_IncludesHeadlessPasswordLogin(t *testing.T) {
now := time.Date(2026, 4, 7, 5, 10, 0, 0, time.UTC)
setDefaultHTTPClientForTest(t, roundTripFunc(func(r *http.Request) (*http.Response, error) {
if r.URL.Path == "/sessions/whoami" {
return httpJSONAny(r, http.StatusOK, map[string]any{
"id": "current-sid",
"authenticated_at": now.Format(time.RFC3339),
"identity": map[string]any{
"id": "user-123",
"traits": map[string]any{
"email": "user@example.com",
"name": "User",
"role": "user",
},
},
}), nil
}
return httpResponse(r, http.StatusNotFound, "not found"), nil
}))
h := &AuthHandler{
AuditRepo: &mockAuditRepo{
logs: []domain.AuditLog{
{
EventID: "audit-1",
Timestamp: now,
UserID: "user-123",
SessionID: "headless-session-1",
EventType: "POST /api/v1/auth/headless/password/login",
Status: "success",
IPAddress: "203.0.113.20",
UserAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/146.0.0.0 Safari/537.36",
Details: `{"client_id":"headless-login-client","client_name":"Headless Login Portal","session_id":"headless-session-1","login_id":"user@example.com","login_challenge":"challenge-123"}`,
},
},
},
}
app := fiber.New()
app.Get("/api/v1/audit/auth/timeline", h.GetAuthTimeline)
req := httptest.NewRequest(http.MethodGet, "/api/v1/audit/auth/timeline", nil)
req.Header.Set("Cookie", "ory_kratos_session=valid")
resp, err := app.Test(req, -1)
assert.NoError(t, err)
assert.Equal(t, http.StatusOK, resp.StatusCode)
var body struct {
Items []struct {
SessionID string `json:"session_id"`
ClientID string `json:"client_id"`
AppName string `json:"app_name"`
AuthMethod string `json:"auth_method"`
EventType string `json:"event_type"`
} `json:"items"`
}
err = json.NewDecoder(resp.Body).Decode(&body)
assert.NoError(t, err)
if assert.Len(t, body.Items, 1) {
assert.Equal(t, "headless-session-1", body.Items[0].SessionID)
assert.Equal(t, "headless-login-client", body.Items[0].ClientID)
assert.Equal(t, "Headless Login Portal", body.Items[0].AppName)
assert.Equal(t, "비밀번호(Email)", body.Items[0].AuthMethod)
assert.Equal(t, "POST /api/v1/auth/headless/password/login", body.Items[0].EventType)
}
}
func TestListMySessions_UsesHeadlessPasswordLoginForClientBinding(t *testing.T) {
now := time.Date(2026, 4, 7, 5, 35, 0, 0, time.UTC)
setDefaultHTTPClientForTest(t, roundTripFunc(func(r *http.Request) (*http.Response, error) {
if r.URL.Path == "/sessions/whoami" {
return httpJSONAny(r, http.StatusOK, map[string]any{
"id": "current-sid",
"authenticated_at": now.Format(time.RFC3339),
"identity": map[string]any{
"id": "user-123",
"traits": map[string]any{
"email": "user@example.com",
"name": "User",
"role": "user",
},
},
}), nil
}
return httpResponse(r, http.StatusNotFound, "not found"), nil
}))
mockKratos := new(MockKratosAdminService)
mockKratos.On("ListIdentitySessions", mock.Anything, "user-123").Return([]service.KratosSession{
{
ID: "current-sid",
Active: true,
AuthenticatedAt: now,
ExpiresAt: now.Add(24 * time.Hour),
},
{
ID: "headless-session-1",
Active: true,
AuthenticatedAt: now.Add(-10 * time.Minute),
ExpiresAt: now.Add(23*time.Hour + 50*time.Minute),
},
}, nil).Once()
auditRepo := &mockAuditRepo{
logs: []domain.AuditLog{
{
UserID: "user-123",
EventType: "POST /api/v1/auth/headless/password/login",
SessionID: "headless-session-1",
Timestamp: now,
IPAddress: "203.0.113.20",
UserAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/146.0.0.0 Safari/537.36",
Details: `{"client_id":"headless-login-client","client_name":"Headless Login Portal","session_id":"headless-session-1"}`,
},
},
}
h := &AuthHandler{
KratosAdmin: mockKratos,
AuditRepo: auditRepo,
}
app := fiber.New()
app.Get("/api/v1/user/sessions", h.ListMySessions)
req := httptest.NewRequest(http.MethodGet, "/api/v1/user/sessions", nil)
req.Header.Set("Cookie", "ory_kratos_session=valid")
resp, err := app.Test(req, -1)
assert.NoError(t, err)
assert.Equal(t, http.StatusOK, resp.StatusCode)
var body struct {
Items []struct {
SessionID string `json:"session_id"`
AppName string `json:"app_name"`
ClientID string `json:"client_id"`
IPAddress string `json:"ip_address"`
UserAgent string `json:"user_agent"`
} `json:"items"`
}
err = json.NewDecoder(resp.Body).Decode(&body)
assert.NoError(t, err)
if assert.Len(t, body.Items, 2) {
assert.Equal(t, "headless-session-1", body.Items[1].SessionID)
assert.Equal(t, "Headless Login Portal", body.Items[1].AppName)
assert.Equal(t, "headless-login-client", body.Items[1].ClientID)
assert.Equal(t, "203.0.113.20", body.Items[1].IPAddress)
assert.Equal(t, "Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/146.0.0.0 Safari/537.36", body.Items[1].UserAgent)
}
mockKratos.AssertExpectations(t)
}