forked from baron/baron-sso
headless link login 애플리케이션 표시
This commit is contained in:
@@ -109,6 +109,7 @@ type signupState struct {
|
|||||||
|
|
||||||
type headlessLinkState struct {
|
type headlessLinkState struct {
|
||||||
ClientID string `json:"clientId"`
|
ClientID string `json:"clientId"`
|
||||||
|
ClientName string `json:"clientName,omitempty"`
|
||||||
LoginChallenge string `json:"loginChallenge"`
|
LoginChallenge string `json:"loginChallenge"`
|
||||||
LoginID string `json:"loginId"`
|
LoginID string `json:"loginId"`
|
||||||
RedirectTo string `json:"redirectTo,omitempty"`
|
RedirectTo string `json:"redirectTo,omitempty"`
|
||||||
@@ -2675,6 +2676,7 @@ func (h *AuthHandler) HeadlessLinkInit(c *fiber.Ctx) error {
|
|||||||
}
|
}
|
||||||
h.storeHeadlessLinkState(pendingRef, headlessLinkState{
|
h.storeHeadlessLinkState(pendingRef, headlessLinkState{
|
||||||
ClientID: clientID,
|
ClientID: clientID,
|
||||||
|
ClientName: strings.TrimSpace(loginReq.Client.ClientName),
|
||||||
LoginChallenge: loginChallenge,
|
LoginChallenge: loginChallenge,
|
||||||
LoginID: resolvedLoginID,
|
LoginID: resolvedLoginID,
|
||||||
}, ttl)
|
}, ttl)
|
||||||
@@ -4119,6 +4121,21 @@ func (h *AuthHandler) writeLinkAuditLog(loginID, pendingRef string, sessionToken
|
|||||||
if rawLoginID != "" && rawLoginID != loginID {
|
if rawLoginID != "" && rawLoginID != loginID {
|
||||||
details["login_id_effective"] = loginID
|
details["login_id_effective"] = loginID
|
||||||
}
|
}
|
||||||
|
if state, ok := h.loadHeadlessLinkState(pendingRef); ok {
|
||||||
|
if strings.TrimSpace(state.ClientID) != "" {
|
||||||
|
details["client_id"] = strings.TrimSpace(state.ClientID)
|
||||||
|
}
|
||||||
|
clientName := strings.TrimSpace(state.ClientName)
|
||||||
|
if clientName == "" && strings.TrimSpace(state.ClientID) != "" {
|
||||||
|
clientName = strings.TrimSpace(state.ClientID)
|
||||||
|
}
|
||||||
|
if clientName != "" {
|
||||||
|
details["client_name"] = clientName
|
||||||
|
}
|
||||||
|
if strings.TrimSpace(state.LoginChallenge) != "" {
|
||||||
|
details["login_challenge"] = strings.TrimSpace(state.LoginChallenge)
|
||||||
|
}
|
||||||
|
}
|
||||||
if approverMeta, ok := h.loadLoginApproverMeta(pendingRef); ok {
|
if approverMeta, ok := h.loadLoginApproverMeta(pendingRef); ok {
|
||||||
if approverMeta.IPAddress != "" {
|
if approverMeta.IPAddress != "" {
|
||||||
details["approved_ip"] = approverMeta.IPAddress
|
details["approved_ip"] = approverMeta.IPAddress
|
||||||
|
|||||||
@@ -243,11 +243,6 @@ func TestHeadlessLinkPoll_AfterApprovalReturnsRedirect(t *testing.T) {
|
|||||||
redis := &mockRedisRepo{data: make(map[string]string)}
|
redis := &mockRedisRepo{data: make(map[string]string)}
|
||||||
privateKey, jwks := mustHeadlessRSAJWK(t)
|
privateKey, jwks := mustHeadlessRSAJWK(t)
|
||||||
jwksBody, _ := json.Marshal(jwks)
|
jwksBody, _ := json.Marshal(jwks)
|
||||||
jwksServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
|
||||||
_, _ = w.Write(jwksBody)
|
|
||||||
}))
|
|
||||||
defer jwksServer.Close()
|
|
||||||
|
|
||||||
idp := &mockIdpProvider{
|
idp := &mockIdpProvider{
|
||||||
userExists: true,
|
userExists: true,
|
||||||
@@ -261,12 +256,13 @@ func TestHeadlessLinkPoll_AfterApprovalReturnsRedirect(t *testing.T) {
|
|||||||
Challenge: "challenge-123",
|
Challenge: "challenge-123",
|
||||||
Client: domain.HydraClient{
|
Client: domain.HydraClient{
|
||||||
ClientID: "headless-login-client",
|
ClientID: "headless-login-client",
|
||||||
|
ClientName: "local-demo-rp",
|
||||||
TokenEndpointAuthMethod: "none",
|
TokenEndpointAuthMethod: "none",
|
||||||
Metadata: map[string]interface{}{
|
Metadata: map[string]interface{}{
|
||||||
"status": "active",
|
"status": "active",
|
||||||
"headless_login_enabled": true,
|
"headless_login_enabled": true,
|
||||||
"headless_token_endpoint_auth_method": "private_key_jwt",
|
"headless_token_endpoint_auth_method": "private_key_jwt",
|
||||||
"headless_jwks_uri": jwksServer.URL + "/.well-known/jwks.json",
|
"headless_jwks_uri": "https://rp.example.com/.well-known/jwks.json",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@@ -280,12 +276,21 @@ func TestHeadlessLinkPoll_AfterApprovalReturnsRedirect(t *testing.T) {
|
|||||||
|
|
||||||
mockKratos := new(MockKratosAdminService)
|
mockKratos := new(MockKratosAdminService)
|
||||||
mockKratos.On("FindIdentityIDByIdentifier", mock.Anything, "+821012345678").Return("kratos-identity-id", nil)
|
mockKratos.On("FindIdentityIDByIdentifier", mock.Anything, "+821012345678").Return("kratos-identity-id", nil)
|
||||||
|
auditRepo := &mockAuditRepo{}
|
||||||
|
headlessClient := &http.Client{Transport: roundTripFunc(func(r *http.Request) (*http.Response, error) {
|
||||||
|
if r.URL.Host == "rp.example.com" && r.URL.Path == "/.well-known/jwks.json" {
|
||||||
|
return httpResponse(r, http.StatusOK, string(jwksBody)), nil
|
||||||
|
}
|
||||||
|
return httpResponse(r, http.StatusNotFound, "not found"), nil
|
||||||
|
})}
|
||||||
|
|
||||||
h := &AuthHandler{
|
h := &AuthHandler{
|
||||||
RedisService: redis,
|
RedisService: redis,
|
||||||
IdpProvider: idp,
|
IdpProvider: idp,
|
||||||
SmsService: &mockSmsService{},
|
SmsService: &mockSmsService{},
|
||||||
KratosAdmin: mockKratos,
|
KratosAdmin: mockKratos,
|
||||||
|
AuditRepo: auditRepo,
|
||||||
|
HeadlessJWKS: service.NewHeadlessJWKSCacheService(nil, headlessClient),
|
||||||
Hydra: &service.HydraAdminService{
|
Hydra: &service.HydraAdminService{
|
||||||
AdminURL: "http://hydra.test",
|
AdminURL: "http://hydra.test",
|
||||||
HTTPClient: &http.Client{Transport: mockHydraTransport(hydraHandler)},
|
HTTPClient: &http.Client{Transport: mockHydraTransport(hydraHandler)},
|
||||||
@@ -343,4 +348,14 @@ func TestHeadlessLinkPoll_AfterApprovalReturnsRedirect(t *testing.T) {
|
|||||||
_ = json.NewDecoder(resp.Body).Decode(&pollResp)
|
_ = json.NewDecoder(resp.Body).Decode(&pollResp)
|
||||||
assert.Equal(t, "http://rp/cb", pollResp["redirectTo"])
|
assert.Equal(t, "http://rp/cb", pollResp["redirectTo"])
|
||||||
assert.Equal(t, "ok", pollResp["status"])
|
assert.Equal(t, "ok", pollResp["status"])
|
||||||
|
if assert.Len(t, auditRepo.logs, 1) {
|
||||||
|
assert.Contains(t, auditRepo.logs[0].EventType, "/api/v1/auth/")
|
||||||
|
details, err := parseAuditDetails(auditRepo.logs[0].Details)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to parse audit details: %v", err)
|
||||||
|
}
|
||||||
|
assert.Equal(t, "headless-login-client", details["client_id"])
|
||||||
|
assert.Equal(t, "local-demo-rp", details["client_name"])
|
||||||
|
assert.Equal(t, "challenge-123", details["login_challenge"])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user