Skip to content

Commit

Permalink
feat: make password settings method API-able
Browse files Browse the repository at this point in the history
  • Loading branch information
aeneasr committed Aug 25, 2020
1 parent 8c1d1f7 commit 0cf6027
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 28 deletions.
14 changes: 14 additions & 0 deletions selfservice/strategy/password/.schema/settings.schema.json
@@ -0,0 +1,14 @@
{
"$id": "https://schemas.ory.sh/kratos/selfservice/strategy/password/settings.schema.json",
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"required": [
"password"
],
"properties": {
"password": {
"type": "string",
"minLength": 1
}
}
}
7 changes: 6 additions & 1 deletion selfservice/strategy/password/schema.go
Expand Up @@ -5,7 +5,7 @@ import (
)

var schemas = packr.New(".schema", ".schema")
var loginSchema, registrationSchema []byte
var loginSchema, registrationSchema, settingsSchema []byte

func init() {
var err error
Expand All @@ -18,4 +18,9 @@ func init() {
if err != nil {
panic(err)
}

settingsSchema, err = schemas.Find("settings.schema.json")
if err != nil {
panic(err)
}
}
72 changes: 45 additions & 27 deletions selfservice/strategy/password/settings.go
Expand Up @@ -8,55 +8,56 @@ import (

"github.com/gofrs/uuid"
"github.com/julienschmidt/httprouter"
"github.com/pkg/errors"

"github.com/ory/herodot"
"github.com/ory/x/decoderx"
"github.com/ory/x/urlx"
"github.com/pkg/errors"

"github.com/ory/kratos/identity"
"github.com/ory/kratos/schema"
"github.com/ory/kratos/selfservice/flow"
"github.com/ory/kratos/selfservice/flow/settings"
"github.com/ory/kratos/selfservice/form"
"github.com/ory/kratos/x"
)

const (
SettingsPath = "/self-service/browser/flows/settings/strategies/password"
RouteSettings = "/self-service/settings/methods/password"
)

func (s *Strategy) RegisterSettingsRoutes(router *x.RouterPublic) {
router.POST(SettingsPath, s.submitSettingsFlow)
router.GET(SettingsPath, s.submitSettingsFlow)
router.POST(RouteSettings, s.submitSettingsFlow)
router.GET(RouteSettings, s.submitSettingsFlow)
}

func (s *Strategy) SettingsStrategyID() string {
return identity.CredentialsTypePassword.String()
}

// swagger:model completeSelfServiceBrowserSettingsPasswordFlowPayload
type completeSelfServiceBrowserSettingsPasswordFlowPayload struct {
// swagger:parameters completeSelfServiceSettingsFlowWithPasswordMethod
type completeSelfServiceSettingsFlowWithPasswordMethod struct {
// Password is the updated password
//
// type: string
// in: body
// required: true
Password string `json:"password"`

// RequestID is request ID.
// Flow is flow ID.
//
// in: query
RequestID string `json:"request_id"`
Flow string `json:"flow"`
}

func (p *completeSelfServiceBrowserSettingsPasswordFlowPayload) GetRequestID() uuid.UUID {
return x.ParseUUID(p.RequestID)
func (p *completeSelfServiceSettingsFlowWithPasswordMethod) GetFlowID() uuid.UUID {
return x.ParseUUID(p.Flow)
}

func (p *completeSelfServiceBrowserSettingsPasswordFlowPayload) SetRequestID(rid uuid.UUID) {
p.RequestID = rid.String()
func (p *completeSelfServiceSettingsFlowWithPasswordMethod) SetFlowID(rid uuid.UUID) {
p.Flow = rid.String()
}

// swagger:route POST /self-service/browser/flows/settings/strategies/password public completeSelfServiceSettingsFlowWithPasswordMethod
// swagger:route POST /self-service/settings/methods/password public completeSelfServiceSettingsFlowWithPasswordMethod
//
// Complete the browser-based settings flow for the password strategy
//
Expand All @@ -77,7 +78,7 @@ func (p *completeSelfServiceBrowserSettingsPasswordFlowPayload) SetRequestID(rid
// 302: emptyResponse
// 500: genericError
func (s *Strategy) submitSettingsFlow(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
var p completeSelfServiceBrowserSettingsPasswordFlowPayload
var p completeSelfServiceSettingsFlowWithPasswordMethod
ctxUpdate, err := settings.PrepareUpdate(s.d, w, r, settings.ContinuityKey(s.SettingsStrategyID()), &p)
if errors.Is(err, settings.ErrContinuePreviousAction) {
s.continueSettingsFlow(w, r, ctxUpdate, &p)
Expand All @@ -87,14 +88,31 @@ func (s *Strategy) submitSettingsFlow(w http.ResponseWriter, r *http.Request, ps
return
}

p.RequestID = ctxUpdate.Flow.ID.String()
p.Password = r.PostFormValue("password")
if err := s.decodeSettingsFlow(r, &p); err != nil {
s.handleSettingsError(w, r, ctxUpdate, &p, err)
return
}

// This does not come from the payload!
p.Flow = ctxUpdate.Flow.ID.String()
s.continueSettingsFlow(w, r, ctxUpdate, &p)
}

func (s *Strategy) decodeSettingsFlow(r *http.Request, dest interface{}) error {
compiler, err := decoderx.HTTPRawJSONSchemaCompiler(settingsSchema)
if err != nil {
return errors.WithStack(err)
}

return decoderx.NewHTTP().Decode(r, dest, compiler,
decoderx.HTTPDecoderSetValidatePayloads(false),
decoderx.HTTPDecoderJSONFollowsFormFormat(),
)
}

func (s *Strategy) continueSettingsFlow(
w http.ResponseWriter, r *http.Request,
ctxUpdate *settings.UpdateContext, p *completeSelfServiceBrowserSettingsPasswordFlowPayload,
ctxUpdate *settings.UpdateContext, p *completeSelfServiceSettingsFlowWithPasswordMethod,
) {
if ctxUpdate.Session.AuthenticatedAt.Add(s.c.SelfServiceFlowSettingsPrivilegedSessionMaxAge()).Before(time.Now()) {
s.handleSettingsError(w, r, ctxUpdate, p, errors.WithStack(settings.ErrRequestNeedsReAuthentication))
Expand Down Expand Up @@ -146,12 +164,9 @@ func (s *Strategy) continueSettingsFlow(
}

func (s *Strategy) PopulateSettingsMethod(r *http.Request, _ *identity.Identity, f *settings.Flow) error {
hf := &form.HTMLForm{
Action: urlx.CopyWithQuery(urlx.AppendPaths(s.c.SelfPublicURL(), SettingsPath),
url.Values{"flow": {f.ID.String()}},
).String(),
Fields: form.Fields{{Name: "password", Type: "password", Required: true}}, Method: "POST",
}
hf := &form.HTMLForm{Action: urlx.CopyWithQuery(urlx.AppendPaths(s.c.SelfPublicURL(), RouteSettings),
url.Values{"flow": {f.ID.String()}}).String(), Fields: form.Fields{{Name: "password",
Type: "password", Required: true}}, Method: "POST"}
hf.SetCSRF(s.d.GenerateCSRFToken(r))

f.Methods[string(s.ID())] = &settings.FlowMethod{
Expand All @@ -161,19 +176,22 @@ func (s *Strategy) PopulateSettingsMethod(r *http.Request, _ *identity.Identity,
return nil
}

func (s *Strategy) handleSettingsError(w http.ResponseWriter, r *http.Request, ctxUpdate *settings.UpdateContext, p *completeSelfServiceBrowserSettingsPasswordFlowPayload, err error) {
if errors.Is(err, settings.ErrRequestNeedsReAuthentication) {
func (s *Strategy) handleSettingsError(w http.ResponseWriter, r *http.Request, ctxUpdate *settings.UpdateContext, p *completeSelfServiceSettingsFlowWithPasswordMethod, err error) {
// Do not pause flow if the flow type is an API flow as we can't save cookies in those flows.
if errors.Is(err, settings.ErrRequestNeedsReAuthentication) && ctxUpdate.Flow != nil && ctxUpdate.Flow.Type == flow.TypeBrowser {
if err := s.d.ContinuityManager().Pause(r.Context(), w, r,
settings.ContinuityKey(s.SettingsStrategyID()), settings.ContinuityOptions(p, ctxUpdate.Session.Identity)...); err != nil {
s.d.SettingsFlowErrorHandler().WriteFlowError(w, r, s.SettingsStrategyID(), ctxUpdate.Flow, ctxUpdate.Session.Identity, err)
return
}
}

var id *identity.Identity
if ctxUpdate.Flow != nil {
ctxUpdate.Flow.Methods[s.SettingsStrategyID()].Config.Reset()
ctxUpdate.Flow.Methods[s.SettingsStrategyID()].Config.SetCSRF(s.d.GenerateCSRFToken(r))
id = ctxUpdate.Session.Identity
}

s.d.SettingsFlowErrorHandler().WriteFlowError(w, r, s.SettingsStrategyID(), ctxUpdate.Flow, ctxUpdate.Session.Identity, err)
s.d.SettingsFlowErrorHandler().WriteFlowError(w, r, s.SettingsStrategyID(), ctxUpdate.Flow, id, err)
}

0 comments on commit 0cf6027

Please sign in to comment.