diff --git a/internal/kratos/fake_kratos.go b/internal/kratos/fake_kratos.go index 17141b046bc..1162690b626 100644 --- a/internal/kratos/fake_kratos.go +++ b/internal/kratos/fake_kratos.go @@ -3,7 +3,11 @@ package kratos -import "context" +import ( + "context" + + "github.com/ory/fosite" +) type ( FakeKratos struct { @@ -14,6 +18,8 @@ type ( const ( FakeSessionID = "fake-kratos-session-id" + FakeUsername = "fake-kratos-username" + FakePassword = "fake-kratos-password" ) var _ Client = new(FakeKratos) @@ -29,8 +35,11 @@ func (f *FakeKratos) DisableSession(_ context.Context, identityProviderSessionID return nil } -func (f *FakeKratos) Authenticate(context.Context, string, string) error { - panic("missing") +func (f *FakeKratos) Authenticate(_ context.Context, username, password string) error { + if username == FakeUsername && password == FakePassword { + return nil + } + return fosite.ErrNotFound } func (f *FakeKratos) Reset() { diff --git a/internal/kratos/kratos.go b/internal/kratos/kratos.go index 4bfbfcd4bcd..898fc54b480 100644 --- a/internal/kratos/kratos.go +++ b/internal/kratos/kratos.go @@ -11,6 +11,7 @@ import ( "github.com/pkg/errors" "go.opentelemetry.io/otel/attribute" + "github.com/ory/fosite" "github.com/ory/hydra/v2/driver/config" "github.com/ory/hydra/v2/x" client "github.com/ory/kratos-client-go" @@ -68,8 +69,11 @@ func (k *Default) Authenticate(ctx context.Context, name, secret string) (err er Password: secret, }, }).Execute() + if err != nil { + return fosite.ErrNotFound.WithWrap(err) + } - return err + return nil } func (k *Default) DisableSession(ctx context.Context, identityProviderSessionID string) (err error) { diff --git a/oauth2/oauth2_rop_test.go b/oauth2/oauth2_rop_test.go new file mode 100644 index 00000000000..73946a25539 --- /dev/null +++ b/oauth2/oauth2_rop_test.go @@ -0,0 +1,65 @@ +// Copyright © 2022 Ory Corp +// SPDX-License-Identifier: Apache-2.0 + +package oauth2_test + +import ( + "context" + "testing" + + "github.com/google/uuid" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "golang.org/x/oauth2" + + "github.com/ory/fosite/compose" + hydra "github.com/ory/hydra/v2/client" + "github.com/ory/hydra/v2/fositex" + "github.com/ory/hydra/v2/internal" + "github.com/ory/hydra/v2/internal/kratos" + "github.com/ory/hydra/v2/internal/testhelpers" + "github.com/ory/x/contextx" +) + +func TestResourceOwnerPasswordGrant(t *testing.T) { + ctx := context.Background() + fakeKratos := kratos.NewFake() + reg := internal.NewMockedRegistry(t, &contextx.Default{}) + reg.WithKratos(fakeKratos) + reg.WithExtraFositeFactories([]fositex.Factory{compose.OAuth2ResourceOwnerPasswordCredentialsFactory}) + _, adminTS := testhelpers.NewOAuth2Server(ctx, t, reg) + + secret := uuid.New().String() + client := &hydra.Client{ + Secret: secret, + GrantTypes: []string{"password"}, + } + require.NoError(t, reg.ClientManager().CreateClient(ctx, client)) + + oauth2Config := &oauth2.Config{ + ClientID: client.GetID(), + ClientSecret: secret, + Endpoint: oauth2.Endpoint{ + AuthURL: reg.Config().OAuth2AuthURL(ctx).String(), + TokenURL: reg.Config().OAuth2TokenURL(ctx).String(), + AuthStyle: oauth2.AuthStyleInHeader, + }, + } + + t.Run("case=get ROP grant token with valid username and password", func(t *testing.T) { + token, err := oauth2Config.PasswordCredentialsToken(ctx, kratos.FakeUsername, kratos.FakePassword) + require.NoError(t, err) + require.NotEmpty(t, token.AccessToken) + i := testhelpers.IntrospectToken(t, oauth2Config, token.AccessToken, adminTS) + assert.True(t, i.Get("active").Bool(), "%s", i) + assert.EqualValues(t, oauth2Config.ClientID, i.Get("client_id").String(), "%s", i) + }) + + t.Run("case=access denied for invalid password", func(t *testing.T) { + _, err := oauth2Config.PasswordCredentialsToken(ctx, kratos.FakeUsername, "invalid") + retrieveError := new(oauth2.RetrieveError) + require.Error(t, err) + require.ErrorAs(t, err, &retrieveError) + assert.Contains(t, retrieveError.ErrorDescription, "Unable to authenticate the provided username and password credentials") + }) +}