Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add failure reason to events #4203

Merged
merged 4 commits into from
Nov 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions internal/client-go/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e h1:bRhVy7zSSasaqNksaRZiA5EEI+Ei4I1nO5Jh72wfHlg=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
Expand Down
6 changes: 4 additions & 2 deletions selfservice/flow/login/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ package login
import (
"net/http"

"github.com/gofrs/uuid"

"go.opentelemetry.io/otel/trace"

"github.com/ory/kratos/selfservice/sessiontokenexchange"
Expand Down Expand Up @@ -88,12 +90,12 @@ func (s *ErrorHandler) WriteFlowError(w http.ResponseWriter, r *http.Request, f
Info("Encountered self-service login error.")

if f == nil {
trace.SpanFromContext(r.Context()).AddEvent(events.NewLoginFailed(r.Context(), "", "", false))
trace.SpanFromContext(r.Context()).AddEvent(events.NewLoginFailed(r.Context(), uuid.Nil, "", "", false, err))
s.forward(w, r, nil, err)
return
}

trace.SpanFromContext(r.Context()).AddEvent(events.NewLoginFailed(r.Context(), string(f.Type), string(f.RequestedAAL), f.Refresh))
trace.SpanFromContext(r.Context()).AddEvent(events.NewLoginFailed(r.Context(), f.ID, string(f.Type), string(f.RequestedAAL), f.Refresh, err))

if expired, inner := s.PrepareReplacementForExpiredFlow(w, r, f, err); inner != nil {
s.WriteFlowError(w, r, f, group, inner)
Expand Down
2 changes: 2 additions & 0 deletions selfservice/flow/login/hook.go
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,7 @@ func (e *HookExecutor) PostLoginHook(
span.AddEvent(events.NewLoginSucceeded(ctx, &events.LoginSucceededOpts{
SessionID: s.ID,
IdentityID: i.ID,
FlowID: f.ID,
FlowType: string(f.Type),
RequestedAAL: string(f.RequestedAAL),
IsRefresh: f.Refresh,
Expand Down Expand Up @@ -262,6 +263,7 @@ func (e *HookExecutor) PostLoginHook(

span.AddEvent(events.NewLoginSucceeded(ctx, &events.LoginSucceededOpts{
SessionID: s.ID,
FlowID: f.ID,
IdentityID: i.ID, FlowType: string(f.Type), RequestedAAL: string(f.RequestedAAL), IsRefresh: f.Refresh, Method: f.Active.String(),
SSOProvider: provider,
}))
Expand Down
6 changes: 4 additions & 2 deletions selfservice/flow/recovery/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import (
"net/http"
"net/url"

"github.com/gofrs/uuid"

"go.opentelemetry.io/otel/trace"

"github.com/ory/kratos/x/events"
Expand Down Expand Up @@ -73,12 +75,12 @@ func (s *ErrorHandler) WriteFlowError(
Info("Encountered self-service recovery error.")

if f == nil {
trace.SpanFromContext(r.Context()).AddEvent(events.NewRecoveryFailed(r.Context(), "", ""))
trace.SpanFromContext(r.Context()).AddEvent(events.NewRecoveryFailed(r.Context(), uuid.Nil, "", "", recoveryErr))
s.forward(w, r, nil, recoveryErr)
return
}

trace.SpanFromContext(r.Context()).AddEvent(events.NewRecoveryFailed(r.Context(), string(f.Type), f.Active.String()))
trace.SpanFromContext(r.Context()).AddEvent(events.NewRecoveryFailed(r.Context(), f.ID, string(f.Type), f.Active.String(), recoveryErr))

if expiredError := new(flow.ExpiredError); errors.As(recoveryErr, &expiredError) {
strategy, err := s.d.RecoveryStrategies(r.Context()).Strategy(f.Active.String())
Expand Down
2 changes: 1 addition & 1 deletion selfservice/flow/recovery/hook.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ func (e *HookExecutor) PostRecoveryHook(w http.ResponseWriter, r *http.Request,
Debug("ExecutePostRecoveryHook completed successfully.")
}

trace.SpanFromContext(r.Context()).AddEvent(events.NewRecoverySucceeded(r.Context(), s.Identity.ID, string(a.Type), a.Active.String()))
trace.SpanFromContext(r.Context()).AddEvent(events.NewRecoverySucceeded(r.Context(), a.ID, s.Identity.ID, string(a.Type), a.Active.String()))

logger.Debug("Post recovery execution hooks completed successfully.")

Expand Down
6 changes: 4 additions & 2 deletions selfservice/flow/registration/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ package registration
import (
"net/http"

"github.com/gofrs/uuid"

"go.opentelemetry.io/otel/trace"

"github.com/ory/kratos/identity"
Expand Down Expand Up @@ -93,11 +95,11 @@ func (s *ErrorHandler) WriteFlowError(
Info("Encountered self-service flow error.")

if f == nil {
trace.SpanFromContext(r.Context()).AddEvent(events.NewRegistrationFailed(r.Context(), "", ""))
trace.SpanFromContext(r.Context()).AddEvent(events.NewRegistrationFailed(r.Context(), uuid.Nil, "", "", err))
s.forward(w, r, nil, err)
return
}
trace.SpanFromContext(r.Context()).AddEvent(events.NewRegistrationFailed(r.Context(), string(f.Type), f.Active.String()))
trace.SpanFromContext(r.Context()).AddEvent(events.NewRegistrationFailed(r.Context(), f.ID, string(f.Type), f.Active.String(), err))

if expired, inner := s.PrepareReplacementForExpiredFlow(w, r, f, err); inner != nil {
s.forward(w, r, f, err)
Expand Down
2 changes: 1 addition & 1 deletion selfservice/flow/registration/hook.go
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ func (e *HookExecutor) PostRegistrationHook(w http.ResponseWriter, r *http.Reque
WithField("identity_id", i.ID).
Info("A new identity has registered using self-service registration.")

span.AddEvent(events.NewRegistrationSucceeded(ctx, i.ID, string(registrationFlow.Type), registrationFlow.Active.String(), provider))
span.AddEvent(events.NewRegistrationSucceeded(ctx, registrationFlow.ID, i.ID, string(registrationFlow.Type), registrationFlow.Active.String(), provider))

s := session.NewInactiveSession()

Expand Down
6 changes: 4 additions & 2 deletions selfservice/flow/settings/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import (
"net/http"
"net/url"

"github.com/gofrs/uuid"

"github.com/ory/x/otelx"

"go.opentelemetry.io/otel/trace"
Expand Down Expand Up @@ -180,11 +182,11 @@ func (s *ErrorHandler) WriteFlowError(
}

if f == nil {
trace.SpanFromContext(ctx).AddEvent(events.NewSettingsFailed(ctx, "", ""))
trace.SpanFromContext(ctx).AddEvent(events.NewSettingsFailed(ctx, uuid.Nil, "", "", err))
s.forward(ctx, w, r, nil, err)
return
}
trace.SpanFromContext(ctx).AddEvent(events.NewSettingsFailed(ctx, string(f.Type), f.Active.String()))
trace.SpanFromContext(ctx).AddEvent(events.NewSettingsFailed(ctx, f.ID, string(f.Type), f.Active.String(), err))

if expired, inner := s.PrepareReplacementForExpiredFlow(ctx, w, r, f, id, err); inner != nil {
s.forward(ctx, w, r, f, err)
Expand Down
2 changes: 1 addition & 1 deletion selfservice/flow/settings/hook.go
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,7 @@ func (e *HookExecutor) PostSettingsHook(ctx context.Context, w http.ResponseWrit
Debug("Completed all PostSettingsPrePersistHooks and PostSettingsPostPersistHooks.")

trace.SpanFromContext(ctx).AddEvent(events.NewSettingsSucceeded(
ctx, i.ID, string(ctxUpdate.Flow.Type), settingsType))
ctx, ctxUpdate.Flow.ID, i.ID, string(ctxUpdate.Flow.Type), settingsType))

if ctxUpdate.Flow.Type == flow.TypeAPI {
updatedFlow, err := e.d.SettingsFlowPersister().GetSettingsFlow(ctx, ctxUpdate.Flow.ID)
Expand Down
6 changes: 4 additions & 2 deletions selfservice/flow/verification/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import (
"net/http"
"net/url"

"github.com/gofrs/uuid"

"go.opentelemetry.io/otel/trace"

"github.com/ory/kratos/x/events"
Expand Down Expand Up @@ -69,11 +71,11 @@ func (s *ErrorHandler) WriteFlowError(
Info("Encountered self-service verification error.")

if f == nil {
trace.SpanFromContext(r.Context()).AddEvent(events.NewVerificationFailed(r.Context(), "", ""))
trace.SpanFromContext(r.Context()).AddEvent(events.NewVerificationFailed(r.Context(), uuid.Nil, "", "", err))
s.forward(w, r, nil, err)
return
}
trace.SpanFromContext(r.Context()).AddEvent(events.NewVerificationFailed(r.Context(), string(f.Type), f.Active.String()))
trace.SpanFromContext(r.Context()).AddEvent(events.NewVerificationFailed(r.Context(), f.ID, string(f.Type), f.Active.String(), err))

if e := new(flow.ExpiredError); errors.As(err, &e) {
strategy, err := s.d.VerificationStrategies(r.Context()).Strategy(f.Active.String())
Expand Down
2 changes: 1 addition & 1 deletion selfservice/flow/verification/hook.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ func (e *HookExecutor) PostVerificationHook(w http.ResponseWriter, r *http.Reque
Debug("ExecutePostVerificationHook completed successfully.")
}

trace.SpanFromContext(r.Context()).AddEvent(events.NewVerificationSucceeded(r.Context(), i.ID, string(a.Type), a.Active.String()))
trace.SpanFromContext(r.Context()).AddEvent(events.NewVerificationSucceeded(r.Context(), a.ID, i.ID, string(a.Type), a.Active.String()))

e.d.Logger().
WithRequest(r).
Expand Down
2 changes: 2 additions & 0 deletions selfservice/hook/session_issuer.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ func (e *SessionIssuer) executePostRegistrationPostPersistHook(w http.ResponseWr
trace.SpanFromContext(r.Context()).AddEvent(events.NewLoginSucceeded(r.Context(), &events.LoginSucceededOpts{
SessionID: s.ID,
IdentityID: s.Identity.ID,
FlowID: a.ID,
FlowType: string(a.Type),
Method: a.Active.String(),
}))
Expand All @@ -90,6 +91,7 @@ func (e *SessionIssuer) executePostRegistrationPostPersistHook(w http.ResponseWr
trace.SpanFromContext(r.Context()).AddEvent(events.NewLoginSucceeded(r.Context(), &events.LoginSucceededOpts{
SessionID: s.ID,
IdentityID: s.Identity.ID,
FlowID: a.ID,
FlowType: string(a.Type),
Method: a.Active.String(),
}))
Expand Down
58 changes: 48 additions & 10 deletions x/events/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,16 @@ package events

import (
"context"
"errors"
"net/url"
"time"

"github.com/gofrs/uuid"
otelattr "go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/trace"

"github.com/ory/herodot"
"github.com/ory/kratos/schema"
"github.com/ory/x/otelx/semconv"
)

Expand Down Expand Up @@ -56,6 +59,8 @@ const (
attributeKeyWebhookResponseStatusCode semconv.AttributeKey = "WebhookResponseStatusCode"
attributeKeyWebhookAttemptNumber semconv.AttributeKey = "WebhookAttemptNumber"
attributeKeyWebhookRequestID semconv.AttributeKey = "WebhookRequestID"
attributeKeyReason semconv.AttributeKey = "Reason"
attributeKeyFlowID semconv.AttributeKey = "FlowID"
)

func attrSessionID(val uuid.UUID) otelattr.KeyValue {
Expand Down Expand Up @@ -118,6 +123,14 @@ func attrWebhookRequestID(id uuid.UUID) otelattr.KeyValue {
return otelattr.String(attributeKeyWebhookRequestID.String(), id.String())
}

func attrReason(err error) otelattr.KeyValue {
return otelattr.String(attributeKeyReason.String(), reasonForError(err))
}

func attrFlowID(id uuid.UUID) otelattr.KeyValue {
return otelattr.String(attributeKeyFlowID.String(), id.String())
}

func NewSessionIssued(ctx context.Context, aal string, sessionID, identityID uuid.UUID) (string, trace.EventOption) {
return SessionIssued.String(),
trace.WithAttributes(
Expand Down Expand Up @@ -155,7 +168,7 @@ func NewSessionLifespanExtended(ctx context.Context, sessionID, identityID uuid.
}

type LoginSucceededOpts struct {
SessionID, IdentityID uuid.UUID
SessionID, IdentityID, FlowID uuid.UUID
FlowType, RequestedAAL, Method, SSOProvider string
IsRefresh bool
}
Expand All @@ -172,84 +185,97 @@ func NewLoginSucceeded(ctx context.Context, o *LoginSucceededOpts) (string, trac
attLoginRequestedPrivilegedSession(o.IsRefresh),
attrSelfServiceMethodUsed(o.Method),
attrSelfServiceSSOProviderUsed(o.SSOProvider),
attrFlowID(o.FlowID),
)...,
)
}

func NewRegistrationSucceeded(ctx context.Context, identityID uuid.UUID, flowType string, method, provider string) (string, trace.EventOption) {
func NewRegistrationSucceeded(ctx context.Context, flowID, identityID uuid.UUID, flowType, method, provider string) (string, trace.EventOption) {
return RegistrationSucceeded.String(),
trace.WithAttributes(append(
semconv.AttributesFromContext(ctx),
attrSelfServiceFlowType(flowType),
semconv.AttrIdentityID(identityID),
attrSelfServiceMethodUsed(method),
attrSelfServiceSSOProviderUsed(provider),
attrFlowID(flowID),
)...)
}

func NewRecoverySucceeded(ctx context.Context, identityID uuid.UUID, flowType string, method string) (string, trace.EventOption) {
func NewRecoverySucceeded(ctx context.Context, flowID, identityID uuid.UUID, flowType, method string) (string, trace.EventOption) {
return RecoverySucceeded.String(),
trace.WithAttributes(append(
semconv.AttributesFromContext(ctx),
attrSelfServiceFlowType(flowType),
semconv.AttrIdentityID(identityID),
attrSelfServiceMethodUsed(method),
attrFlowID(flowID),
)...)
}

func NewSettingsSucceeded(ctx context.Context, identityID uuid.UUID, flowType string, method string) (string, trace.EventOption) {
func NewSettingsSucceeded(ctx context.Context, flowID, identityID uuid.UUID, flowType, method string) (string, trace.EventOption) {
return SettingsSucceeded.String(),
trace.WithAttributes(append(
semconv.AttributesFromContext(ctx),
attrSelfServiceFlowType(flowType),
semconv.AttrIdentityID(identityID),
attrSelfServiceMethodUsed(method),
attrFlowID(flowID),
)...)
}

func NewVerificationSucceeded(ctx context.Context, identityID uuid.UUID, flowType string, method string) (string, trace.EventOption) {
func NewVerificationSucceeded(ctx context.Context, flowID, identityID uuid.UUID, flowType, method string) (string, trace.EventOption) {
return VerificationSucceeded.String(),
trace.WithAttributes(append(
semconv.AttributesFromContext(ctx),
attrSelfServiceMethodUsed(method),
attrSelfServiceFlowType(flowType),
semconv.AttrIdentityID(identityID),
attrFlowID(flowID),
)...)
}

func NewRegistrationFailed(ctx context.Context, flowType string, method string) (string, trace.EventOption) {
func NewRegistrationFailed(ctx context.Context, flowID uuid.UUID, flowType, method string, err error) (string, trace.EventOption) {
return RegistrationFailed.String(),
trace.WithAttributes(append(
semconv.AttributesFromContext(ctx),
attrSelfServiceFlowType(flowType),
attrSelfServiceMethodUsed(method),
attrReason(err),
attrFlowID(flowID),
)...)
}

func NewRecoveryFailed(ctx context.Context, flowType string, method string) (string, trace.EventOption) {
func NewRecoveryFailed(ctx context.Context, flowID uuid.UUID, flowType, method string, err error) (string, trace.EventOption) {
return RecoveryFailed.String(),
trace.WithAttributes(append(
semconv.AttributesFromContext(ctx),
attrSelfServiceFlowType(flowType),
attrSelfServiceMethodUsed(method),
attrReason(err),
attrFlowID(flowID),
)...)
}

func NewSettingsFailed(ctx context.Context, flowType string, method string) (string, trace.EventOption) {
func NewSettingsFailed(ctx context.Context, flowID uuid.UUID, flowType, method string, err error) (string, trace.EventOption) {
return SettingsFailed.String(),
trace.WithAttributes(append(
semconv.AttributesFromContext(ctx),
attrSelfServiceFlowType(flowType),
attrSelfServiceMethodUsed(method),
attrReason(err),
attrFlowID(flowID),
)...)
}

func NewVerificationFailed(ctx context.Context, flowType string, method string) (string, trace.EventOption) {
func NewVerificationFailed(ctx context.Context, flowID uuid.UUID, flowType, method string, err error) (string, trace.EventOption) {
return VerificationFailed.String(),
trace.WithAttributes(append(
semconv.AttributesFromContext(ctx),
attrSelfServiceFlowType(flowType),
attrSelfServiceMethodUsed(method),
attrReason(err),
attrFlowID(flowID),
)...)
}

Expand Down Expand Up @@ -283,13 +309,15 @@ func NewIdentityUpdated(ctx context.Context, identityID uuid.UUID) (string, trac
)
}

func NewLoginFailed(ctx context.Context, flowType string, requestedAAL string, isRefresh bool) (string, trace.EventOption) {
func NewLoginFailed(ctx context.Context, flowID uuid.UUID, flowType, requestedAAL string, isRefresh bool, err error) (string, trace.EventOption) {
return LoginFailed.String(),
trace.WithAttributes(append(
semconv.AttributesFromContext(ctx),
attrSelfServiceFlowType(flowType),
attLoginRequestedAAL(requestedAAL),
attLoginRequestedPrivilegedSession(isRefresh),
attrReason(err),
attrFlowID(flowID),
)...)
}

Expand Down Expand Up @@ -356,3 +384,13 @@ func NewWebhookFailed(ctx context.Context, err error) (string, trace.EventOption
)...,
)
}

func reasonForError(err error) string {
if ve := new(schema.ValidationError); errors.As(err, &ve) {
return ve.Message
}
if r := *new(herodot.ReasonCarrier); errors.As(err, &r) {
return r.Reason()
}
return err.Error()
}
Loading