snapshot of https://github.com/ory/kratos.git (master, 2026-09-15; blobs > 131072B omitted)
Some checks failed
CI / Run tests and lints (push) Failing after 29s
CI / Build CLI docs (push) Has been skipped
CI / Run Playwright end-to-end tests (mysql) (push) Failing after 3s
CI / Run Playwright end-to-end tests (postgres) (push) Failing after 2s
CI / Run Playwright end-to-end tests (sqlite) (push) Failing after 3s
CodeQL / Analyze (go) (push) Failing after 4s
CodeQL / Analyze (javascript) (push) Failing after 3s
Docker Image Scanners / scanners (push) Failing after 4s
Synchronize Issue Labels / Synchronize Issue Labels (push) Has been skipped
CI / Run end-to-end tests (postgres) (push) Failing after 19s
CI / Run end-to-end tests (sqlite) (push) Failing after 2s
CI / Run Playwright end-to-end tests (cockroach) (push) Failing after 2s
CI / Draft newsletter (push) Has been skipped
CI / Generate release (push) Has been skipped
CI / Pending approval Slack notification (push) Has been skipped
CI / Send newsletter (push) Has been skipped
Some checks failed
CI / Run tests and lints (push) Failing after 29s
CI / Build CLI docs (push) Has been skipped
CI / Run Playwright end-to-end tests (mysql) (push) Failing after 3s
CI / Run Playwright end-to-end tests (postgres) (push) Failing after 2s
CI / Run Playwright end-to-end tests (sqlite) (push) Failing after 3s
CodeQL / Analyze (go) (push) Failing after 4s
CodeQL / Analyze (javascript) (push) Failing after 3s
Docker Image Scanners / scanners (push) Failing after 4s
Synchronize Issue Labels / Synchronize Issue Labels (push) Has been skipped
CI / Run end-to-end tests (postgres) (push) Failing after 19s
CI / Run end-to-end tests (sqlite) (push) Failing after 2s
CI / Run Playwright end-to-end tests (cockroach) (push) Failing after 2s
CI / Draft newsletter (push) Has been skipped
CI / Generate release (push) Has been skipped
CI / Pending approval Slack notification (push) Has been skipped
CI / Send newsletter (push) Has been skipped
This commit is contained in:
138
selfservice/hook/password_migration_hook.go
Normal file
138
selfservice/hook/password_migration_hook.go
Normal file
@@ -0,0 +1,138 @@
|
||||
// Copyright © 2024 Ory Corp
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package hook
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/hashicorp/go-retryablehttp"
|
||||
"github.com/pkg/errors"
|
||||
"go.opentelemetry.io/otel/codes"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.11.0"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
grpccodes "google.golang.org/grpc/codes"
|
||||
|
||||
"github.com/ory/herodot"
|
||||
"github.com/ory/kratos/identity"
|
||||
"github.com/ory/kratos/request"
|
||||
"github.com/ory/kratos/schema"
|
||||
"github.com/ory/kratos/selfservice/flow"
|
||||
"github.com/ory/kratos/x"
|
||||
"github.com/ory/x/otelx"
|
||||
)
|
||||
|
||||
type (
|
||||
PasswordMigration struct {
|
||||
deps webHookDependencies
|
||||
conf *request.Config
|
||||
}
|
||||
PasswordMigrationRequest struct {
|
||||
Identifier string `json:"identifier"`
|
||||
Password string `json:"password"`
|
||||
Identity *identity.Identity `json:"-"`
|
||||
}
|
||||
PasswordMigrationResponse struct {
|
||||
Status string `json:"status"`
|
||||
}
|
||||
)
|
||||
|
||||
func NewPasswordMigrationHook(deps webHookDependencies, conf *request.Config) *PasswordMigration {
|
||||
return &PasswordMigration{deps: deps, conf: conf}
|
||||
}
|
||||
|
||||
func (p *PasswordMigration) Execute(ctx context.Context, req *http.Request, flow flow.Flow, data *PasswordMigrationRequest) (err error) {
|
||||
var (
|
||||
httpClient = p.deps.HTTPClient(ctx)
|
||||
emitEvent = p.conf.EmitAnalyticsEvent == nil || *p.conf.EmitAnalyticsEvent // default true
|
||||
tracer = trace.SpanFromContext(ctx).TracerProvider().Tracer("kratos-webhooks")
|
||||
)
|
||||
|
||||
ctx, span := tracer.Start(ctx, "selfservice.login.password_migration")
|
||||
defer otelx.End(span, &err)
|
||||
|
||||
if emitEvent {
|
||||
InstrumentHTTPClientForEvents(ctx, httpClient, x.NewUUID(), "password_migration_hook")
|
||||
}
|
||||
builder, err := request.NewBuilder(p.conf, p.deps)
|
||||
if err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
var whReq *retryablehttp.Request
|
||||
if p.conf.TemplateURI == "" {
|
||||
whReq, err = builder.BuildRequest(ctx, nil) // passing a nil body here skips Jsonnet
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rawData, err := json.Marshal(data)
|
||||
if err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
if err = whReq.SetBody(rawData); err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
} else {
|
||||
type templateContextMerged struct {
|
||||
templateContext
|
||||
Password string `json:"password"`
|
||||
Identifier string `json:"identifier"`
|
||||
}
|
||||
whReq, err = builder.BuildRequest(ctx, templateContextMerged{
|
||||
templateContext: templateContext{
|
||||
Flow: flow,
|
||||
RequestHeaders: req.Header,
|
||||
RequestMethod: req.Method,
|
||||
RequestURL: x.RequestURL(req).String(),
|
||||
RequestCookies: cookies(req),
|
||||
Identity: data.Identity,
|
||||
},
|
||||
Password: data.Password,
|
||||
Identifier: data.Identifier,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
p.deps.Logger().WithRequest(whReq.Request).Info("Dispatching password migration hook")
|
||||
|
||||
resp, err := httpClient.Do(whReq)
|
||||
if err != nil {
|
||||
return (&herodot.DefaultError{
|
||||
CodeField: http.StatusBadGateway,
|
||||
StatusField: http.StatusText(http.StatusBadGateway),
|
||||
GRPCCodeField: grpccodes.Aborted,
|
||||
ReasonField: "A third-party upstream service could not be reached. Please try again later.",
|
||||
ErrorField: "calling the password migration hook failed",
|
||||
}).WithWrap(errors.WithStack(err))
|
||||
}
|
||||
defer func() { _ = resp.Body.Close() }()
|
||||
span.SetAttributes(semconv.HTTPAttributesFromHTTPStatusCode(resp.StatusCode)...)
|
||||
|
||||
switch resp.StatusCode {
|
||||
case http.StatusOK:
|
||||
// We now check if the response matches `{"status": "password_match" }`.
|
||||
dec := json.NewDecoder(io.LimitReader(resp.Body, 1024)) // limit the response body to 1KB
|
||||
var response PasswordMigrationResponse
|
||||
if err := dec.Decode(&response); err != nil || response.Status != "password_match" {
|
||||
return errors.WithStack(schema.NewInvalidCredentialsError())
|
||||
}
|
||||
return nil
|
||||
|
||||
case http.StatusForbidden:
|
||||
return errors.WithStack(schema.NewInvalidCredentialsError())
|
||||
default:
|
||||
span.SetStatus(codes.Error, "Unexpected HTTP status code")
|
||||
return &herodot.DefaultError{
|
||||
CodeField: http.StatusBadGateway,
|
||||
StatusField: http.StatusText(http.StatusBadGateway),
|
||||
GRPCCodeField: grpccodes.Aborted,
|
||||
ReasonField: "A third-party upstream service responded improperly. Please try again later.",
|
||||
ErrorField: fmt.Sprintf("password migration hook failed with status code %v", resp.StatusCode),
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user