Skip to content

Commit

Permalink
feat: add credential migrator pattern
Browse files Browse the repository at this point in the history
  • Loading branch information
aeneasr committed Mar 7, 2022
1 parent 8427322 commit 77afc6f
Show file tree
Hide file tree
Showing 6 changed files with 203 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"id": "00000000-0000-0000-0000-000000000000",
"schema_id": "",
"schema_url": "",
"state": "",
"traits": null,
"created_at": "0001-01-01T00:00:00Z",
"updated_at": "0001-01-01T00:00:00Z"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"id": "4d64fa08-20fc-450d-bebd-ebd7c7b6e249",
"credentials": {
"webauthn": {
"type": "webauthn",
"identifiers": [
"4d64fa08-20fc-450d-bebd-ebd7c7b6e249"
],
"config": {
"credentials": [
{
"id": "HQ4LaIJ9NiqS1r0CQpWY+K0gMvhOq4yk5BHuO/YlitcurSpBK7weDXOvBcuN4lvn6DAmjGfmj/J/6bpOmtdT8Q==",
"public_key": "pQECAyYgASFYILAYFLoH1T8bQMSbPrNBCMMS5U7OFWRwv2U+GkAoiBADIlggBv+8ni7XVZYBB8ufMbP/d9fDxbmOkVVHOgcJifnoOR4=",
"attestation_type": "none",
"authenticator": {
"aaguid": "AAAAAAAAAAAAAAAAAAAAAA==",
"sign_count": 4,
"clone_warning": false
},
"display_name": "asdf",
"added_at": "2022-02-28T16:40:39Z",
"is_passwordless": false,
"user_handle": "4d64fa08-20fc-450d-bebd-ebd7c7b6e249"
}
]
},
"version": 1,
"created_at": "0001-01-01T00:00:00Z",
"updated_at": "0001-01-01T00:00:00Z"
}
},
"schema_id": "",
"schema_url": "",
"state": "",
"traits": null,
"created_at": "0001-01-01T00:00:00Z",
"updated_at": "0001-01-01T00:00:00Z"
}
54 changes: 54 additions & 0 deletions credentialmigrate/migrate.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package credentialmigrate

import (
"encoding/json"
"github.com/ory/kratos/identity"
"github.com/ory/kratos/selfservice/strategy/webauthn"
"github.com/pkg/errors"
)

// UpgradeWebAuthnCredential migrates a webauthn credential from an older version to a newer version.
func UpgradeWebAuthnCredential(i *identity.Identity, ic *identity.Credentials, c *webauthn.CredentialsConfig) {
if ic.Version == 0 {
for k := range c.Credentials {
c.Credentials[k].UserHandle = i.ID.String()

// We do not set c.IsPasswordless as it defaults to false anyways, which is the correct migration .
}

ic.Version = 1
}
}

func UpgradeWebAuthnCredentials(i *identity.Identity, c *identity.Credentials) error {
if c.Type != identity.CredentialsTypeWebAuthn {
return nil
}

var cred webauthn.CredentialsConfig
if err := json.Unmarshal(c.Config, &cred); err != nil {
return errors.WithStack(err)
}

UpgradeWebAuthnCredential(i, c, &cred)

updatedConf, err := json.Marshal(&cred)
if err != nil {
return errors.WithStack(err)
}

c.Config = updatedConf
return nil
}

// UpgradeCredentials migrates a set of older WebAuthn credentials to newer ones.
func UpgradeCredentials(i *identity.Identity) error {
for k := range i.Credentials {
c := i.Credentials[k]
if err := UpgradeWebAuthnCredentials(i, &c); err != nil {
return errors.WithStack(err)
}
i.Credentials[k] = c
}
return nil
}
68 changes: 68 additions & 0 deletions credentialmigrate/migrate_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package credentialmigrate

import (
_ "embed"
"github.com/gofrs/uuid"
"github.com/ory/kratos/identity"
"github.com/ory/x/snapshotx"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"testing"
)

