diff --git a/.github/workflows/oidc-conformity.yml b/.github/workflows/oidc-conformity.yml index 1c7ecdd05..6994fb5e0 100644 --- a/.github/workflows/oidc-conformity.yml +++ b/.github/workflows/oidc-conformity.yml @@ -14,7 +14,7 @@ jobs: with: fetch-depth: 2 repository: ory/hydra - ref: master + ref: 2866a0499d02341ed0603601cfe4e63b24506fcb - uses: actions/setup-go@v2 with: go-version: "1.21" diff --git a/handler/oauth2/flow_resource_owner.go b/handler/oauth2/flow_resource_owner.go index ad0b45a20..8c1e04370 100644 --- a/handler/oauth2/flow_resource_owner.go +++ b/handler/oauth2/flow_resource_owner.go @@ -33,6 +33,11 @@ type ResourceOwnerPasswordCredentialsGrantHandler struct { } } +type Session interface { + // SetSubject sets the session's subject. + SetSubject(subject string) +} + // HandleTokenEndpointRequest implements https://tools.ietf.org/html/rfc6749#section-4.3.2 func (c *ResourceOwnerPasswordCredentialsGrantHandler) HandleTokenEndpointRequest(ctx context.Context, request fosite.AccessRequester) error { if !c.CanHandleTokenEndpointRequest(ctx, request) { @@ -58,10 +63,14 @@ func (c *ResourceOwnerPasswordCredentialsGrantHandler) HandleTokenEndpointReques password := request.GetRequestForm().Get("password") if username == "" || password == "" { return errorsx.WithStack(fosite.ErrInvalidRequest.WithHint("Username or password are missing from the POST body.")) - } else if err := c.ResourceOwnerPasswordCredentialsGrantStorage.Authenticate(ctx, username, password); errors.Is(err, fosite.ErrNotFound) { + } else if sub, err := c.ResourceOwnerPasswordCredentialsGrantStorage.Authenticate(ctx, username, password); errors.Is(err, fosite.ErrNotFound) { return errorsx.WithStack(fosite.ErrInvalidGrant.WithHint("Unable to authenticate the provided username and password credentials.").WithWrap(err).WithDebug(err.Error())) } else if err != nil { return errorsx.WithStack(fosite.ErrServerError.WithWrap(err).WithDebug(err.Error())) + } else { + if sess, ok := request.GetSession().(Session); ok { + sess.SetSubject(sub) + } } // Credentials must not be passed around, potentially leaking to the database! diff --git a/handler/oauth2/flow_resource_owner_storage.go b/handler/oauth2/flow_resource_owner_storage.go index ffc076428..b0b4efdb2 100644 --- a/handler/oauth2/flow_resource_owner_storage.go +++ b/handler/oauth2/flow_resource_owner_storage.go @@ -8,7 +8,7 @@ import ( ) type ResourceOwnerPasswordCredentialsGrantStorage interface { - Authenticate(ctx context.Context, name string, secret string) error + Authenticate(ctx context.Context, name string, secret string) (subject string, err error) AccessTokenStorage RefreshTokenStorage } diff --git a/handler/oauth2/flow_resource_owner_test.go b/handler/oauth2/flow_resource_owner_test.go index 59acb5ab1..6e8280acc 100644 --- a/handler/oauth2/flow_resource_owner_test.go +++ b/handler/oauth2/flow_resource_owner_test.go @@ -71,21 +71,21 @@ func TestResourceOwnerFlow_HandleTokenEndpointRequest(t *testing.T) { areq.Form.Set("password", "pan") areq.Client = &fosite.DefaultClient{GrantTypes: fosite.Arguments{"password"}, Scopes: []string{"foo-scope"}, Audience: []string{"https://www.ory.sh/api"}} - store.EXPECT().Authenticate(gomock.Any(), "peter", "pan").Return(fosite.ErrNotFound) + store.EXPECT().Authenticate(gomock.Any(), "peter", "pan").Return("", fosite.ErrNotFound) }, expectErr: fosite.ErrInvalidGrant, }, { description: "should fail because error on lookup", setup: func(config *fosite.Config) { - store.EXPECT().Authenticate(gomock.Any(), "peter", "pan").Return(errors.New("")) + store.EXPECT().Authenticate(gomock.Any(), "peter", "pan").Return("", errors.New("")) }, expectErr: fosite.ErrServerError, }, { description: "should pass", setup: func(config *fosite.Config) { - store.EXPECT().Authenticate(gomock.Any(), "peter", "pan").Return(nil) + store.EXPECT().Authenticate(gomock.Any(), "peter", "pan").Return("", nil) }, check: func(areq *fosite.AccessRequest) { //assert.NotEmpty(t, areq.GetSession().GetExpiresAt(fosite.AccessToken)) diff --git a/internal/oauth2_owner_storage.go b/internal/oauth2_owner_storage.go index 79e797958..7e79b1783 100644 --- a/internal/oauth2_owner_storage.go +++ b/internal/oauth2_owner_storage.go @@ -12,7 +12,6 @@ import ( reflect "reflect" gomock "github.com/golang/mock/gomock" - fosite "github.com/ory/fosite" ) @@ -40,11 +39,12 @@ func (m *MockResourceOwnerPasswordCredentialsGrantStorage) EXPECT() *MockResourc } // Authenticate mocks base method. -func (m *MockResourceOwnerPasswordCredentialsGrantStorage) Authenticate(arg0 context.Context, arg1, arg2 string) error { +func (m *MockResourceOwnerPasswordCredentialsGrantStorage) Authenticate(arg0 context.Context, arg1, arg2 string) (string, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Authenticate", arg0, arg1, arg2) - ret0, _ := ret[0].(error) - return ret0 + ret0, _ := ret[0].(string) + ret1, _ := ret[1].(error) + return ret0, ret1 } // Authenticate indicates an expected call of Authenticate. diff --git a/storage/memory.go b/storage/memory.go index 9e9fd1ba5..5e88772a7 100644 --- a/storage/memory.go +++ b/storage/memory.go @@ -10,6 +10,7 @@ import ( "time" "github.com/go-jose/go-jose/v3" + "github.com/google/uuid" "github.com/ory/fosite" "github.com/ory/fosite/internal" @@ -355,18 +356,18 @@ func (s *MemoryStore) DeleteRefreshTokenSession(_ context.Context, signature str return nil } -func (s *MemoryStore) Authenticate(_ context.Context, name string, secret string) error { +func (s *MemoryStore) Authenticate(_ context.Context, name string, secret string) (subject string, err error) { s.usersMutex.RLock() defer s.usersMutex.RUnlock() rel, ok := s.Users[name] if !ok { - return fosite.ErrNotFound + return "", fosite.ErrNotFound } if rel.Password != secret { - return fosite.ErrNotFound.WithDebug("Invalid credentials") + return "", fosite.ErrNotFound.WithDebug("Invalid credentials") } - return nil + return uuid.New().String(), nil } func (s *MemoryStore) RevokeRefreshToken(ctx context.Context, requestID string) error { diff --git a/storage/memory_test.go b/storage/memory_test.go index 0f082ee80..d06f4a409 100644 --- a/storage/memory_test.go +++ b/storage/memory_test.go @@ -52,7 +52,7 @@ func TestMemoryStore_Authenticate(t *testing.T) { Users: tt.fields.Users, usersMutex: tt.fields.usersMutex, } - if err := s.Authenticate(tt.args.in0, tt.args.name, tt.args.secret); err == nil || !errors.Is(err, tt.wantErr) { + if _, err := s.Authenticate(tt.args.in0, tt.args.name, tt.args.secret); err == nil || !errors.Is(err, tt.wantErr) { t.Errorf("Authenticate() error = %v, wantErr %v", err, tt.wantErr) } })