Files
upstream-mirror c60cc7ca75
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
snapshot of https://github.com/ory/kratos.git (master, 2026-09-15; blobs > 131072B omitted)
2026-09-15 21:19:12 +05:00

178 lines
4.5 KiB
Go

// Copyright © 2023 Ory Corp
// SPDX-License-Identifier: Apache-2.0
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"strings"
log "github.com/sirupsen/logrus"
)
const AuthHeader = "X-Authorize-Request"
type (
// copied from selfservice/hook/web_hook.go
detailedMessage struct {
ID int `json:"id"`
Text string `json:"text"`
Type string `json:"type"`
Context json.RawMessage `json:"context,omitempty"`
}
errorMessage struct {
InstancePtr string `json:"instance_ptr"`
Messages []detailedMessage `json:"messages"`
}
rawHookResponse struct {
Messages []errorMessage `json:"messages"`
}
logResponseWriter struct {
Status int
Size int
http.ResponseWriter
}
)
// Header returns & satisfies the http.ResponseWriter interface
func (w *logResponseWriter) Header() http.Header {
return w.ResponseWriter.Header()
}
// Write satisfies the http.ResponseWriter interface and
// captures data written, in bytes
func (w *logResponseWriter) Write(data []byte) (int, error) {
written, err := w.ResponseWriter.Write(data)
w.Size += written
return written, err
}
// WriteHeader satisfies the http.ResponseWriter interface and
// allows us to catch the status code
func (w *logResponseWriter) WriteHeader(statusCode int) {
w.Status = statusCode
w.ResponseWriter.WriteHeader(statusCode)
}
func accessLog(next http.HandlerFunc) http.HandlerFunc {
fn := func(w http.ResponseWriter, r *http.Request) {
log.WithFields(log.Fields{"application": "webhooks", "method": r.Method, "path": r.URL.Path}).Info("incoming request")
responseWriter := logResponseWriter{http.StatusOK, 0, w}
next.ServeHTTP(&responseWriter, r)
log.WithFields(log.Fields{"application": "webhooks", "status": responseWriter.Status, "size": responseWriter.Size, "path": r.URL.Path}).Info("response generated")
}
return fn
}
func headerAuth(next http.HandlerFunc) http.HandlerFunc {
fn := func(w http.ResponseWriter, r *http.Request) {
if r.Header.Get(AuthHeader) != "1" {
w.WriteHeader(http.StatusUnauthorized)
} else {
next.ServeHTTP(w, r)
}
}
return fn
}
func healthCheck(w http.ResponseWriter, _ *http.Request) {
_, _ = w.Write([]byte("OK"))
}
func webhookHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
w.WriteHeader(http.StatusBadRequest)
return
}
payload := struct {
IdentityId string `json:"identity_id,omitempty"`
Email string `json:"email,omitempty"`
FlowId string `json:"flow_id"`
FlowType string `json:"flow_type"`
Context json.RawMessage `json:"ctx"`
}{}
body, err := io.ReadAll(r.Body)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
log.WithError(err).Warn("could not read request body")
b := bytes.NewBufferString(fmt.Sprintf("error while reading request body: %s", err))
_, _ = w.Write(b.Bytes())
return
}
defer r.Body.Close()
if err := json.Unmarshal(body, &payload); err != nil {
w.WriteHeader(http.StatusBadRequest)
log.WithError(err).Warn("could not unmarshal request JSON")
b := bytes.NewBufferString(fmt.Sprintf("error while parsing request JSON: %s", err))
_, _ = w.Write(b.Bytes())
return
}
log.WithField("payload", string(body)).Info("unmarshalled request")
if !strings.Contains(payload.Email, "_blocked@ory.sh") || payload.FlowType == "api" {
w.WriteHeader(http.StatusOK)
return
}
w.WriteHeader(http.StatusConflict)
detail := detailedMessage{
ID: 1234,
Type: "error",
Text: "email could not be validated",
}
msg := errorMessage{InstancePtr: "#/traits/email", Messages: []detailedMessage{detail}}
resp := rawHookResponse{Messages: []errorMessage{msg}}
err = json.NewEncoder(w).Encode(&resp)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
buff := bytes.NewBufferString(err.Error())
_, _ = w.Write(buff.Bytes())
return
}
}
func writeWebhookHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
w.WriteHeader(http.StatusBadRequest)
return
}
log.WithFields(log.Fields{"application": "webhooks", "path": r.URL.Path, "response": r.URL.Query().Get("response")}).Info("sending response")
_, _ = w.Write([]byte(r.URL.Query().Get("response")))
}
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/health", healthCheck)
mux.HandleFunc("/webhook", accessLog(headerAuth(webhookHandler)))
mux.HandleFunc("/webhook/write", accessLog(writeWebhookHandler))
s := http.Server{
Addr: ":4459",
Handler: mux,
}
err := s.ListenAndServe()
if err != nil {
panic(err)
}
}