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:
78
selfservice/strategy/passkey/passkey_login_signcount_test.go
Normal file
78
selfservice/strategy/passkey/passkey_login_signcount_test.go
Normal file
@@ -0,0 +1,78 @@
|
||||
// Copyright © 2025 Ory Corp
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package passkey_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
"github.com/gofrs/uuid"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/tidwall/gjson"
|
||||
|
||||
"github.com/ory/kratos/driver/config"
|
||||
"github.com/ory/kratos/identity"
|
||||
"github.com/ory/kratos/pkg/testhelpers"
|
||||
"github.com/ory/kratos/ui/node"
|
||||
)
|
||||
|
||||
// TestLoginUpdatesSignCount is a regression test for HackerOne #3780149 on the
|
||||
// passkey strategy (which uses ValidateDiscoverableLogin).
|
||||
//
|
||||
// The stored passkey credential starts at sign_count = 3 and the assertion
|
||||
// response reports counter = 10. After a successful login, the stored
|
||||
// SignCount must be updated to 10 so that W3C clone detection works.
|
||||
func TestLoginUpdatesSignCount(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
fix := newLoginFixture(t)
|
||||
fix.conf.MustSet(t.Context(), config.ViperKeySessionWhoAmIAAL, "aal1")
|
||||
|
||||
storedCredential := func(t *testing.T, idID uuid.UUID) (*identity.Credentials, *identity.AuthenticatorWebAuthn) {
|
||||
stored, err := fix.reg.PrivilegedIdentityPool().GetIdentityConfidential(t.Context(), idID)
|
||||
require.NoError(t, err)
|
||||
c, ok := stored.GetCredentials(identity.CredentialsTypePasskey)
|
||||
require.True(t, ok)
|
||||
var conf identity.CredentialsWebAuthnConfig
|
||||
require.NoError(t, json.Unmarshal(c.Config, &conf))
|
||||
require.NotEmpty(t, conf.Credentials)
|
||||
require.NotNil(t, conf.Credentials[0].Authenticator)
|
||||
return c, conf.Credentials[0].Authenticator
|
||||
}
|
||||
|
||||
id := fix.createIdentityWithPasskey(t, identity.Credentials{
|
||||
Config: loginPasswordlessCredentials,
|
||||
Version: 1,
|
||||
})
|
||||
|
||||
// Sanity: the registered credential starts at the fixture's sign_count = 3.
|
||||
credBefore, authBefore := storedCredential(t, id.ID)
|
||||
require.EqualValues(t, 3, authBefore.SignCount)
|
||||
|
||||
browserClient := testhelpers.NewClientWithCookies(t)
|
||||
body, _, _ := fix.submitWebAuthnLoginWithClient(t, true, loginPasswordlessContext, browserClient, func(values url.Values) {
|
||||
values.Set(node.PasskeyLogin, string(loginPasswordlessResponse))
|
||||
}, testhelpers.InitFlowWithAAL(identity.AuthenticatorAssuranceLevel1))
|
||||
|
||||
require.True(t, gjson.Get(body, "session.active").Bool(), "%s", body)
|
||||
|
||||
credAfter, auth := storedCredential(t, id.ID)
|
||||
assert.EqualValues(t, 10, auth.SignCount,
|
||||
"stored SignCount must be updated from the login assertion for clone detection to work")
|
||||
|
||||
// The assertion counter advanced (3 -> 10), so this is not a clone: the
|
||||
// persisted clone warning must round-trip as false rather than being set
|
||||
// spuriously on a normal login.
|
||||
assert.False(t, auth.CloneWarning,
|
||||
"a normally advancing counter must not set the clone warning")
|
||||
|
||||
// The credential row must be updated in place, not deleted and recreated:
|
||||
// its id and created_at must be stable across the login.
|
||||
assert.Equal(t, credBefore.ID, credAfter.ID,
|
||||
"the credential row must be updated in place (stable id), not recreated")
|
||||
assert.Equal(t, credBefore.CreatedAt, credAfter.CreatedAt,
|
||||
"persisting the sign counter must not reset the credential's created_at")
|
||||
}
|
||||
Reference in New Issue
Block a user