Skip to content

Commit

Permalink
feat(totp): support account name setting from schema
Browse files Browse the repository at this point in the history
  • Loading branch information
aeneasr committed Oct 19, 2021
1 parent 0a7c812 commit 19a6bcc
Show file tree
Hide file tree
Showing 9 changed files with 78 additions and 6 deletions.
4 changes: 2 additions & 2 deletions driver/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import (
"crypto/tls"
"encoding/json"
"fmt"
"io"
"github.com/duo-labs/webauthn/protocol"
"io"
"net"
"net/http"
"net/url"
Expand Down Expand Up @@ -1010,7 +1010,7 @@ func (p *Config) WebAuthnConfig() *webauthn.Config {
RPOrigin: p.p.String(ViperKeyWebAuthnRPOrigin),
RPIcon: p.p.String(ViperKeyWebAuthnRPIcon),
AuthenticatorSelection: protocol.AuthenticatorSelection{
UserVerification: protocol.VerificationDiscouraged,
UserVerification: protocol.VerificationDiscouraged,
},
}
}
Expand Down
3 changes: 3 additions & 0 deletions schema/extension.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ type (
Password struct {
Identifier bool `json:"identifier"`
} `json:"password"`
TOTP struct {
AccountName bool `json:"account_name"`
} `json:"totp"`
} `json:"credentials"`
Verification struct {
Via string `json:"via"`
Expand Down
7 changes: 6 additions & 1 deletion schema/extension_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,17 @@ import (
)

type extensionStub struct {
identifiers []string
identifiers []string
accountNames []string
}

func (r *extensionStub) Run(ctx jsonschema.ValidationContext, config ExtensionConfig, value interface{}) error {
if config.Credentials.Password.Identifier {
r.identifiers = append(r.identifiers, fmt.Sprintf("%s", value))
}
if config.Credentials.TOTP.AccountName {
r.accountNames = append(r.accountNames, fmt.Sprintf("%s", value))
}
return nil
}

Expand Down Expand Up @@ -60,6 +64,7 @@ func TestExtensionRunner(t *testing.T) {
}

assert.EqualValues(t, tc.expect, r.identifiers)
assert.EqualValues(t, tc.expect, r.accountNames)
})
}
})
Expand Down
3 changes: 3 additions & 0 deletions schema/stub/extension/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
"credentials": {
"password": {
"identifier": true
},
"totp": {
"account_name": true
}
}
}
Expand Down
3 changes: 3 additions & 0 deletions schema/stub/extension/schema.nested.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
"credentials": {
"password": {
"identifier": true
},
"totp": {
"account_name": true
}
}
}
Expand Down
31 changes: 31 additions & 0 deletions selfservice/strategy/totp/schema_extension.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package totp

import (
"fmt"
"sync"

"github.com/ory/jsonschema/v3"
"github.com/ory/kratos/schema"
)

type SchemaExtension struct {
AccountName string
l sync.Mutex
}

func NewSchemaExtension(fallback string) *SchemaExtension {
return &SchemaExtension{AccountName: fallback}
}

func (r *SchemaExtension) Run(_ jsonschema.ValidationContext, s schema.ExtensionConfig, value interface{}) error {
r.l.Lock()
defer r.l.Unlock()
if s.Credentials.TOTP.AccountName {
r.AccountName = fmt.Sprintf("%s", value)
}
return nil
}

func (r *SchemaExtension) Finish() error {
return nil
}
5 changes: 4 additions & 1 deletion selfservice/strategy/totp/settings.go
Original file line number Diff line number Diff line change
Expand Up @@ -249,8 +249,11 @@ func (s *Strategy) PopulateSettingsMethod(r *http.Request, id *identity.Identity
if hasTOTP {
f.UI.Nodes.Upsert(NewUnlinkTOTPNode())
} else {
e := NewSchemaExtension(id.ID.String())
_ = s.d.IdentityValidator().ValidateWithRunner(r.Context(), id, e)

// No TOTP set up yet, add nodes allowing us to add it.
key, err := NewKey(r.Context(), id.ID.String(), s.d)
key, err := NewKey(r.Context(), e.AccountName, s.d)
if err != nil {
return err
}
Expand Down
5 changes: 3 additions & 2 deletions selfservice/strategy/totp/settings_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ func TestCompleteSettings(t *testing.T) {

conf.MustSet(config.ViperKeySelfServiceSettingsPrivilegedAuthenticationAfter, "1m")

conf.MustSet(config.ViperKeyDefaultIdentitySchemaURL, "file://./stub/login.schema.json")
conf.MustSet(config.ViperKeyDefaultIdentitySchemaURL, "file://./stub/settings.schema.json")
conf.MustSet(config.ViperKeySecretsDefault, []string{"not-a-secure-session-key"})

t.Run("case=device unlinking is available when identity has totp", func(t *testing.T) {
Expand Down Expand Up @@ -297,13 +297,14 @@ func TestCompleteSettings(t *testing.T) {

t.Run("type=set up TOTP device", func(t *testing.T) {
checkIdentity := func(t *testing.T, id *identity.Identity, key string) {
_, cred, err := reg.PrivilegedIdentityPool().FindByCredentialsIdentifier(context.Background(), identity.CredentialsTypeTOTP, id.ID.String())
i, cred, err := reg.PrivilegedIdentityPool().FindByCredentialsIdentifier(context.Background(), identity.CredentialsTypeTOTP, id.ID.String())
require.NoError(t, err)
var c totp.CredentialsConfig
require.NoError(t, json.Unmarshal(cred.Config, &c))
actual, err := otp.NewKeyFromURL(c.TOTPURL)
require.NoError(t, err)
assert.Equal(t, key, actual.Secret())
assert.Contains(t, c.TOTPURL, gjson.GetBytes(i.Traits, "subject").String())
}

run := func(t *testing.T, isAPI, isSPA bool, id *identity.Identity, hc *http.Client, f *kratos.SelfServiceSettingsFlow) {
Expand Down
23 changes: 23 additions & 0 deletions selfservice/strategy/totp/stub/settings.schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"$id": "https://example.com/person.schema.json",
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Person",
"type": "object",
"properties": {
"traits": {
"type": "object",
"properties": {
"subject": {
"type": "string",
"ory.sh/kratos": {
"credentials": {
"totp": {
"account_name": true
}
}
}
}
}
}
}
}

0 comments on commit 19a6bcc

Please sign in to comment.