diff --git a/selfservice/strategy/webauthn/settings.go b/selfservice/strategy/webauthn/settings.go index e2294001225c..43e6cfac235b 100644 --- a/selfservice/strategy/webauthn/settings.go +++ b/selfservice/strategy/webauthn/settings.go @@ -20,16 +20,18 @@ import ( "github.com/tidwall/sjson" "github.com/ory/herodot" + "github.com/ory/kratos/session" "github.com/gofrs/uuid" "github.com/pkg/errors" + "github.com/ory/x/decoderx" + "github.com/ory/kratos/identity" "github.com/ory/kratos/selfservice/flow" "github.com/ory/kratos/selfservice/flow/settings" "github.com/ory/kratos/x" - "github.com/ory/x/decoderx" ) func (s *Strategy) RegisterSettingsRoutes(_ *x.RouterPublic) { @@ -188,6 +190,10 @@ func (s *Strategy) continueSettingsFlowRemove(w http.ResponseWriter, r *http.Req if len(updated) == len(cc.Credentials) { return errors.WithStack(herodot.ErrBadRequest.WithReasonf("You tried to remove a WebAuthn credential which does not exist.")) } + if len(updated) == 0 { + i.DeleteCredentialsType(identity.CredentialsTypeWebAuthn) + return nil + } cc.Credentials = updated cred.Config, err = json.Marshal(cc) diff --git a/selfservice/strategy/webauthn/settings_test.go b/selfservice/strategy/webauthn/settings_test.go index 4708ae649ed2..6252ac3d23c3 100644 --- a/selfservice/strategy/webauthn/settings_test.go +++ b/selfservice/strategy/webauthn/settings_test.go @@ -24,14 +24,15 @@ import ( "github.com/stretchr/testify/require" + "github.com/ory/x/assertx" + "github.com/ory/x/sqlxx" + "github.com/ory/kratos/driver" "github.com/ory/kratos/driver/config" "github.com/ory/kratos/identity" "github.com/ory/kratos/internal" "github.com/ory/kratos/internal/testhelpers" "github.com/ory/kratos/x" - "github.com/ory/x/assertx" - "github.com/ory/x/sqlxx" ) //go:embed fixtures/settings/has_webauth.json @@ -347,6 +348,44 @@ func TestCompleteSettings(t *testing.T) { }) }) + t.Run("case=remove all security keys", func(t *testing.T) { + run := func(t *testing.T, spa bool) { + id := createIdentity(t, reg) + allCred, ok := id.GetCredentials(identity.CredentialsTypeWebAuthn) + assert.True(t, ok) + + var cc webauthn.CredentialsConfig + require.NoError(t, json.Unmarshal(allCred.Config, &cc)) + require.Len(t, cc.Credentials, 2) + + for _, cred := range cc.Credentials { + body, res := doBrowserFlow(t, spa, func(v url.Values) { + v.Set(node.WebAuthnRemove, fmt.Sprintf("%x", cred.ID)) + }, id) + + if spa { + assert.Contains(t, res.Request.URL.String(), publicTS.URL+settings.RouteSubmitFlow) + } else { + assert.Contains(t, res.Request.URL.String(), uiTS.URL) + } + assert.EqualValues(t, settings.StateSuccess, gjson.Get(body, "state").String(), body) + } + + actual, err := reg.Persister().GetIdentityConfidential(context.Background(), id.ID) + require.NoError(t, err) + _, ok = actual.GetCredentials(identity.CredentialsTypeWebAuthn) + assert.False(t, ok) + } + + t.Run("type=browser", func(t *testing.T) { + run(t, false) + }) + + t.Run("type=spa", func(t *testing.T) { + run(t, true) + }) + }) + t.Run("case=fails with browser submit register payload is invalid", func(t *testing.T) { run := func(t *testing.T, spa bool) { id := createIdentity(t, reg)