Make delivery and integration failures explicit
Persist reminder presentations and retry state, atomically complete collapsed deliveries, fall back across away reaches, and block permanent failures visibly (V-715, V-678). Fail closed when enabled integrations lack credentials and keep remote arms explicitly dark (V-691). Give mavweb one sanitized, request-correlated error contract (V-689). Owner explicitly requested direct commits to master.
This commit is contained in:
+18
-14
@@ -92,8 +92,8 @@ var passkeyTmpl = parsePage("passkey", passkeyPageHTML, nil)
|
||||
func (h *PasskeyHandle) RegisterBegin(w http.ResponseWriter, r *http.Request) {
|
||||
opts, challenge, err := h.rp.CreationOptions([]byte("maven-user"), "maven user")
|
||||
if err != nil {
|
||||
log.Printf("webauthn: register begin: %v", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
writeProblem(w, r, http.StatusInternalServerError, problemWebAuthnBegin,
|
||||
"passkey registration could not start", fmt.Errorf("webauthn register begin: %w", err))
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
@@ -102,7 +102,8 @@ func (h *PasskeyHandle) RegisterBegin(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
func (h *PasskeyHandle) RegisterFinish(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPost {
|
||||
http.Error(w, "POST only", http.StatusMethodNotAllowed)
|
||||
writeProblem(w, r, http.StatusMethodNotAllowed, problemMethodNotAllowed,
|
||||
"POST only", nil)
|
||||
return
|
||||
}
|
||||
var body struct {
|
||||
@@ -110,7 +111,8 @@ func (h *PasskeyHandle) RegisterFinish(w http.ResponseWriter, r *http.Request) {
|
||||
Credential map[string]any `json:"credential"`
|
||||
}
|
||||
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
|
||||
http.Error(w, "bad request: "+err.Error(), http.StatusBadRequest)
|
||||
writeProblem(w, r, http.StatusBadRequest, problemInvalidRequest,
|
||||
"invalid registration request", fmt.Errorf("decode webauthn registration: %w", err))
|
||||
return
|
||||
}
|
||||
save := func(id string, publicKey []byte, _ []byte, _ string) error {
|
||||
@@ -118,8 +120,8 @@ func (h *PasskeyHandle) RegisterFinish(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
credID, err := h.rp.FinishRegistration(save, body.Challenge, body.Credential)
|
||||
if err != nil {
|
||||
log.Printf("webauthn: register finish: %v", err)
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
writeProblem(w, r, http.StatusBadRequest, problemWebAuthnFinish,
|
||||
"passkey registration failed", fmt.Errorf("webauthn register finish: %w", err))
|
||||
return
|
||||
}
|
||||
log.Printf("webauthn: registered credential %s", credID)
|
||||
@@ -140,8 +142,8 @@ func (h *PasskeyHandle) RegisterFinish(w http.ResponseWriter, r *http.Request) {
|
||||
func (h *PasskeyHandle) AssertBegin(w http.ResponseWriter, r *http.Request) {
|
||||
opts, challenge, err := h.rp.AssertionOptions()
|
||||
if err != nil {
|
||||
log.Printf("webauthn: assert begin: %v", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
writeProblem(w, r, http.StatusInternalServerError, problemWebAuthnBegin,
|
||||
"passkey assertion could not start", fmt.Errorf("webauthn assert begin: %w", err))
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
@@ -150,7 +152,8 @@ func (h *PasskeyHandle) AssertBegin(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
func (h *PasskeyHandle) AssertFinish(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPost {
|
||||
http.Error(w, "POST only", http.StatusMethodNotAllowed)
|
||||
writeProblem(w, r, http.StatusMethodNotAllowed, problemMethodNotAllowed,
|
||||
"POST only", nil)
|
||||
return
|
||||
}
|
||||
var body struct {
|
||||
@@ -176,7 +179,8 @@ func (h *PasskeyHandle) AssertFinish(w http.ResponseWriter, r *http.Request) {
|
||||
Explicit bool `json:"explicit"`
|
||||
}
|
||||
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
|
||||
http.Error(w, "bad request: "+err.Error(), http.StatusBadRequest)
|
||||
writeProblem(w, r, http.StatusBadRequest, problemInvalidRequest,
|
||||
"invalid assertion request", fmt.Errorf("decode webauthn assertion: %w", err))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -189,8 +193,8 @@ func (h *PasskeyHandle) AssertFinish(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
credID, err := h.rp.FinishAssertion(lookup, update, body.Challenge, body.Credential)
|
||||
if err != nil {
|
||||
log.Printf("webauthn: assert finish: %v", err)
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
writeProblem(w, r, http.StatusBadRequest, problemWebAuthnFinish,
|
||||
"passkey assertion failed", fmt.Errorf("webauthn assert finish: %w", err))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -201,8 +205,8 @@ func (h *PasskeyHandle) AssertFinish(w http.ResponseWriter, r *http.Request) {
|
||||
ctx, cancel := context.WithTimeout(r.Context(), 5*time.Second)
|
||||
defer cancel()
|
||||
if err := h.assertFn.AssertStepUp(ctx); err != nil {
|
||||
log.Printf("webauthn: assert step-up: %v", err)
|
||||
http.Error(w, "step-up assertion failed", http.StatusBadGateway)
|
||||
writeProblem(w, r, http.StatusBadGateway, problemWebAuthnStepUp,
|
||||
"step-up assertion failed", fmt.Errorf("assert step-up in core: %w", err))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user