diff --git a/cmd/identities/get_test.go b/cmd/identities/get_test.go index 911da181e28f..fcbd3309902c 100644 --- a/cmd/identities/get_test.go +++ b/cmd/identities/get_test.go @@ -7,8 +7,6 @@ import ( "testing" "github.com/ory/kratos/cmd/identities" - "github.com/ory/kratos/selfservice/strategy/oidc" - "github.com/ory/x/assertx" "github.com/ory/kratos/x" @@ -55,7 +53,7 @@ func TestGetCmd(t *testing.T) { t.Run("case=gets a single identity with oidc credentials", func(t *testing.T) { applyCredentials := func(identifier, accessToken, refreshToken, idToken string, encrypt bool) identity.Credentials { - toJson := func(c oidc.CredentialsConfig) []byte { + toJson := func(c identity.CredentialsOIDC) []byte { out, err := json.Marshal(&c) require.NoError(t, err) return out @@ -69,7 +67,7 @@ func TestGetCmd(t *testing.T) { return identity.Credentials{ Type: identity.CredentialsTypeOIDC, Identifiers: []string{"bar:" + identifier}, - Config: toJson(oidc.CredentialsConfig{Providers: []oidc.ProviderCredentialsConfig{ + Config: toJson(identity.CredentialsOIDC{Providers: []identity.CredentialsOIDCProvider{ { Subject: "foo", Provider: "bar", diff --git a/identity/credentials_oidc.go b/identity/credentials_oidc.go new file mode 100644 index 000000000000..406643fac636 --- /dev/null +++ b/identity/credentials_oidc.go @@ -0,0 +1,57 @@ +package identity + +import ( + "bytes" + "encoding/json" + "fmt" + + "github.com/pkg/errors" + + "github.com/ory/kratos/x" +) + +// CredentialsOIDC is contains the configuration for credentials of the type oidc. +// +// swagger:model identityCredentialsOidc +type CredentialsOIDC struct { + Providers []CredentialsOIDCProvider `json:"providers"` +} + +// CredentialsOIDCProvider is contains a specific OpenID COnnect credential for a particular connection (e.g. Google). +// +// swagger:model identityCredentialsOidcProvider +type CredentialsOIDCProvider struct { + Subject string `json:"subject"` + Provider string `json:"provider"` + InitialIDToken string `json:"initial_id_token"` + InitialAccessToken string `json:"initial_access_token"` + InitialRefreshToken string `json:"initial_refresh_token"` +} + +// NewCredentialsOIDC creates a new OIDC credential. +func NewCredentialsOIDC(idToken, accessToken, refreshToken, provider, subject string) (*Credentials, error) { + var b bytes.Buffer + if err := json.NewEncoder(&b).Encode(CredentialsOIDC{ + Providers: []CredentialsOIDCProvider{ + { + Subject: subject, + Provider: provider, + InitialIDToken: idToken, + InitialAccessToken: accessToken, + InitialRefreshToken: refreshToken, + }}, + }); err != nil { + return nil, errors.WithStack(x.PseudoPanic. + WithDebugf("Unable to encode password options to JSON: %s", err)) + } + + return &Credentials{ + Type: CredentialsTypeOIDC, + Identifiers: []string{OIDCUniqueID(provider, subject)}, + Config: b.Bytes(), + }, nil +} + +func OIDCUniqueID(provider, subject string) string { + return fmt.Sprintf("%s:%s", provider, subject) +} diff --git a/identity/credentials_password.go b/identity/credentials_password.go new file mode 100644 index 000000000000..9a9783e56ddb --- /dev/null +++ b/identity/credentials_password.go @@ -0,0 +1,9 @@ +package identity + +// CredentialsPassword is contains the configuration for credentials of the type password. +// +// swagger:model identityCredentialsPassword +type CredentialsPassword struct { + // HashedPassword is a hash-representation of the password. + HashedPassword string `json:"hashed_password"` +} diff --git a/identity/handler.go b/identity/handler.go index 92a9adcf6039..bd9d4f3dd1e2 100644 --- a/identity/handler.go +++ b/identity/handler.go @@ -202,12 +202,58 @@ type AdminCreateIdentityBody struct { // required: true Traits json.RawMessage `json:"traits"` + // Credentials represents all credentials that can be used for authenticating this identity. + // + // Use this structure to import credentials for a user. + Credentials *AdminIdentityImportCredentials `json:"credentials"` + + // VerifiableAddresses contains all the addresses that can be verified by the user. + // + // Use this structure to import verified addresses for an identity. Please keep in mind + // that the address needs to be represented in the Identity Schema or this field will be overwritten + // on the next identity update. + VerifiableAddresses []VerifiableAddress `json:"verifiable_addresses"` + + // RecoveryAddresses contains all the addresses that can be used to recover an identity. + // + // Use this structure to import recovery addresses for an identity. Please keep in mind + // that the address needs to be represented in the Identity Schema or this field will be overwritten + // on the next identity update. + RecoveryAddresses []RecoveryAddress `json:"recovery_addresses"` + // State is the identity's state. // // required: false State State `json:"state"` } +// swagger:model adminIdentityImportCredentials +type AdminIdentityImportCredentials struct { + // Password if set will import a password credential. + Password *AdminIdentityImportCredentialsPassword `json:"password,omitempty"` + + // OIDC if set will import an OIDC credential. + OIDC *AdminIdentityImportCredentialsOIDC `json:"oidc,omitempty"` +} + +// swagger:model AdminCreateIdentityImportCredentialsPassword +type AdminIdentityImportCredentialsPassword struct { + // The hashed password in [PHC format]( https://www.ory.sh/docs/kratos/concepts/credentials/username-email-password#hashed-password-format) + HashedPassword string `json:"hashed_password"` + + // The password in plain text if no hash is available. + Password string `json:"password"` +} + +// swagger:model AdminCreateIdentityImportCredentialsOIDC +type AdminIdentityImportCredentialsOIDC struct { + // The subject (`sub`) of the OpenID Connect connection. Usually the `sub` field of the ID Token. + Subject string `json:"subject"` + + // The OpenID Connect provider to link the subject to. Usually something like `google` or `github`. + Provider string `json:"provider"` +} + // swagger:route POST /identities v0alpha2 adminCreateIdentity // // Create an Identity @@ -249,7 +295,19 @@ func (h *Handler) create(w http.ResponseWriter, r *http.Request, _ httprouter.Pa } state = cr.State } - i := &Identity{SchemaID: cr.SchemaID, Traits: []byte(cr.Traits), State: state, StateChangedAt: &stateChangedAt} + + i := &Identity{ + SchemaID: cr.SchemaID, + Traits: []byte(cr.Traits), + State: state, + StateChangedAt: &stateChangedAt, + //Credentials: cr.Credentials, + VerifiableAddresses: cr.VerifiableAddresses, + RecoveryAddresses: cr.RecoveryAddresses, + } + //i.Traits = identity.Traits(p.Traits) + //i.SetCredentials(s.ID(), identity.Credentials{Type: s.ID(), Identifiers: []string{}, Config: co}) + if err := h.r.IdentityManager().Create(r.Context(), i); err != nil { h.r.Writer().WriteError(w, r, err) return diff --git a/identity/handler_test.go b/identity/handler_test.go index d3d7aae1ebc5..03d5cc7ef0c8 100644 --- a/identity/handler_test.go +++ b/identity/handler_test.go @@ -11,8 +11,6 @@ import ( "testing" "time" - "github.com/ory/kratos/selfservice/strategy/oidc" - "github.com/bxcodec/faker/v3" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -174,7 +172,7 @@ func TestHandler(t *testing.T) { } iId := x.NewUUID() - toJson := func(c oidc.CredentialsConfig) []byte { + toJson := func(c identity.CredentialsOIDC) []byte { out, err := json.Marshal(&c) require.NoError(t, err) return out @@ -186,7 +184,7 @@ func TestHandler(t *testing.T) { identity.CredentialsTypeOIDC: { Type: identity.CredentialsTypeOIDC, Identifiers: []string{"bar:" + identifier}, - Config: toJson(oidc.CredentialsConfig{Providers: []oidc.ProviderCredentialsConfig{ + Config: toJson(identity.CredentialsOIDC{Providers: []identity.CredentialsOIDCProvider{ { Subject: "foo", Provider: "bar", diff --git a/identity/identity.go b/identity/identity.go index 2b026303106c..472416cb941d 100644 --- a/identity/identity.go +++ b/identity/identity.go @@ -189,6 +189,23 @@ func (i *Identity) SetCredentials(t CredentialsType, c Credentials) { i.Credentials[t] = c } +func (i *Identity) SetCredentialsWithConfig(t CredentialsType, c Credentials, conf interface{}) (err error) { + i.lock().Lock() + defer i.lock().Unlock() + if i.Credentials == nil { + i.Credentials = make(map[CredentialsType]Credentials) + } + + c.Config, err = json.Marshal(conf) + if err != nil { + return errors.WithStack(herodot.ErrInternalServerError.WithReasonf("Unable to encode %s credentials to JSON: %s", t, err)) + } + + c.Type = t + i.Credentials[t] = c + return nil +} + func (i *Identity) DeleteCredentialsType(t CredentialsType) { i.lock().Lock() defer i.lock().Unlock() diff --git a/selfservice/strategy/oidc/strategy.go b/selfservice/strategy/oidc/strategy.go index c2a09b2f12c3..858820b14e02 100644 --- a/selfservice/strategy/oidc/strategy.go +++ b/selfservice/strategy/oidc/strategy.go @@ -4,7 +4,6 @@ import ( "bytes" "context" "encoding/json" - "fmt" "net/http" "path/filepath" "strings" @@ -116,7 +115,7 @@ type authCodeContainer struct { func (s *Strategy) CountActiveCredentials(cc map[identity.CredentialsType]identity.Credentials) (count int, err error) { for _, c := range cc { if c.Type == s.ID() && gjson.ValidBytes(c.Config) { - var conf CredentialsConfig + var conf identity.CredentialsOIDC if err = json.Unmarshal(c.Config, &conf); err != nil { return 0, errors.WithStack(err) } @@ -366,10 +365,6 @@ func (s *Strategy) handleCallback(w http.ResponseWriter, r *http.Request, ps htt } } -func uid(provider, subject string) string { - return fmt.Sprintf("%s:%s", provider, subject) -} - func (s *Strategy) populateMethod(r *http.Request, c *container.Container, message func(provider string) *text.Message) error { conf, err := s.Config(r.Context()) if err != nil { diff --git a/selfservice/strategy/oidc/strategy_login.go b/selfservice/strategy/oidc/strategy_login.go index 1199915b3761..a1bbdacbcb6b 100644 --- a/selfservice/strategy/oidc/strategy_login.go +++ b/selfservice/strategy/oidc/strategy_login.go @@ -72,7 +72,7 @@ type SubmitSelfServiceLoginFlowWithOidcMethodBody struct { } func (s *Strategy) processLogin(w http.ResponseWriter, r *http.Request, a *login.Flow, token *oauth2.Token, claims *Claims, provider Provider, container *authCodeContainer) (*registration.Flow, error) { - i, c, err := s.d.PrivilegedIdentityPool().FindByCredentialsIdentifier(r.Context(), identity.CredentialsTypeOIDC, uid(provider.Config().ID, claims.Subject)) + i, c, err := s.d.PrivilegedIdentityPool().FindByCredentialsIdentifier(r.Context(), identity.CredentialsTypeOIDC, identity.OIDCUniqueID(provider.Config().ID, claims.Subject)) if err != nil { if errors.Is(err, sqlcon.ErrNoRows) { // If no account was found we're "manually" creating a new registration flow and redirecting the browser @@ -104,7 +104,7 @@ func (s *Strategy) processLogin(w http.ResponseWriter, r *http.Request, a *login return nil, s.handleError(w, r, a, provider.Config().ID, nil, err) } - var o CredentialsConfig + var o identity.CredentialsOIDC if err := json.NewDecoder(bytes.NewBuffer(c.Config)).Decode(&o); err != nil { return nil, s.handleError(w, r, a, provider.Config().ID, nil, errors.WithStack(herodot.ErrInternalServerError.WithReason("The password credentials could not be decoded properly").WithDebug(err.Error()))) } diff --git a/selfservice/strategy/oidc/strategy_registration.go b/selfservice/strategy/oidc/strategy_registration.go index 3dcc82e76280..dd4effa9f79e 100644 --- a/selfservice/strategy/oidc/strategy_registration.go +++ b/selfservice/strategy/oidc/strategy_registration.go @@ -154,7 +154,7 @@ func (s *Strategy) Register(w http.ResponseWriter, r *http.Request, f *registrat } func (s *Strategy) processRegistration(w http.ResponseWriter, r *http.Request, a *registration.Flow, token *oauth2.Token, claims *Claims, provider Provider, container *authCodeContainer) (*login.Flow, error) { - if _, _, err := s.d.PrivilegedIdentityPool().FindByCredentialsIdentifier(r.Context(), identity.CredentialsTypeOIDC, uid(provider.Config().ID, claims.Subject)); err == nil { + if _, _, err := s.d.PrivilegedIdentityPool().FindByCredentialsIdentifier(r.Context(), identity.CredentialsTypeOIDC, identity.OIDCUniqueID(provider.Config().ID, claims.Subject)); err == nil { // If the identity already exists, we should perform the login flow instead. // That will execute the "pre registration" hook which allows to e.g. disallow this flow. The registration @@ -254,7 +254,7 @@ func (s *Strategy) processRegistration(w http.ResponseWriter, r *http.Request, a return nil, s.handleError(w, r, a, provider.Config().ID, i.Traits, err) } - creds, err := NewCredentials(it, cat, crt, provider.Config().ID, claims.Subject) + creds, err := identity.NewCredentialsOIDC(it, cat, crt, provider.Config().ID, claims.Subject) if err != nil { return nil, s.handleError(w, r, a, provider.Config().ID, i.Traits, err) } diff --git a/selfservice/strategy/oidc/strategy_settings.go b/selfservice/strategy/oidc/strategy_settings.go index 70383c442a51..e9232568befc 100644 --- a/selfservice/strategy/oidc/strategy_settings.go +++ b/selfservice/strategy/oidc/strategy_settings.go @@ -78,7 +78,7 @@ func (s *Strategy) linkedProviders(ctx context.Context, r *http.Request, conf *C return nil, nil } - var available CredentialsConfig + var available identity.CredentialsOIDC if err := json.Unmarshal(creds.Config, &available); err != nil { return nil, errors.WithStack(err) } @@ -115,7 +115,7 @@ func (s *Strategy) linkedProviders(ctx context.Context, r *http.Request, conf *C } func (s *Strategy) linkableProviders(ctx context.Context, r *http.Request, conf *ConfigurationCollection, confidential *identity.Identity) ([]Provider, error) { - var available CredentialsConfig + var available identity.CredentialsOIDC creds, ok := confidential.GetCredentials(s.ID()) if ok { if err := json.Unmarshal(creds.Config, &available); err != nil { @@ -394,18 +394,18 @@ func (s *Strategy) linkProvider(w http.ResponseWriter, r *http.Request, ctxUpdat return s.handleSettingsError(w, r, ctxUpdate, p, err) } - var conf CredentialsConfig + var conf identity.CredentialsOIDC creds, err := i.ParseCredentials(s.ID(), &conf) if errors.Is(err, herodot.ErrNotFound) { var err error - if creds, err = NewCredentials(it, cat, crt, provider.Config().ID, claims.Subject); err != nil { + if creds, err = identity.NewCredentialsOIDC(it, cat, crt, provider.Config().ID, claims.Subject); err != nil { return s.handleSettingsError(w, r, ctxUpdate, p, err) } } else if err != nil { return s.handleSettingsError(w, r, ctxUpdate, p, err) } else { - creds.Identifiers = append(creds.Identifiers, uid(provider.Config().ID, claims.Subject)) - conf.Providers = append(conf.Providers, ProviderCredentialsConfig{ + creds.Identifiers = append(creds.Identifiers, identity.OIDCUniqueID(provider.Config().ID, claims.Subject)) + conf.Providers = append(conf.Providers, identity.CredentialsOIDCProvider{ Subject: claims.Subject, Provider: provider.Config().ID, InitialAccessToken: cat, InitialRefreshToken: crt, @@ -448,20 +448,20 @@ func (s *Strategy) unlinkProvider(w http.ResponseWriter, r *http.Request, ctxUpd return s.handleSettingsError(w, r, ctxUpdate, p, err) } - var cc CredentialsConfig + var cc identity.CredentialsOIDC creds, err := i.ParseCredentials(s.ID(), &cc) if err != nil { return s.handleSettingsError(w, r, ctxUpdate, p, errors.WithStack(UnknownConnectionValidationError)) } var found bool - var updatedProviders []ProviderCredentialsConfig + var updatedProviders []identity.CredentialsOIDCProvider var updatedIdentifiers []string for _, available := range availableProviders { if p.Unlink == available.Config().ID { for _, link := range cc.Providers { if link.Provider != p.Unlink { - updatedIdentifiers = append(updatedIdentifiers, uid(link.Provider, link.Subject)) + updatedIdentifiers = append(updatedIdentifiers, identity.OIDCUniqueID(link.Provider, link.Subject)) updatedProviders = append(updatedProviders, link) } else { found = true @@ -475,7 +475,7 @@ func (s *Strategy) unlinkProvider(w http.ResponseWriter, r *http.Request, ctxUpd } creds.Identifiers = updatedIdentifiers - creds.Config, err = json.Marshal(&CredentialsConfig{updatedProviders}) + creds.Config, err = json.Marshal(&identity.CredentialsOIDC{updatedProviders}) if err != nil { return s.handleSettingsError(w, r, ctxUpdate, p, errors.WithStack(err)) diff --git a/selfservice/strategy/oidc/strategy_settings_test.go b/selfservice/strategy/oidc/strategy_settings_test.go index e143dcfae4a6..f809d55009e8 100644 --- a/selfservice/strategy/oidc/strategy_settings_test.go +++ b/selfservice/strategy/oidc/strategy_settings_test.go @@ -212,7 +212,7 @@ func TestSettingsStrategy(t *testing.T) { actual, err := reg.PrivilegedIdentityPool().GetIdentityConfidential(context.Background(), iid) require.NoError(t, err) - var cc oidc.CredentialsConfig + var cc identity.CredentialsOIDC creds, err := actual.ParseCredentials(identity.CredentialsTypeOIDC, &cc) require.NoError(t, err) diff --git a/selfservice/strategy/oidc/strategy_test.go b/selfservice/strategy/oidc/strategy_test.go index f40f487b49c1..db5c18b53c04 100644 --- a/selfservice/strategy/oidc/strategy_test.go +++ b/selfservice/strategy/oidc/strategy_test.go @@ -501,7 +501,7 @@ func TestCountActiveCredentials(t *testing.T) { _, reg := internal.NewFastRegistryWithMocks(t) strategy := oidc.NewStrategy(reg) - toJson := func(c oidc.CredentialsConfig) []byte { + toJson := func(c identity.CredentialsOIDC) []byte { out, err := json.Marshal(&c) require.NoError(t, err) return out @@ -520,7 +520,7 @@ func TestCountActiveCredentials(t *testing.T) { { in: identity.CredentialsCollection{{ Type: strategy.ID(), - Config: toJson(oidc.CredentialsConfig{Providers: []oidc.ProviderCredentialsConfig{ + Config: toJson(identity.CredentialsOIDC{Providers: []identity.CredentialsOIDCProvider{ {Subject: "foo", Provider: "bar"}, }}), }}, @@ -529,7 +529,7 @@ func TestCountActiveCredentials(t *testing.T) { in: identity.CredentialsCollection{{ Type: strategy.ID(), Identifiers: []string{""}, - Config: toJson(oidc.CredentialsConfig{Providers: []oidc.ProviderCredentialsConfig{ + Config: toJson(identity.CredentialsOIDC{Providers: []identity.CredentialsOIDCProvider{ {Subject: "foo", Provider: "bar"}, }}), }}, @@ -538,7 +538,7 @@ func TestCountActiveCredentials(t *testing.T) { in: identity.CredentialsCollection{{ Type: strategy.ID(), Identifiers: []string{"bar:"}, - Config: toJson(oidc.CredentialsConfig{Providers: []oidc.ProviderCredentialsConfig{ + Config: toJson(identity.CredentialsOIDC{Providers: []identity.CredentialsOIDCProvider{ {Subject: "foo", Provider: "bar"}, }}), }}, @@ -547,7 +547,7 @@ func TestCountActiveCredentials(t *testing.T) { in: identity.CredentialsCollection{{ Type: strategy.ID(), Identifiers: []string{":foo"}, - Config: toJson(oidc.CredentialsConfig{Providers: []oidc.ProviderCredentialsConfig{ + Config: toJson(identity.CredentialsOIDC{Providers: []identity.CredentialsOIDCProvider{ {Subject: "foo", Provider: "bar"}, }}), }}, @@ -556,7 +556,7 @@ func TestCountActiveCredentials(t *testing.T) { in: identity.CredentialsCollection{{ Type: strategy.ID(), Identifiers: []string{"not-bar:foo"}, - Config: toJson(oidc.CredentialsConfig{Providers: []oidc.ProviderCredentialsConfig{ + Config: toJson(identity.CredentialsOIDC{Providers: []identity.CredentialsOIDCProvider{ {Subject: "foo", Provider: "bar"}, }}), }}, @@ -565,7 +565,7 @@ func TestCountActiveCredentials(t *testing.T) { in: identity.CredentialsCollection{{ Type: strategy.ID(), Identifiers: []string{"bar:not-foo"}, - Config: toJson(oidc.CredentialsConfig{Providers: []oidc.ProviderCredentialsConfig{ + Config: toJson(identity.CredentialsOIDC{Providers: []identity.CredentialsOIDCProvider{ {Subject: "foo", Provider: "bar"}, }}), }}, @@ -574,7 +574,7 @@ func TestCountActiveCredentials(t *testing.T) { in: identity.CredentialsCollection{{ Type: strategy.ID(), Identifiers: []string{"bar:foo"}, - Config: toJson(oidc.CredentialsConfig{Providers: []oidc.ProviderCredentialsConfig{ + Config: toJson(identity.CredentialsOIDC{Providers: []identity.CredentialsOIDCProvider{ {Subject: "foo", Provider: "bar"}, }}), }}, diff --git a/selfservice/strategy/oidc/types.go b/selfservice/strategy/oidc/types.go index 8952e2992a10..12c17c23c946 100644 --- a/selfservice/strategy/oidc/types.go +++ b/selfservice/strategy/oidc/types.go @@ -1,9 +1,6 @@ package oidc import ( - "bytes" - "encoding/json" - "github.com/ory/kratos/text" "github.com/ory/x/stringsx" @@ -12,48 +9,8 @@ import ( "github.com/ory/kratos/ui/node" "github.com/gofrs/uuid" - "github.com/pkg/errors" - - "github.com/ory/kratos/identity" - - "github.com/ory/kratos/x" ) -type CredentialsConfig struct { - Providers []ProviderCredentialsConfig `json:"providers"` -} - -func NewCredentials(idToken, accessToken, refreshToken, provider, subject string) (*identity.Credentials, error) { - var b bytes.Buffer - if err := json.NewEncoder(&b).Encode(CredentialsConfig{ - Providers: []ProviderCredentialsConfig{ - { - Subject: subject, - Provider: provider, - InitialIDToken: idToken, - InitialAccessToken: accessToken, - InitialRefreshToken: refreshToken, - }}, - }); err != nil { - return nil, errors.WithStack(x.PseudoPanic. - WithDebugf("Unable to encode password options to JSON: %s", err)) - } - - return &identity.Credentials{ - Type: identity.CredentialsTypeOIDC, - Identifiers: []string{uid(provider, subject)}, - Config: b.Bytes(), - }, nil -} - -type ProviderCredentialsConfig struct { - Subject string `json:"subject"` - Provider string `json:"provider"` - InitialIDToken string `json:"initial_id_token"` - InitialAccessToken string `json:"initial_access_token"` - InitialRefreshToken string `json:"initial_refresh_token"` -} - type FlowMethod struct { *container.Container } diff --git a/selfservice/strategy/password/login.go b/selfservice/strategy/password/login.go index 641b3fec182a..867560180f47 100644 --- a/selfservice/strategy/password/login.go +++ b/selfservice/strategy/password/login.go @@ -68,7 +68,7 @@ func (s *Strategy) Login(w http.ResponseWriter, r *http.Request, f *login.Flow, return nil, s.handleLoginError(w, r, f, &p, errors.WithStack(schema.NewInvalidCredentialsError())) } - var o CredentialsConfig + var o identity.CredentialsPassword d := json.NewDecoder(bytes.NewBuffer(c.Config)) if err := d.Decode(&o); err != nil { return nil, herodot.ErrInternalServerError.WithReason("The password credentials could not be decoded properly").WithDebug(err.Error()).WithWrap(err) @@ -101,7 +101,7 @@ func (s *Strategy) migratePasswordHash(ctx context.Context, identifier uuid.UUID if err != nil { return err } - co, err := json.Marshal(&CredentialsConfig{HashedPassword: string(hpw)}) + co, err := json.Marshal(&identity.CredentialsPassword{HashedPassword: string(hpw)}) if err != nil { return errors.Wrap(err, "unable to encode password configuration to JSON") } diff --git a/selfservice/strategy/password/login_test.go b/selfservice/strategy/password/login_test.go index 49b60e089be4..ec12de398945 100644 --- a/selfservice/strategy/password/login_test.go +++ b/selfservice/strategy/password/login_test.go @@ -20,8 +20,6 @@ import ( kratos "github.com/ory/kratos-client-go" "github.com/ory/kratos/hash" - "github.com/ory/kratos/selfservice/strategy/password" - "github.com/ory/x/assertx" "github.com/ory/x/errorsx" "github.com/ory/x/ioutilx" @@ -772,7 +770,7 @@ func TestCompleteLogin(t *testing.T) { // check if password hash algorithm is upgraded _, c, err := reg.PrivilegedIdentityPool().FindByCredentialsIdentifier(context.Background(), identity.CredentialsTypePassword, identifier) require.NoError(t, err) - var o password.CredentialsConfig + var o identity.CredentialsPassword require.NoError(t, json.NewDecoder(bytes.NewBuffer(c.Config)).Decode(&o)) assert.True(t, reg.Hasher().Understands([]byte(o.HashedPassword)), "%s", o.HashedPassword) assert.True(t, hash.IsBcryptHash([]byte(o.HashedPassword)), "%s", o.HashedPassword) diff --git a/selfservice/strategy/password/registration.go b/selfservice/strategy/password/registration.go index 0916b9fc4255..f449f9831c21 100644 --- a/selfservice/strategy/password/registration.go +++ b/selfservice/strategy/password/registration.go @@ -114,13 +114,10 @@ func (s *Strategy) Register(w http.ResponseWriter, r *http.Request, f *registrat return s.handleRegistrationError(w, r, f, &p, err) } - co, err := json.Marshal(&CredentialsConfig{HashedPassword: string(hpw)}) - if err != nil { - return s.handleRegistrationError(w, r, f, &p, errors.WithStack(herodot.ErrInternalServerError.WithReasonf("Unable to encode password options to JSON: %s", err))) - } - i.Traits = identity.Traits(p.Traits) - i.SetCredentials(s.ID(), identity.Credentials{Type: s.ID(), Identifiers: []string{}, Config: co}) + if err := i.SetCredentialsWithConfig(s.ID(), identity.Credentials{Type: s.ID(), Identifiers: []string{}}, &identity.CredentialsPassword{HashedPassword: string(hpw)}); err != nil { + return s.handleRegistrationError(w, r, f, &p, err) + } if err := s.validateCredentials(r.Context(), i, p.Password); err != nil { return s.handleRegistrationError(w, r, f, &p, err) diff --git a/selfservice/strategy/password/settings.go b/selfservice/strategy/password/settings.go index 45d6aa60d4db..08b5271e6ba5 100644 --- a/selfservice/strategy/password/settings.go +++ b/selfservice/strategy/password/settings.go @@ -1,7 +1,6 @@ package password import ( - "encoding/json" "net/http" "time" @@ -14,7 +13,6 @@ import ( "github.com/gofrs/uuid" "github.com/pkg/errors" - "github.com/ory/herodot" "github.com/ory/kratos/identity" "github.com/ory/kratos/schema" "github.com/ory/kratos/selfservice/flow" @@ -124,11 +122,6 @@ func (s *Strategy) continueSettingsFlow( return err } - co, err := json.Marshal(&CredentialsConfig{HashedPassword: string(hpw)}) - if err != nil { - return errors.WithStack(herodot.ErrInternalServerError.WithReasonf("Unable to encode password options to JSON: %s", err)) - } - i, err := s.d.PrivilegedIdentityPool().GetIdentityConfidential(r.Context(), ctxUpdate.Session.Identity.ID) if err != nil { return err @@ -141,8 +134,10 @@ func (s *Strategy) continueSettingsFlow( Identifiers: []string{x.NewUUID().String()}} } - c.Config = co - i.SetCredentials(s.ID(), *c) + if err := i.SetCredentialsWithConfig(s.ID(), *c, &identity.CredentialsPassword{HashedPassword: string(hpw)}); err != nil { + return err + } + if err := s.validateCredentials(r.Context(), i, p.Password); err != nil { return err } diff --git a/selfservice/strategy/password/strategy.go b/selfservice/strategy/password/strategy.go index 2b5f2211ee63..8608fbe3e15e 100644 --- a/selfservice/strategy/password/strategy.go +++ b/selfservice/strategy/password/strategy.go @@ -81,7 +81,7 @@ func NewStrategy(d registrationStrategyDependencies) *Strategy { func (s *Strategy) CountActiveCredentials(cc map[identity.CredentialsType]identity.Credentials) (count int, err error) { for _, c := range cc { if c.Type == s.ID() && len(c.Config) > 0 { - var conf CredentialsConfig + var conf identity.CredentialsPassword if err = json.Unmarshal(c.Config, &conf); err != nil { return 0, errors.WithStack(err) } diff --git a/selfservice/strategy/password/types.go b/selfservice/strategy/password/types.go index e8d8d924310c..4471b2e5e62d 100644 --- a/selfservice/strategy/password/types.go +++ b/selfservice/strategy/password/types.go @@ -4,12 +4,6 @@ import ( "github.com/ory/kratos/ui/container" ) -// CredentialsConfig is the struct that is being used as part of the identity credentials. -type CredentialsConfig struct { - // HashedPassword is a hash-representation of the password. - HashedPassword string `json:"hashed_password"` -} - // submitSelfServiceLoginFlowWithPasswordMethodBody is used to decode the login form payload. // // swagger:model submitSelfServiceLoginFlowWithPasswordMethodBody