Skip to content

Commit

Permalink
feat: add identity ID to password grant extra claims (#831)
Browse files Browse the repository at this point in the history
  • Loading branch information
hperl committed Oct 30, 2024
1 parent 3ec8db8 commit b40b1cb
Show file tree
Hide file tree
Showing 7 changed files with 25 additions and 15 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/oidc-conformity.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
11 changes: 10 additions & 1 deletion handler/oauth2/flow_resource_owner.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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!
Expand Down
2 changes: 1 addition & 1 deletion handler/oauth2/flow_resource_owner_storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
6 changes: 3 additions & 3 deletions handler/oauth2/flow_resource_owner_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
8 changes: 4 additions & 4 deletions internal/oauth2_owner_storage.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 5 additions & 4 deletions storage/memory.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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 {
Expand Down
2 changes: 1 addition & 1 deletion storage/memory_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
})
Expand Down

0 comments on commit b40b1cb

Please sign in to comment.