Skip to content

Commit

Permalink
fix: do not populate id_first first step for account linking flows (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
jonas-jonas committed Sep 4, 2024
1 parent 32737dc commit 6ab2637
Show file tree
Hide file tree
Showing 6 changed files with 204 additions and 3 deletions.
2 changes: 2 additions & 0 deletions selfservice/flow/login/flow.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,8 @@ type Flow struct {

// ReturnToVerification contains the redirect URL for the verification flow.
ReturnToVerification string `json:"-" db:"-"`

isAccountLinkingFlow bool `json:"-" db:"-"`
}

var _ flow.Flow = new(Flow)
Expand Down
8 changes: 7 additions & 1 deletion selfservice/flow/login/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,12 @@ func WithFormErrorMessage(messages []text.Message) FlowOption {
}
}

func WithIsAccountLinking() FlowOption {
return func(f *Flow) {
f.isAccountLinkingFlow = true
}
}

func (h *Handler) NewLoginFlow(w http.ResponseWriter, r *http.Request, ft flow.Type, opts ...FlowOption) (*Flow, *session.Session, error) {
conf := h.d.Config()
f, err := NewFlow(conf, conf.SelfServiceFlowLoginRequestLifespan(r.Context()), h.d.GenerateCSRFToken(r), r, ft)
Expand Down Expand Up @@ -226,7 +232,7 @@ preLoginHook:
// Refreshing takes precedence over identifier_first auth which can not be a refresh flow.
// Therefor this comes first.
populateErr = strategy.PopulateLoginMethodFirstFactorRefresh(r, f)
case h.d.Config().SelfServiceLoginFlowIdentifierFirstEnabled(r.Context()):
case h.d.Config().SelfServiceLoginFlowIdentifierFirstEnabled(r.Context()) && !f.isAccountLinkingFlow:
populateErr = strategy.PopulateLoginMethodIdentifierFirstIdentification(r, f)
default:
populateErr = strategy.PopulateLoginMethodFirstFactor(r, f)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
{
"organization_id": null,
"type": "browser",
"active": "oidc",
"ui": {
"method": "POST",
"nodes": [
{
"type": "input",
"group": "oidc",
"attributes": {
"name": "provider",
"type": "submit",
"value": "claimsViaUserInfo",
"disabled": false,
"node_type": "input"
},
"messages": [],
"meta": {
"label": {
"id": 1010018,
"text": "Confirm with claimsViaUserInfo",
"type": "info",
"context": {
"provider": "claimsViaUserInfo"
}
}
}
},
{
"type": "input",
"group": "oidc",
"attributes": {
"name": "provider",
"type": "submit",
"value": "invalid-issuer",
"disabled": false,
"node_type": "input"
},
"messages": [],
"meta": {
"label": {
"id": 1010018,
"text": "Confirm with invalid-issuer",
"type": "info",
"context": {
"provider": "invalid-issuer"
}
}
}
},
{
"type": "input",
"group": "oidc",
"attributes": {
"name": "provider",
"type": "submit",
"value": "secondProvider",
"disabled": false,
"node_type": "input"
},
"messages": [],
"meta": {
"label": {
"id": 1010018,
"text": "Confirm with secondProvider",
"type": "info",
"context": {
"provider": "secondProvider"
}
}
}
},
{
"type": "input",
"group": "oidc",
"attributes": {
"name": "provider",
"type": "submit",
"value": "valid2",
"disabled": false,
"node_type": "input"
},
"messages": [],
"meta": {
"label": {
"id": 1010018,
"text": "Confirm with valid2",
"type": "info",
"context": {
"provider": "valid2"
}
}
}
},
{
"type": "input",
"group": "default",
"attributes": {
"name": "csrf_token",
"type": "hidden",
"required": true,
"disabled": false,
"node_type": "input"
},
"messages": [],
"meta": {}
},
{
"type": "input",
"group": "default",
"attributes": {
"name": "identifier",
"type": "hidden",
"value": "[email protected]",
"required": true,
"disabled": false,
"node_type": "input"
},
"messages": [],
"meta": {
"label": {
"id": 1070004,
"text": "ID",
"type": "info"
}
}
},
{
"type": "input",
"group": "password",
"attributes": {
"name": "password",
"type": "password",
"required": true,
"autocomplete": "current-password",
"disabled": false,
"node_type": "input"
},
"messages": [],
"meta": {
"label": {
"id": 1070001,
"text": "Password",
"type": "info"
}
}
},
{
"type": "input",
"group": "password",
"attributes": {
"name": "method",
"type": "submit",
"value": "password",
"disabled": false,
"node_type": "input"
},
"messages": [],
"meta": {
"label": {
"id": 1010022,
"text": "Sign in with password",
"type": "info"
}
}
}
],
"messages": [
{
"id": 1010016,
"text": "You tried to sign in with \"[email protected]\", but that email is already used by another account. Sign in to your account with one of the options below to add your account \"[email protected]\" at \"generic\" as another way to sign in.",
"type": "info",
"context": {
"duplicateIdentifier": "[email protected]",
"provider": "generic"
}
}
]
},
"refresh": false,
"requested_aal": "aal1",
"state": "choose_method"
}

2 changes: 1 addition & 1 deletion selfservice/strategy/oidc/strategy.go
Original file line number Diff line number Diff line change
Expand Up @@ -680,7 +680,7 @@ func (s *Strategy) populateAccountLinkingUI(ctx context.Context, lf *login.Flow,
nodes := []*node.Node{}
for _, n := range lf.UI.Nodes {
// We don't want to touch nodes unecessary nodes
if n.Meta == nil || n.Meta.Label == nil || n.Group == "default" {
if n.Meta == nil || n.Meta.Label == nil || n.Group == node.DefaultGroup {
nodes = append(nodes, n)
continue
}
Expand Down
2 changes: 1 addition & 1 deletion selfservice/strategy/oidc/strategy_registration.go
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ func (s *Strategy) registrationToLogin(w http.ResponseWriter, r *http.Request, r
opts = append(opts, login.WithFormErrorMessage(rf.UI.Messages))
}

opts = append(opts, login.WithInternalContext(rf.InternalContext))
opts = append(opts, login.WithInternalContext(rf.InternalContext), login.WithIsAccountLinking())

lf, _, err := s.d.LoginHandler().NewLoginFlow(w, r, rf.Type, opts...)
if err != nil {
Expand Down
8 changes: 8 additions & 0 deletions selfservice/strategy/oidc/strategy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1095,6 +1095,14 @@ func TestStrategy(t *testing.T) {
snapshotx.SnapshotTJSON(t, body, snapshotx.ExceptPaths("expires_at", "updated_at", "issued_at", "id", "created_at", "ui.action", "ui.nodes.4.attributes.value", "request_url"), snapshotx.ExceptNestedKeys("newLoginUrl"))
})

t.Run("case=should fail registration id_first strategy enabled", func(t *testing.T) {
conf.Set(ctx, config.ViperKeySelfServiceLoginFlowStyle, "identifier_first")
r := newBrowserRegistrationFlow(t, returnTS.URL, time.Minute)
action := assertFormValues(t, r.ID, "valid")
_, body := makeRequest(t, "valid", action, url.Values{})
snapshotx.SnapshotTJSON(t, body, snapshotx.ExceptPaths("expires_at", "updated_at", "issued_at", "id", "created_at", "ui.action", "ui.nodes.4.attributes.value", "request_url"), snapshotx.ExceptNestedKeys("newLoginUrl"))
})

t.Run("case=should fail login", func(t *testing.T) {
r := newBrowserLoginFlow(t, returnTS.URL, time.Minute)
action := assertFormValues(t, r.ID, "valid")
Expand Down

0 comments on commit 6ab2637

Please sign in to comment.