@@ -19,22 +19,26 @@ import (
1919 "github.com/stretchr/testify/require"
2020 "github.com/tidwall/gjson"
2121
22+ "github.com/ory/herodot"
2223 "github.com/ory/x/configx"
2324
2425 "github.com/ory/kratos/courier"
2526 "github.com/ory/kratos/driver"
2627 "github.com/ory/kratos/driver/config"
28+ "github.com/ory/kratos/hydra"
2729 "github.com/ory/kratos/identity"
2830 "github.com/ory/kratos/internal"
2931 oryClient "github.com/ory/kratos/internal/httpclient"
3032 "github.com/ory/kratos/internal/testhelpers"
3133 "github.com/ory/kratos/selfservice/flow"
3234 "github.com/ory/kratos/selfservice/flow/login"
35+ "github.com/ory/kratos/selfservice/strategy/code"
3336 "github.com/ory/kratos/selfservice/strategy/idfirst"
3437 "github.com/ory/kratos/selfservice/strategy/totp"
3538 "github.com/ory/kratos/session"
3639 "github.com/ory/kratos/text"
3740 "github.com/ory/kratos/x"
41+ "github.com/ory/kratos/x/nosurfx"
3842 "github.com/ory/x/contextx"
3943 "github.com/ory/x/ioutilx"
4044 "github.com/ory/x/snapshotx"
@@ -1495,3 +1499,68 @@ func TestFormHydration(t *testing.T) {
14951499 toSnapshot (t , f )
14961500 })
14971501}
1502+
1503+ func TestCodeLoginWithLoginChallenge (t * testing.T ) {
1504+ t .Parallel ()
1505+
1506+ _ , reg := internal .NewFastRegistryWithMocks (t ,
1507+ configx .WithValue (config .ViperKeySelfServiceStrategyConfig + "." + string (identity .CredentialsTypeCodeAuth ), map [string ]interface {}{
1508+ "enabled" : true ,
1509+ "passwordless_enabled" : true ,
1510+ }),
1511+ configx .WithValues (testhelpers .DefaultIdentitySchemaConfig ("file://./stub/code.identity.schema.json" )),
1512+ )
1513+ reg .SetHydra (hydra .NewFake ())
1514+ reg .WithCSRFTokenGenerator (nosurfx .FakeCSRFTokenGenerator )
1515+ s := code .NewStrategy (reg )
1516+
1517+ loginChallenge := hydra .FakeValidLoginChallenge
1518+
1519+ newFlow := func (ctx context.Context , t * testing.T ) (* http.Request , * login.Flow ) {
1520+ t .Helper ()
1521+ r := httptest .NewRequest ("GET" , "/self-service/login/browser" , nil )
1522+ r = r .WithContext (ctx )
1523+ f , err := login .NewFlow (reg .Config (), time .Minute , nosurfx .FakeCSRFToken , r , flow .TypeBrowser )
1524+ require .NoError (t , err )
1525+ require .NoError (t , reg .LoginFlowPersister ().CreateLoginFlow (ctx , f ))
1526+ return r , f
1527+ }
1528+
1529+ t .Run ("case=fetches login challenge on code input state" , func (t * testing.T ) {
1530+ _ , f := newFlow (t .Context (), t )
1531+ f .OAuth2LoginChallenge = sqlxx .NullString (loginChallenge )
1532+ i := createIdentity (t .Context (), t , reg , false , false )
1533+ email := gjson .Get (i .Traits .String (), "email" ).String ()
1534+
1535+ body := gjson .Parse (`{
1536+ "method": "code",
1537+ "identifier": "` + email + `",
1538+ "csrf_token": "` + f .CSRFToken + `"
1539+ }` )
1540+ r := httptest .NewRequest ("POST" , "/self-service/login/browser" , strings .NewReader (body .Raw ))
1541+ r .Header .Add ("Content-Type" , "application/json" )
1542+
1543+ _ , err := s .Login (httptest .NewRecorder (), r , f , nil )
1544+ require .ErrorIs (t , err , flow .ErrCompletedByStrategy )
1545+ require .NotNil (t , f .HydraLoginRequest )
1546+ })
1547+
1548+ t .Run ("case=returns error if login challenge is invalid" , func (t * testing.T ) {
1549+ _ , f := newFlow (t .Context (), t )
1550+ f .OAuth2LoginChallenge = sqlxx .NullString (hydra .FakeInvalidLoginChallenge )
1551+ i := createIdentity (t .Context (), t , reg , false , false )
1552+ email := gjson .Get (i .Traits .String (), "email" ).String ()
1553+
1554+ body := gjson .Parse (`{
1555+ "method": "code",
1556+ "identifier": "` + email + `",
1557+ "csrf_token": "` + f .CSRFToken + `"
1558+ }` )
1559+ r := httptest .NewRequest ("POST" , "/self-service/login/browser" , strings .NewReader (body .Raw ))
1560+ r .Header .Add ("Content-Type" , "application/json" )
1561+
1562+ _ , err := s .Login (httptest .NewRecorder (), r , f , nil )
1563+ require .ErrorIs (t , err , herodot .ErrBadRequest )
1564+ require .Nil (t , f .HydraLoginRequest )
1565+ })
1566+ }
0 commit comments