//go:embed stub/webauthn/v0.json
var webAuthnV0 []byte

//go:embed stub/webauthn/v1.json
var webAuthnV1 []byte

func TestUpgradeCredentials(t *testing.T) {
t.Run("empty credentials", func(t *testing.T) {
i := &identity.Identity{}

err := UpgradeCredentials(i)
require.NoError(t, err)
wc := identity.WithCredentialsInJSON(*i)
snapshotx.SnapshotTExcept(t, &wc, nil)
})

identityID := uuid.FromStringOrNil("4d64fa08-20fc-450d-bebd-ebd7c7b6e249")
t.Run("type=webauthn", func(t *testing.T) {
t.Run("from=v0", func(t *testing.T) {
i := &identity.Identity{
ID: identityID,
Credentials: map[identity.CredentialsType]identity.Credentials{
identity.CredentialsTypeWebAuthn: {
Identifiers: []string{"4d64fa08-20fc-450d-bebd-ebd7c7b6e249"},
Type: identity.CredentialsTypeWebAuthn,
Version: 0,
Config: webAuthnV0,
}},
}

require.NoError(t, UpgradeCredentials(i))
wc := identity.WithCredentialsInJSON(*i)
snapshotx.SnapshotTExcept(t, &wc, nil)

assert.Equal(t, 1, i.Credentials[identity.CredentialsTypeWebAuthn].Version)
})

t.Run("from=v1", func(t *testing.T) {
i := &identity.Identity{
ID: identityID,
Credentials: map[identity.CredentialsType]identity.Credentials{
identity.CredentialsTypeWebAuthn: {
Type: identity.CredentialsTypeWebAuthn,
Version: 1,
Config: webAuthnV1,
}},
}

require.NoError(t, UpgradeCredentials(i))
wc := identity.WithCredentialsInJSON(*i)
snapshotx.SnapshotTExcept(t, &wc, nil)

assert.Equal(t, 1, i.Credentials[identity.CredentialsTypeWebAuthn].Version)
})
})
}
16 changes: 16 additions & 0 deletions credentialmigrate/stub/webauthn/v0.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"credentials": [
{
"id": "HQ4LaIJ9NiqS1r0CQpWY+K0gMvhOq4yk5BHuO/YlitcurSpBK7weDXOvBcuN4lvn6DAmjGfmj/J/6bpOmtdT8Q==",
"public_key": "pQECAyYgASFYILAYFLoH1T8bQMSbPrNBCMMS5U7OFWRwv2U+GkAoiBADIlggBv+8ni7XVZYBB8ufMbP/d9fDxbmOkVVHOgcJifnoOR4=",
"attestation_type": "none",
"authenticator": {
"aaguid": "AAAAAAAAAAAAAAAAAAAAAA==",
"sign_count": 4,
"clone_warning": false
},
"display_name": "asdf",
"added_at": "2022-02-28T16:40:39Z"
}
]
}
18 changes: 18 additions & 0 deletions credentialmigrate/stub/webauthn/v1.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"credentials": [
{
"id": "HQ4LaIJ9NiqS1r0CQpWY+K0gMvhOq4yk5BHuO/YlitcurSpBK7weDXOvBcuN4lvn6DAmjGfmj/J/6bpOmtdT8Q==",
"public_key": "pQECAyYgASFYILAYFLoH1T8bQMSbPrNBCMMS5U7OFWRwv2U+GkAoiBADIlggBv+8ni7XVZYBB8ufMbP/d9fDxbmOkVVHOgcJifnoOR4=",
"attestation_type": "none",
"authenticator": {
"aaguid": "AAAAAAAAAAAAAAAAAAAAAA==",
"sign_count": 4,
"clone_warning": false
},
"display_name": "asdf",
"added_at": "2022-02-28T16:40:39Z",
"is_passwordless": true,
"user_handle": "4d64fa08-20fc-450d-bebd-ebd7c7b6e249"
}
]
}

0 comments on commit 77afc6f

Please sign in to comment.