diff --git a/cmd/daemon/serve.go b/cmd/daemon/serve.go index 52a4d69188db..c14f91a2d2eb 100644 --- a/cmd/daemon/serve.go +++ b/cmd/daemon/serve.go @@ -9,6 +9,8 @@ import ( "net/http" "time" + "github.com/ory/x/otelx/semconv" + "github.com/pkg/errors" "github.com/rs/cors" "github.com/spf13/cobra" @@ -87,6 +89,7 @@ func ServePublic(r driver.Registry, cmd *cobra.Command, args []string, slOpts *s publicLogger.ExcludePaths(healthx.AliveCheckPath, healthx.ReadyCheckPath) } + n.UseFunc(semconv.Middleware) n.Use(publicLogger) n.Use(x.HTTPLoaderContextMiddleware(r)) n.Use(sqa(ctx, cmd, r)) @@ -169,6 +172,7 @@ func ServeAdmin(r driver.Registry, cmd *cobra.Command, args []string, slOpts *se if r.Config().DisableAdminHealthRequestLog(ctx) { adminLogger.ExcludePaths(x.AdminPrefix+healthx.AliveCheckPath, x.AdminPrefix+healthx.ReadyCheckPath, x.AdminPrefix+prometheus.MetricsPrometheusPath) } + n.UseFunc(semconv.Middleware) n.Use(adminLogger) n.UseFunc(x.RedirectAdminMiddleware) n.Use(x.HTTPLoaderContextMiddleware(r)) diff --git a/go.mod b/go.mod index 9cf1f7a74783..77641b4e6c8d 100644 --- a/go.mod +++ b/go.mod @@ -75,7 +75,7 @@ require ( github.com/ory/jsonschema/v3 v3.0.8 github.com/ory/mail/v3 v3.0.0 github.com/ory/nosurf v1.2.7 - github.com/ory/x v0.0.551 + github.com/ory/x v0.0.552 github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2 github.com/pkg/errors v0.9.1 github.com/pquerna/otp v1.4.0 @@ -152,6 +152,7 @@ require ( github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/fullstorydev/grpcurl v1.8.1 // indirect github.com/fxamacker/cbor/v2 v2.4.0 // indirect + github.com/go-bindata/go-bindata v3.1.2+incompatible // indirect github.com/go-logr/logr v1.2.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-openapi/analysis v0.21.4 // indirect diff --git a/go.sum b/go.sum index 52b9f182c650..87c1e69ff981 100644 --- a/go.sum +++ b/go.sum @@ -377,6 +377,8 @@ github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49P github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= +github.com/go-bindata/go-bindata v3.1.2+incompatible h1:5vjJMVhowQdPzjE1LdxyFF7YFTXg5IgGVW4gBr5IbvE= +github.com/go-bindata/go-bindata v3.1.2+incompatible/go.mod h1:xK8Dsgwmeed+BBsSy2XTopBn/8uK2HWuGSnA11C3Joo= github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= @@ -1137,8 +1139,8 @@ github.com/ory/nosurf v1.2.7 h1:YrHrbSensQyU6r6HT/V5+HPdVEgrOTMJiLoJABSBOp4= github.com/ory/nosurf v1.2.7/go.mod h1:d4L3ZBa7Amv55bqxCBtCs63wSlyaiCkWVl4vKf3OUxA= github.com/ory/sessions v1.2.2-0.20220110165800-b09c17334dc2 h1:zm6sDvHy/U9XrGpixwHiuAwpp0Ock6khSVHkrv6lQQU= github.com/ory/sessions v1.2.2-0.20220110165800-b09c17334dc2/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= -github.com/ory/x v0.0.551 h1:U3z2bvSzAwDP0SWmbAdjzfvWPu4k+oWrPctoCdalGk0= -github.com/ory/x v0.0.551/go.mod h1:oRVemI3SQQOLvOCJWIRinHQKlgmay/NbwSyRUIsS/Yk= +github.com/ory/x v0.0.552 h1:vgDw7FFQ7Ama3iyDLbjElY2Um1/ub82iIubK0pUj81M= +github.com/ory/x v0.0.552/go.mod h1:oRVemI3SQQOLvOCJWIRinHQKlgmay/NbwSyRUIsS/Yk= github.com/otiai10/copy v1.2.0/go.mod h1:rrF5dJ5F0t/EWSYODDu4j9/vEeYHMkc8jt0zJChqQWw= github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE= github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs= diff --git a/selfservice/flow/login/error.go b/selfservice/flow/login/error.go index 9d0dec08fdc6..cd1bfa652432 100644 --- a/selfservice/flow/login/error.go +++ b/selfservice/flow/login/error.go @@ -6,7 +6,10 @@ package login import ( "net/http" + "go.opentelemetry.io/otel/trace" + "github.com/ory/kratos/ui/node" + "github.com/ory/kratos/x/events" "github.com/ory/kratos/selfservice/flow" "github.com/ory/kratos/text" @@ -80,6 +83,8 @@ func (s *ErrorHandler) WriteFlowError(w http.ResponseWriter, r *http.Request, f WithField("login_flow", f). Info("Encountered self-service login error.") + trace.SpanFromContext(r.Context()).AddEvent(events.NewLoginFailed(r.Context())) + if f == nil { s.forward(w, r, nil, err) return diff --git a/selfservice/flow/login/hook.go b/selfservice/flow/login/hook.go index 7517a0107f64..6dc000dbb0e5 100644 --- a/selfservice/flow/login/hook.go +++ b/selfservice/flow/login/hook.go @@ -9,10 +9,12 @@ import ( "net/http" "time" - "github.com/pkg/errors" - "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/trace" + "github.com/ory/kratos/x/events" + + "github.com/pkg/errors" + "github.com/ory/kratos/driver/config" "github.com/ory/kratos/hydra" "github.com/ory/kratos/identity" @@ -21,9 +23,7 @@ import ( "github.com/ory/kratos/ui/container" "github.com/ory/kratos/ui/node" "github.com/ory/kratos/x" - "github.com/ory/x/httpx" "github.com/ory/x/otelx" - "github.com/ory/x/otelx/semconv" ) type ( @@ -178,15 +178,8 @@ func (e *HookExecutor) PostLoginHook(w http.ResponseWriter, r *http.Request, g n WithField("session_id", s.ID). WithField("identity_id", i.ID). Info("Identity authenticated successfully and was issued an Ory Kratos Session Token.") - trace.SpanFromContext(r.Context()).AddEvent( - semconv.EventSessionIssued, - trace.WithAttributes( - attribute.String(semconv.AttrIdentityID, i.ID.String()), - attribute.String(semconv.AttrNID, i.NID.String()), - attribute.String(semconv.AttrClientIP, httpx.ClientIP(r)), - attribute.String("flow", string(flow.TypeAPI)), - ), - ) + + trace.SpanFromContext(r.Context()).AddEvent(events.NewSessionIssued(r.Context(), s.ID, i.ID)) response := &APIFlowResponse{Session: s, Token: s.Token} if required, _ := e.requiresAAL2(r, classified, a); required { @@ -207,15 +200,8 @@ func (e *HookExecutor) PostLoginHook(w http.ResponseWriter, r *http.Request, g n WithField("identity_id", i.ID). WithField("session_id", s.ID). Info("Identity authenticated successfully and was issued an Ory Kratos Session Cookie.") - trace.SpanFromContext(r.Context()).AddEvent( - semconv.EventSessionIssued, - trace.WithAttributes( - attribute.String(semconv.AttrIdentityID, i.ID.String()), - attribute.String(semconv.AttrNID, i.NID.String()), - attribute.String(semconv.AttrClientIP, httpx.ClientIP(r)), - attribute.String("flow", string(flow.TypeBrowser)), - ), - ) + + trace.SpanFromContext(r.Context()).AddEvent(events.NewSessionIssued(r.Context(), s.ID, i.ID)) if x.IsJSONRequest(r) { // Browser flows rely on cookies. Adding tokens in the mix will confuse consumers. diff --git a/selfservice/flow/logout/handler.go b/selfservice/flow/logout/handler.go index 36aec9b2e4bf..73c0002c0120 100644 --- a/selfservice/flow/logout/handler.go +++ b/selfservice/flow/logout/handler.go @@ -7,8 +7,13 @@ import ( "net/http" "net/url" + "go.opentelemetry.io/otel/trace" + "github.com/pkg/errors" + "github.com/ory/kratos/identity" + "github.com/ory/kratos/x/events" + "github.com/ory/herodot" "github.com/ory/x/decoderx" "github.com/ory/x/sqlcon" @@ -198,6 +203,16 @@ func (h *Handler) performNativeLogout(w http.ResponseWriter, r *http.Request, _ h.d.Writer().WriteError(w, r, err) return } + sess, err := h.d.SessionPersister().GetSessionByToken(r.Context(), p.SessionToken, session.ExpandNothing, identity.ExpandNothing) + if err != nil { + if errors.Is(err, sqlcon.ErrNoRows) { + h.d.Writer().WriteError(w, r, errors.WithStack(herodot.ErrForbidden.WithReason("The provided Ory Session Token could not be found, is invalid, or otherwise malformed."))) + return + } + + h.d.Writer().WriteError(w, r, err) + return + } if err := h.d.SessionPersister().RevokeSessionByToken(r.Context(), p.SessionToken); err != nil { if errors.Is(err, sqlcon.ErrNoRows) { @@ -209,6 +224,8 @@ func (h *Handler) performNativeLogout(w http.ResponseWriter, r *http.Request, _ return } + trace.SpanFromContext(r.Context()).AddEvent(events.NewSessionRevoked(r.Context(), sess.ID, sess.IdentityID)) + w.WriteHeader(http.StatusNoContent) } @@ -286,6 +303,8 @@ func (h *Handler) updateLogoutFlow(w http.ResponseWriter, r *http.Request, ps ht return } + trace.SpanFromContext(r.Context()).AddEvent(events.NewSessionRevoked(r.Context(), sess.ID, sess.IdentityID)) + h.completeLogout(w, r) } diff --git a/selfservice/flow/registration/error.go b/selfservice/flow/registration/error.go index ae01ca2850b4..c3281c2027cf 100644 --- a/selfservice/flow/registration/error.go +++ b/selfservice/flow/registration/error.go @@ -6,8 +6,11 @@ package registration import ( "net/http" + "go.opentelemetry.io/otel/trace" + "github.com/ory/kratos/schema" "github.com/ory/kratos/ui/node" + "github.com/ory/kratos/x/events" "github.com/ory/kratos/selfservice/flow" "github.com/ory/kratos/text" @@ -86,6 +89,8 @@ func (s *ErrorHandler) WriteFlowError( WithField("registration_flow", f). Info("Encountered self-service flow error.") + trace.SpanFromContext(r.Context()).AddEvent(events.NewRegistrationFailed(r.Context())) + if f == nil { s.forward(w, r, nil, err) return diff --git a/selfservice/flow/registration/hook.go b/selfservice/flow/registration/hook.go index 5c77f0d5f020..d5a3a489e8ec 100644 --- a/selfservice/flow/registration/hook.go +++ b/selfservice/flow/registration/hook.go @@ -9,12 +9,12 @@ import ( "net/http" "time" - "github.com/pkg/errors" - "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/trace" - "github.com/ory/x/httpx" - "github.com/ory/x/otelx/semconv" + "github.com/ory/kratos/x/events" + + "github.com/pkg/errors" + "github.com/ory/x/sqlcon" "github.com/ory/kratos/driver/config" @@ -169,15 +169,8 @@ func (e *HookExecutor) PostRegistrationHook(w http.ResponseWriter, r *http.Reque WithRequest(r). WithField("identity_id", i.ID). Info("A new identity has registered using self-service registration.") - trace.SpanFromContext(r.Context()).AddEvent( - semconv.EventIdentityCreated, - trace.WithAttributes( - attribute.String(semconv.AttrIdentityID, i.ID.String()), - attribute.String(semconv.AttrNID, i.NID.String()), - attribute.String(semconv.AttrClientIP, httpx.ClientIP(r)), - attribute.String("flow", string(a.Type)), - ), - ) + + trace.SpanFromContext(r.Context()).AddEvent(events.NewIdentityCreated(r.Context(), i.ID)) s, err := session.NewActiveSession(r, i, e.d.Config(), time.Now().UTC(), ct, identity.AuthenticatorAssuranceLevel1) if err != nil { diff --git a/selfservice/hook/session_issuer.go b/selfservice/hook/session_issuer.go index 4aed85284cb8..84177a28b419 100644 --- a/selfservice/hook/session_issuer.go +++ b/selfservice/hook/session_issuer.go @@ -8,6 +8,10 @@ import ( "net/http" "time" + "go.opentelemetry.io/otel/trace" + + "github.com/ory/kratos/x/events" + "github.com/pkg/errors" "github.com/ory/kratos/selfservice/flow" @@ -51,6 +55,8 @@ func (e *SessionIssuer) executePostRegistrationPostPersistHook(w http.ResponseWr return err } + trace.SpanFromContext(r.Context()).AddEvent(events.NewSessionIssued(r.Context(), s.ID, s.IdentityID)) + if a.Type == flow.TypeAPI { a.AddContinueWith(flow.NewContinueWithSetToken(s.Token)) e.r.Writer().Write(w, r, ®istration.APIFlowResponse{ diff --git a/x/events/events.go b/x/events/events.go new file mode 100644 index 000000000000..ac64d5454b8b --- /dev/null +++ b/x/events/events.go @@ -0,0 +1,76 @@ +// Copyright © 2023 Ory Corp +// SPDX-License-Identifier: Apache-2.0 + +package events + +import ( + "context" + + "github.com/gofrs/uuid" + otelattr "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/trace" + + "github.com/ory/x/otelx/semconv" +) + +const ( + SessionIssued semconv.Event = "SessionIssued" + SessionRevoked semconv.Event = "SessionRevoked" + RegistrationFailed semconv.Event = "RegistrationFailed" + IdentityCreated semconv.Event = "IdentityCreated" + LoginFailed semconv.Event = "LoginFailed" +) + +const ( + attributeKeySessionID semconv.AttributeKey = "SessionID" +) + +func attrSessionID(val uuid.UUID) otelattr.KeyValue { + return otelattr.String(attributeKeySessionID.String(), val.String()) +} + +func NewSessionIssued(ctx context.Context, sessionID, identityID uuid.UUID) (string, trace.EventOption) { + return SessionIssued.String(), + trace.WithAttributes( + append( + semconv.AttributesFromContext(ctx), + semconv.AttrIdentityID(identityID), + attrSessionID(sessionID), + )..., + ) +} + +func NewRegistrationFailed(ctx context.Context) (string, trace.EventOption) { + return RegistrationFailed.String(), + trace.WithAttributes( + semconv.AttributesFromContext(ctx)..., + ) +} + +func NewIdentityCreated(ctx context.Context, identityID uuid.UUID) (string, trace.EventOption) { + return IdentityCreated.String(), + trace.WithAttributes( + append( + semconv.AttributesFromContext(ctx), + semconv.AttrIdentityID(identityID), + )..., + ) +} + +func NewLoginFailed(ctx context.Context) (string, trace.EventOption) { + return LoginFailed.String(), + trace.WithAttributes( + semconv.AttributesFromContext(ctx)..., + ) +} + +func NewSessionRevoked(ctx context.Context, sessionID, identityID uuid.UUID) (string, trace.EventOption) { + return SessionRevoked.String(), + trace.WithAttributes( + append( + semconv.AttributesFromContext(ctx), + semconv.AttrIdentityID(identityID), + attrSessionID(sessionID), + )..., + ) +}