From 1bf820ce35dc1d2a1670b75dd975c3b1569cce03 Mon Sep 17 00:00:00 2001 From: David Sharnoff Date: Thu, 23 Mar 2023 17:04:36 -0700 Subject: [PATCH 1/2] add CanSetUserinfoFromRequest interface --- NEXT-RELEASE.md | 12 ++++++++++++ example/server/storage/storage.go | 13 ++++++++++--- example/server/storage/storage_dynamic.go | 15 +++++++++++++-- pkg/op/storage.go | 9 +++++++++ pkg/op/token.go | 6 ++++++ 5 files changed, 50 insertions(+), 5 deletions(-) create mode 100644 NEXT-RELEASE.md diff --git a/NEXT-RELEASE.md b/NEXT-RELEASE.md new file mode 100644 index 00000000..1198aa1f --- /dev/null +++ b/NEXT-RELEASE.md @@ -0,0 +1,12 @@ +This file is a place to note backwards-incompatible changes that will be present +in the next major release. + +Here are planned changes. + +- `op.CanSetUserinfoFromRequest` will be removed. +- `op.SetUserinfoFromScopes` will be replaced with `op.SetUserinfoFromRequest`. + You can switch to `op.SetUserinfoFromRequest` immediately and have an empty + implementation of `op.SetUserinfoFromScopes`. To get the subject and client + (current parameters to `op.SetUserinfoFromScopes`) in `op.SetUserinfoFromRequest`, + call `token.GetSubject()` and `request.GetClientID` respectively. + diff --git a/example/server/storage/storage.go b/example/server/storage/storage.go index 7e1afbd5..acac5711 100644 --- a/example/server/storage/storage.go +++ b/example/server/storage/storage.go @@ -438,10 +438,17 @@ func (s *Storage) AuthorizeClientIDSecret(ctx context.Context, clientID, clientS return nil } -// SetUserinfoFromScopes implements the op.Storage interface -// it will be called for the creation of an id_token, so we'll just pass it to the private function without any further check +// SetUserinfoFromScopes implements the op.Storage interface. +// Provide an empty implementation and use SetUserinfoFromRequest instead. func (s *Storage) SetUserinfoFromScopes(ctx context.Context, userinfo *oidc.UserInfo, userID, clientID string, scopes []string) error { - return s.setUserinfo(ctx, userinfo, userID, clientID, scopes) + return nil +} + +// SetUserinfoFromRequests implements the op.CanSetUserinfoFromRequest interface. In the +// next major release, it will be required for op.Storage. +// It will be called for the creation of an id_token, so we'll just pass it to the private function without any further check +func (s *Storage) SetUserinfoFromRequest(ctx context.Context, userinfo *oidc.UserInfo, token op.IDTokenRequest, scopes []string) error { + return s.setUserinfo(ctx, userinfo, token.GetSubject(), token.GetClientID(), scopes) } // SetUserinfoFromToken implements the op.Storage interface diff --git a/example/server/storage/storage_dynamic.go b/example/server/storage/storage_dynamic.go index 6e5ee321..07af903f 100644 --- a/example/server/storage/storage_dynamic.go +++ b/example/server/storage/storage_dynamic.go @@ -196,8 +196,8 @@ func (s *multiStorage) AuthorizeClientIDSecret(ctx context.Context, clientID, cl return storage.AuthorizeClientIDSecret(ctx, clientID, clientSecret) } -// SetUserinfoFromScopes implements the op.Storage interface -// it will be called for the creation of an id_token, so we'll just pass it to the private function without any further check +// SetUserinfoFromScopes implements the op.Storage interface. +// Provide an empty implementation and use SetUserinfoFromRequest instead. func (s *multiStorage) SetUserinfoFromScopes(ctx context.Context, userinfo *oidc.UserInfo, userID, clientID string, scopes []string) error { storage, err := s.storageFromContext(ctx) if err != nil { @@ -206,6 +206,17 @@ func (s *multiStorage) SetUserinfoFromScopes(ctx context.Context, userinfo *oidc return storage.SetUserinfoFromScopes(ctx, userinfo, userID, clientID, scopes) } +// SetUserinfoFromRequests implements the op.CanSetUserinfoFromRequest interface. In the +// next major release, it will be required for op.Storage. +// It will be called for the creation of an id_token, so we'll just pass it to the private function without any further check +func (s *multiStorage) SetUserinfoFromRequest(ctx context.Context, userinfo *oidc.UserInfo, token op.IDTokenRequest, scopes []string) error { + storage, err := s.storageFromContext(ctx) + if err != nil { + return err + } + return storage.SetUserinfoFromRequest(ctx, userinfo, token, scopes) +} + // SetUserinfoFromToken implements the op.Storage interface // it will be called for the userinfo endpoint, so we read the token and pass the information from that to the private function func (s *multiStorage) SetUserinfoFromToken(ctx context.Context, userinfo *oidc.UserInfo, tokenID, subject, origin string) error { diff --git a/pkg/op/storage.go b/pkg/op/storage.go index e36eac7a..590c4a03 100644 --- a/pkg/op/storage.go +++ b/pkg/op/storage.go @@ -113,6 +113,8 @@ type OPStorage interface { // handle the current request. GetClientByClientID(ctx context.Context, clientID string) (Client, error) AuthorizeClientIDSecret(ctx context.Context, clientID, clientSecret string) error + // SetUserinfoFromScopes is deprecated and should have an empty implementation for now. + // Implement SetUserinfoFromRequest instead. SetUserinfoFromScopes(ctx context.Context, userinfo *oidc.UserInfo, userID, clientID string, scopes []string) error SetUserinfoFromToken(ctx context.Context, userinfo *oidc.UserInfo, tokenID, subject, origin string) error SetIntrospectionFromToken(ctx context.Context, userinfo *oidc.IntrospectionResponse, tokenID, subject, clientID string) error @@ -127,6 +129,13 @@ type JWTProfileTokenStorage interface { JWTProfileTokenType(ctx context.Context, request TokenRequest) (AccessTokenType, error) } +// CanSetUserinfoFromRequest is an optional additional interface that may be implemented by +// implementors of Storage. It allows additional data to be set in id_tokens based on the +// request. +type CanSetUserinfoFromRequest interface { + SetUserinfoFromRequest(ctx context.Context, userinfo *oidc.UserInfo, request IDTokenRequest, scopes []string) error +} + // Storage is a required parameter for NewOpenIDProvider(). In addition to the // embedded interfaces below, if the passed Storage implements ClientCredentialsStorage // then the grant type "client_credentials" will be supported. In that case, the access diff --git a/pkg/op/token.go b/pkg/op/token.go index 58568a78..6dfc993d 100644 --- a/pkg/op/token.go +++ b/pkg/op/token.go @@ -190,6 +190,12 @@ func CreateIDToken(ctx context.Context, issuer string, request IDTokenRequest, v if err != nil { return "", err } + if fromRequest, ok := storage.(CanSetUserinfoFromRequest); ok { + err := fromRequest.SetUserinfoFromRequest(ctx, userInfo, request, scopes) + if err != nil { + return "", err + } + } claims.SetUserInfo(userInfo) } if code != "" { From 2719a4762930585a480669c8b6eb026ed8f88b56 Mon Sep 17 00:00:00 2001 From: David Sharnoff Date: Fri, 24 Mar 2023 08:48:36 -0700 Subject: [PATCH 2/2] review feedback: move NEXT-RELEASE to issue --- NEXT-RELEASE.md | 12 ------------ 1 file changed, 12 deletions(-) delete mode 100644 NEXT-RELEASE.md diff --git a/NEXT-RELEASE.md b/NEXT-RELEASE.md deleted file mode 100644 index 1198aa1f..00000000 --- a/NEXT-RELEASE.md +++ /dev/null @@ -1,12 +0,0 @@ -This file is a place to note backwards-incompatible changes that will be present -in the next major release. - -Here are planned changes. - -- `op.CanSetUserinfoFromRequest` will be removed. -- `op.SetUserinfoFromScopes` will be replaced with `op.SetUserinfoFromRequest`. - You can switch to `op.SetUserinfoFromRequest` immediately and have an empty - implementation of `op.SetUserinfoFromScopes`. To get the subject and client - (current parameters to `op.SetUserinfoFromScopes`) in `op.SetUserinfoFromRequest`, - call `token.GetSubject()` and `request.GetClientID` respectively. -