From 27f59b25032a2ac26032cc77cf1a110d68261317 Mon Sep 17 00:00:00 2001 From: Halil Cankilic <141903706+Eiermitsucuk@users.noreply.github.com> Date: Tue, 24 Mar 2026 15:43:21 +0100 Subject: [PATCH 01/15] fix: clean up after migrating obo logic to ims-go --- cmd/obo_exchange.go | 2 +- go.mod | 4 +- go.sum | 6 +-- ims/obo_exchange.go | 89 ++++++++++++--------------------------------- 4 files changed, 29 insertions(+), 72 deletions(-) diff --git a/cmd/obo_exchange.go b/cmd/obo_exchange.go index 12ef35d..85307e4 100644 --- a/cmd/obo_exchange.go +++ b/cmd/obo_exchange.go @@ -22,7 +22,7 @@ func oboExchangeCmd(imsConfig *ims.Config) *cobra.Command { Use: "on-behalf-of", Aliases: []string{"obo"}, Short: "On-Behalf-Of token exchange.", - Long: `On-Behalf-Of token exchange: exchange a user access token for a new token. Do NOT send OBO access tokens to frontend clients.`, + Long: `On-Behalf-Of token exchange: exchange a user access token for a new token. Do NOT send OBO access tokens to frontend clients.`, RunE: func(cmd *cobra.Command, args []string) error { cmd.SilenceUsage = true cmd.SilenceErrors = true diff --git a/go.mod b/go.mod index daf77a1..2772965 100644 --- a/go.mod +++ b/go.mod @@ -21,10 +21,12 @@ require ( github.com/spf13/viper v1.21.0 ) +replace github.com/adobe/ims-go => /Users/hcankilic/GolandProjects/ims-go + require ( github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/go-viper/mapstructure/v2 v2.4.0 // indirect - github.com/golang-jwt/jwt/v5 v5.3.0 // indirect + github.com/golang-jwt/jwt/v5 v5.3.1 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/pelletier/go-toml/v2 v2.2.4 // indirect github.com/sagikazarmark/locafero v0.11.0 // indirect diff --git a/go.sum b/go.sum index 48f52ed..b2ce72d 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,3 @@ -github.com/adobe/ims-go v0.19.2 h1:PzomRLP/ZDE5myXCAT2ofs6v0mm0yy4vNEl6mKOYLEU= -github.com/adobe/ims-go v0.19.2/go.mod h1:fBMENOn081lCW9w+XDrG3RNkWzUPYzDAfGCZZGwSIKg= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -9,8 +7,8 @@ github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs= github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= -github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo= -github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= +github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63YCY= +github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= diff --git a/ims/obo_exchange.go b/ims/obo_exchange.go index c6b09de..c3e2187 100644 --- a/ims/obo_exchange.go +++ b/ims/obo_exchange.go @@ -8,99 +8,56 @@ // OF ANY KIND, either express or implied. See the License for the specific language // governing permissions and limitations under the License. -package ims - -import ( - "encoding/json" - "fmt" - "io" - "net/http" - "net/url" - "strings" -) - // - Subject token restrictions: only user access tokens are accepted; ServiceTokens and // impersonation tokens must not be used as the subject token. // - Scope boundary: requested scopes cannot exceed the client's configured scopes. // - Audit trail: the full actor chain is preserved in the act claim of the issued token. // OBO uses token v4 and RFC 8693 grant type per IMS OBO documentation. -const defaultOBOGrantType = "urn:ietf:params:oauth:grant-type:token-exchange" + +package ims + +import ( + "fmt" + + "github.com/adobe/ims-go/ims" +) func (i Config) validateOBOExchangeConfig() error { switch { case i.URL == "": return fmt.Errorf("missing IMS base URL parameter") - case i.ClientID == "": - return fmt.Errorf("missing client ID parameter") - case i.ClientSecret == "": - return fmt.Errorf("missing client secret parameter") + case !validateURL(i.URL): + return fmt.Errorf("invalid IMS base URL parameter") case i.AccessToken == "": - return fmt.Errorf("missing access token parameter (only access tokens are accepted)") - case len(i.Scopes) == 0 || (len(i.Scopes) == 1 && i.Scopes[0] == ""): - return fmt.Errorf("scopes are required for On-Behalf-Of exchange") + return fmt.Errorf("missing access token parameter") default: return nil } } func (i Config) OBOExchange() (TokenInfo, error) { - if err := i.validateOBOExchangeConfig(); err != nil { - return TokenInfo{}, fmt.Errorf("invalid parameters for On-Behalf-Of exchange: %v", err) - } - httpClient, err := i.httpClient() - if err != nil { - return TokenInfo{}, fmt.Errorf("error creating the HTTP Client: %v", err) - } - - data := url.Values{} - data.Set("grant_type", defaultOBOGrantType) - data.Set("client_id", i.ClientID) - data.Set("client_secret", i.ClientSecret) - data.Set("subject_token", i.AccessToken) - data.Set("subject_token_type", "urn:ietf:params:oauth:token-type:access_token") - data.Set("requested_token_type", "urn:ietf:params:oauth:token-type:access_token") - data.Set("scope", strings.Join(i.Scopes, ",")) - - // OBO Token Exchange requires /ims/token/v4 (v3 does not support this grant type). - tokenURL := fmt.Sprintf("%s/ims/token/v4?client_id=%s", strings.TrimSuffix(i.URL, "/"), url.QueryEscape(i.ClientID)) - req, err := http.NewRequest(http.MethodPost, tokenURL, strings.NewReader(data.Encode())) - if err != nil { - return TokenInfo{}, fmt.Errorf("error creating On-Behalf-Of request: %v", err) + if err := i.validateOBOExchangeConfig(); err != nil { + return TokenInfo{}, fmt.Errorf("invalid parameters for On-Behalf-Of exchange: %w", err) } - req.Header.Set("Content-Type", "application/x-www-form-urlencoded") - resp, err := httpClient.Do(req) + c, err := i.newIMSClient() if err != nil { - return TokenInfo{}, fmt.Errorf("error during On-Behalf-Of exchange: %v", err) + return TokenInfo{}, fmt.Errorf("error creating the IMS client: %w", err) } - defer func() { _ = resp.Body.Close() }() - body, err := io.ReadAll(resp.Body) + r, err := c.OBOExchange(&ims.OBOExchangeRequest{ + ClientID: i.ClientID, + ClientSecret: i.ClientSecret, + SubjectToken: i.AccessToken, + Scopes: i.Scopes, + }) if err != nil { - return TokenInfo{}, fmt.Errorf("error reading On-Behalf-Of response: %v", err) - } - - if resp.StatusCode != http.StatusOK { - errMsg := fmt.Sprintf("On-Behalf-Of exchange failed (status %d): %s", resp.StatusCode, string(body)) - if resp.StatusCode == http.StatusBadRequest { - if strings.Contains(string(body), "invalid_scope") { - errMsg += " — IMS may be rejecting the subject token's scopes for this client. Ensure the client has Token exchange enabled and allowed scopes in the portal, or try a user token obtained with fewer scopes." - } - } - return TokenInfo{}, fmt.Errorf("%s", errMsg) - } - - var out struct { - AccessToken string `json:"access_token"` - ExpiresIn int `json:"expires_in"` - } - if err := json.Unmarshal(body, &out); err != nil { - return TokenInfo{}, fmt.Errorf("error decoding On-Behalf-Of response: %v", err) + return TokenInfo{}, fmt.Errorf("error during the On-Behalf-Of exchange: %w", err) } return TokenInfo{ - AccessToken: out.AccessToken, + AccessToken: r.AccessToken, }, nil } From d3488166b623ebcc3891b7ec89535266320a4ba4 Mon Sep 17 00:00:00 2001 From: Halil Cankilic <141903706+Eiermitsucuk@users.noreply.github.com> Date: Wed, 25 Mar 2026 09:51:44 +0100 Subject: [PATCH 02/15] refactor: trying the refactoring with github repository --- go.mod | 2 +- go.sum | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 2772965..f09d7e3 100644 --- a/go.mod +++ b/go.mod @@ -21,7 +21,7 @@ require ( github.com/spf13/viper v1.21.0 ) -replace github.com/adobe/ims-go => /Users/hcankilic/GolandProjects/ims-go +replace github.com/adobe/ims-go => github.com/Eiermitsucuk/ims-go v0.19.2 require ( github.com/fsnotify/fsnotify v1.9.0 // indirect diff --git a/go.sum b/go.sum index b2ce72d..01b3a82 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +github.com/Eiermitsucuk/ims-go v0.19.2 h1:WcRa8MwyX1c2O/o0lPhnqmueWMsmORyj8Y4DRP/tIFk= +github.com/Eiermitsucuk/ims-go v0.19.2/go.mod h1:fBMENOn081lCW9w+XDrG3RNkWzUPYzDAfGCZZGwSIKg= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= From 855a662de11f9337f0d8e03344291a4283802cd4 Mon Sep 17 00:00:00 2001 From: Halil Cankilic <141903706+Eiermitsucuk@users.noreply.github.com> Date: Wed, 25 Mar 2026 16:48:47 +0100 Subject: [PATCH 03/15] fix: go.mod --- go.mod | 2 -- go.sum | 4 ++-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/go.mod b/go.mod index f09d7e3..9520a18 100644 --- a/go.mod +++ b/go.mod @@ -21,8 +21,6 @@ require ( github.com/spf13/viper v1.21.0 ) -replace github.com/adobe/ims-go => github.com/Eiermitsucuk/ims-go v0.19.2 - require ( github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/go-viper/mapstructure/v2 v2.4.0 // indirect diff --git a/go.sum b/go.sum index 01b3a82..7184a5b 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -github.com/Eiermitsucuk/ims-go v0.19.2 h1:WcRa8MwyX1c2O/o0lPhnqmueWMsmORyj8Y4DRP/tIFk= -github.com/Eiermitsucuk/ims-go v0.19.2/go.mod h1:fBMENOn081lCW9w+XDrG3RNkWzUPYzDAfGCZZGwSIKg= +github.com/adobe/ims-go v0.19.2 h1:PzomRLP/ZDE5myXCAT2ofs6v0mm0yy4vNEl6mKOYLEU= +github.com/adobe/ims-go v0.19.2/go.mod h1:fBMENOn081lCW9w+XDrG3RNkWzUPYzDAfGCZZGwSIKg= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= From 7a8d2a9d45ee8368ee204c21207e63f53954f605 Mon Sep 17 00:00:00 2001 From: Halil Cankilic <141903706+Eiermitsucuk@users.noreply.github.com> Date: Fri, 27 Mar 2026 11:20:22 +0100 Subject: [PATCH 04/15] fix: go.mod --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 9520a18..20c309d 100644 --- a/go.mod +++ b/go.mod @@ -15,7 +15,7 @@ go 1.23.0 toolchain go1.26.1 require ( - github.com/adobe/ims-go v0.19.2 + github.com/adobe/ims-go v0.20.0 github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c github.com/spf13/cobra v1.10.2 github.com/spf13/viper v1.21.0 diff --git a/go.sum b/go.sum index 7184a5b..ce3ae82 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -github.com/adobe/ims-go v0.19.2 h1:PzomRLP/ZDE5myXCAT2ofs6v0mm0yy4vNEl6mKOYLEU= -github.com/adobe/ims-go v0.19.2/go.mod h1:fBMENOn081lCW9w+XDrG3RNkWzUPYzDAfGCZZGwSIKg= +github.com/adobe/ims-go v0.20.0 h1:M1OyF9xWLgsTVGbZ+c+G0EbPGpPhXQiPuLEdanNyaRo= +github.com/adobe/ims-go v0.20.0/go.mod h1:zGpx0ylsumBjkgd8fYgzJ8+Ci/zFABiBTAxbCscsyR8= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= From b90a8b24eb4c124f55308773bef4089a2f39bb84 Mon Sep 17 00:00:00 2001 From: Halil Cankilic <141903706+Eiermitsucuk@users.noreply.github.com> Date: Fri, 27 Mar 2026 15:20:46 +0100 Subject: [PATCH 05/15] fix: small resolves related to pr --- cmd/obo_exchange.go | 10 +++++----- ims/obo_exchange.go | 4 ++++ 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/cmd/obo_exchange.go b/cmd/obo_exchange.go index 85307e4..d8e1f3d 100644 --- a/cmd/obo_exchange.go +++ b/cmd/obo_exchange.go @@ -22,25 +22,25 @@ func oboExchangeCmd(imsConfig *ims.Config) *cobra.Command { Use: "on-behalf-of", Aliases: []string{"obo"}, Short: "On-Behalf-Of token exchange.", - Long: `On-Behalf-Of token exchange: exchange a user access token for a new token. Do NOT send OBO access tokens to frontend clients.`, + Long: `Do NOT send OBO access tokens to frontend clients.`, RunE: func(cmd *cobra.Command, args []string) error { cmd.SilenceUsage = true cmd.SilenceErrors = true resp, err := imsConfig.OBOExchange() if err != nil { - return fmt.Errorf("error during On-Behalf-Of exchange: %v", err) + return fmt.Errorf("error during On-Behalf-Of exchange: %w", err) } fmt.Println(resp.AccessToken) return nil }, } - + cmd.Flags().StringVarP(&imsConfig.ClientID, "clientID", "c", "", "IMS client ID.") cmd.Flags().StringVarP(&imsConfig.ClientSecret, "clientSecret", "p", "", "IMS client secret.") cmd.Flags().StringVarP(&imsConfig.AccessToken, "accessToken", "t", "", "User access token (subject token). Only access tokens are accepted.") - cmd.Flags().StringSliceVarP(&imsConfig.Scopes, "scopes", "s", []string{""}, - "Scopes to request. Must be within the client's configured scope boundary.") + cmd.Flags().StringSliceVarP(&imsConfig.Scopes, "scopes", "s", nil, + "Optional scopes to request; if omitted, none are sent. When set, must stay within the client's configured scope boundary.") return cmd } diff --git a/ims/obo_exchange.go b/ims/obo_exchange.go index c3e2187..ac1a529 100644 --- a/ims/obo_exchange.go +++ b/ims/obo_exchange.go @@ -29,6 +29,10 @@ func (i Config) validateOBOExchangeConfig() error { return fmt.Errorf("missing IMS base URL parameter") case !validateURL(i.URL): return fmt.Errorf("invalid IMS base URL parameter") + case i.ClientID == "": + return fmt.Errorf("missing client ID parameter") + case i.ClientSecret == "": + return fmt.Errorf("missing client secret parameter") case i.AccessToken == "": return fmt.Errorf("missing access token parameter") default: From d43a3c6153bc1f8c9c5d4e9c2e1cab2dfc2362c1 Mon Sep 17 00:00:00 2001 From: Halil Cankilic <141903706+Eiermitsucuk@users.noreply.github.com> Date: Tue, 31 Mar 2026 09:22:17 +0200 Subject: [PATCH 06/15] fix: obo_exchange.go --- cmd/obo_exchange.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/obo_exchange.go b/cmd/obo_exchange.go index d8e1f3d..c1b9eba 100644 --- a/cmd/obo_exchange.go +++ b/cmd/obo_exchange.go @@ -22,7 +22,7 @@ func oboExchangeCmd(imsConfig *ims.Config) *cobra.Command { Use: "on-behalf-of", Aliases: []string{"obo"}, Short: "On-Behalf-Of token exchange.", - Long: `Do NOT send OBO access tokens to frontend clients.`, + Long: `Token exchange using the RFC 8693 (urn:ietf:params:oauth:grant-type:token-exchange) grant.`, RunE: func(cmd *cobra.Command, args []string) error { cmd.SilenceUsage = true cmd.SilenceErrors = true @@ -35,7 +35,7 @@ func oboExchangeCmd(imsConfig *ims.Config) *cobra.Command { return nil }, } - + cmd.Flags().StringVarP(&imsConfig.ClientID, "clientID", "c", "", "IMS client ID.") cmd.Flags().StringVarP(&imsConfig.ClientSecret, "clientSecret", "p", "", "IMS client secret.") cmd.Flags().StringVarP(&imsConfig.AccessToken, "accessToken", "t", "", "User access token (subject token). Only access tokens are accepted.") From b0f59f47f6b501fbc27ab3207c584b61394245d8 Mon Sep 17 00:00:00 2001 From: Halil Cankilic <141903706+Eiermitsucuk@users.noreply.github.com> Date: Thu, 2 Apr 2026 09:27:03 +0200 Subject: [PATCH 07/15] refactor: cleaning up after moving the dcr logic to ims-go --- cmd/dcr.go | 2 +- go.mod | 2 ++ go.sum | 2 -- ims/config_test.go | 4 +-- ims/dcr.go | 60 ++++++++++++++++++++++++++++++++++ ims/register.go | 80 ---------------------------------------------- 6 files changed, 65 insertions(+), 85 deletions(-) create mode 100644 ims/dcr.go delete mode 100644 ims/register.go diff --git a/cmd/dcr.go b/cmd/dcr.go index c059781..7fbcddb 100644 --- a/cmd/dcr.go +++ b/cmd/dcr.go @@ -38,7 +38,7 @@ func registerCmd(imsConfig *ims.Config) *cobra.Command { RunE: func(cmd *cobra.Command, args []string) error { cmd.SilenceUsage = true - resp, err := imsConfig.Register() + resp, err := imsConfig.DCRRegister() if err != nil { return fmt.Errorf("error during client registration: %w", err) } diff --git a/go.mod b/go.mod index 20c309d..58c838d 100644 --- a/go.mod +++ b/go.mod @@ -21,6 +21,8 @@ require ( github.com/spf13/viper v1.21.0 ) +replace github.com/adobe/ims-go => /Users/hcankilic/GolandProjects/ims-go + require ( github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/go-viper/mapstructure/v2 v2.4.0 // indirect diff --git a/go.sum b/go.sum index ce3ae82..b2ce72d 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,3 @@ -github.com/adobe/ims-go v0.20.0 h1:M1OyF9xWLgsTVGbZ+c+G0EbPGpPhXQiPuLEdanNyaRo= -github.com/adobe/ims-go v0.20.0/go.mod h1:zGpx0ylsumBjkgd8fYgzJ8+Ci/zFABiBTAxbCscsyR8= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= diff --git a/ims/config_test.go b/ims/config_test.go index 5cbcab0..a82a9f4 100644 --- a/ims/config_test.go +++ b/ims/config_test.go @@ -295,7 +295,7 @@ func TestValidateAuthorizeClientCredentialsConfig(t *testing.T) { } } -func TestValidateRegisterConfig(t *testing.T) { +func TestValidateDCRConfig(t *testing.T) { tests := []struct { name string config Config @@ -308,7 +308,7 @@ func TestValidateRegisterConfig(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - err := tt.config.validateRegisterConfig() + err := tt.config.validateDCRConfig() assertError(t, err, tt.wantErr) }) } diff --git a/ims/dcr.go b/ims/dcr.go new file mode 100644 index 0000000..cdb4fb4 --- /dev/null +++ b/ims/dcr.go @@ -0,0 +1,60 @@ +// Copyright 2026 Adobe. All rights reserved. +// This file is licensed to you under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. You may obtain a copy +// of the License at http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software distributed under +// the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +// OF ANY KIND, either express or implied. See the License for the specific language +// governing permissions and limitations under the License. + +// Dynamic Client Registration (DCR): POST JSON to IMS /ims/register. + +package ims + +import ( + "fmt" + + "github.com/adobe/ims-go/ims" +) + +const ( + dcrRegisterPath = "/ims/register" + maxDCRResponseBodySize = 1 << 20 // 1 MiB +) + +func (i Config) validateDCRConfig() error { + switch { + case i.URL == "": + return fmt.Errorf("missing IMS base URL parameter") + case !validateURL(i.URL): + return fmt.Errorf("invalid IMS base URL parameter") + case i.ClientName == "": + return fmt.Errorf("missing client name parameter") + case len(i.RedirectURIs) == 0: + return fmt.Errorf("missing redirect URIs parameter") + default: + return nil + } +} + +func (i Config) DCRRegister() (string, error) { + if err := i.validateDCRConfig(); err != nil { + return "", fmt.Errorf("invalid parameters for client registration: %w", err) + } + + c, err := i.newIMSClient() + if err != nil { + return "", fmt.Errorf("error creating the IMS client: %w", err) + } + + resp, err := c.DCR(&ims.DCRRequest{ + ClientName: i.ClientName, + RedirectURIs: i.RedirectURIs, + }) + if err != nil { + return "", fmt.Errorf("error during client registration: %w", err) + } + + return string(resp.Body), nil +} diff --git a/ims/register.go b/ims/register.go deleted file mode 100644 index d8a6358..0000000 --- a/ims/register.go +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright 2025 Adobe. All rights reserved. -// This file is licensed to you under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. You may obtain a copy -// of the License at http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software distributed under -// the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -// OF ANY KIND, either express or implied. See the License for the specific language -// governing permissions and limitations under the License. - -package ims - -import ( - "encoding/json" - "fmt" - "io" - "net/http" - "strings" -) - -func (i Config) validateRegisterConfig() error { - switch { - case i.URL == "": - return fmt.Errorf("missing IMS base URL parameter") - case !validateURL(i.URL): - return fmt.Errorf("invalid IMS base URL parameter") - case i.ClientName == "": - return fmt.Errorf("missing client name parameter") - case len(i.RedirectURIs) == 0: - return fmt.Errorf("missing redirect URIs parameter") - default: - return nil - } -} - -// Register performs Dynamic Client Registration. -func (i Config) Register() (string, error) { - if err := i.validateRegisterConfig(); err != nil { - return "", fmt.Errorf("invalid parameters for client registration: %w", err) - } - - // Build the request payload using json.Marshal for proper escaping. - payload, err := json.Marshal(map[string]any{ - "client_name": i.ClientName, - "redirect_uris": i.RedirectURIs, - }) - if err != nil { - return "", fmt.Errorf("error building registration payload: %w", err) - } - - endpoint := strings.TrimRight(i.URL, "/") + "/ims/register" - - req, err := http.NewRequest("POST", endpoint, strings.NewReader(string(payload))) - if err != nil { - return "", fmt.Errorf("error creating request: %w", err) - } - req.Header.Add("content-type", "application/json") - - httpClient, err := i.httpClient() - if err != nil { - return "", fmt.Errorf("error creating the HTTP client: %w", err) - } - - res, err := httpClient.Do(req) - if err != nil { - return "", fmt.Errorf("error making registration request: %w", err) - } - defer func() { _ = res.Body.Close() }() - - body, err := io.ReadAll(io.LimitReader(res.Body, 1<<20)) // 1 MB max - if err != nil { - return "", fmt.Errorf("error reading response body: %w", err) - } - - if res.StatusCode < 200 || res.StatusCode >= 300 { - return "", fmt.Errorf("registration failed with status %d: %s", res.StatusCode, string(body)) - } - - return string(body), nil -} From 6df9e76f37cf9f08f0d4114029c2ba673aa34ee1 Mon Sep 17 00:00:00 2001 From: Halil Cankilic <141903706+Eiermitsucuk@users.noreply.github.com> Date: Thu, 2 Apr 2026 14:36:21 +0200 Subject: [PATCH 08/15] feat: added scopes --- cmd/dcr.go | 3 ++- ims/dcr.go | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/cmd/dcr.go b/cmd/dcr.go index 7fbcddb..d59f1f5 100644 --- a/cmd/dcr.go +++ b/cmd/dcr.go @@ -35,7 +35,7 @@ func registerCmd(imsConfig *ims.Config) *cobra.Command { Use: "register", Short: "Register a client.", Long: `Register a new OAuth client using Dynamic Client Registration.`, - RunE: func(cmd *cobra.Command, args []string) error { + RunE: func(cmd *cobra.Command, asrgs []string) error { cmd.SilenceUsage = true resp, err := imsConfig.DCRRegister() @@ -50,6 +50,7 @@ func registerCmd(imsConfig *ims.Config) *cobra.Command { cmd.Flags().StringVarP(&imsConfig.ClientName, "clientName", "n", "", "Client application name.") cmd.Flags().StringSliceVarP(&imsConfig.RedirectURIs, "redirectURIs", "r", []string{}, "Redirect URIs (comma-separated or multiple flags).") + cmd.Flags().StringSliceVarP(&imsConfig.Scopes, "scopes", "s", []string{}, "Requested scopes (comma-separated or multiple flags).") return cmd } diff --git a/ims/dcr.go b/ims/dcr.go index cdb4fb4..a2eec68 100644 --- a/ims/dcr.go +++ b/ims/dcr.go @@ -51,6 +51,7 @@ func (i Config) DCRRegister() (string, error) { resp, err := c.DCR(&ims.DCRRequest{ ClientName: i.ClientName, RedirectURIs: i.RedirectURIs, + Scopes: i.Scopes, }) if err != nil { return "", fmt.Errorf("error during client registration: %w", err) From ad275544a7535cce180b3eff8d30e5996a461664 Mon Sep 17 00:00:00 2001 From: Halil Cankilic <141903706+Eiermitsucuk@users.noreply.github.com> Date: Thu, 2 Apr 2026 14:46:58 +0200 Subject: [PATCH 09/15] fix: go.mod --- go.mod | 2 -- go.sum | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index 58c838d..20c309d 100644 --- a/go.mod +++ b/go.mod @@ -21,8 +21,6 @@ require ( github.com/spf13/viper v1.21.0 ) -replace github.com/adobe/ims-go => /Users/hcankilic/GolandProjects/ims-go - require ( github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/go-viper/mapstructure/v2 v2.4.0 // indirect diff --git a/go.sum b/go.sum index b2ce72d..ce3ae82 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +github.com/adobe/ims-go v0.20.0 h1:M1OyF9xWLgsTVGbZ+c+G0EbPGpPhXQiPuLEdanNyaRo= +github.com/adobe/ims-go v0.20.0/go.mod h1:zGpx0ylsumBjkgd8fYgzJ8+Ci/zFABiBTAxbCscsyR8= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= From a2e07a87d52a08b881d5da67eb76edbebdacab41 Mon Sep 17 00:00:00 2001 From: Halil Cankilic <141903706+Eiermitsucuk@users.noreply.github.com> Date: Tue, 7 Apr 2026 15:58:10 +0200 Subject: [PATCH 10/15] fix: demo --- demo.sh | 45 +++++++++++++++++++++++++++++++++++++++++++++ go.sum | 1 + 2 files changed, 46 insertions(+) create mode 100755 demo.sh diff --git a/demo.sh b/demo.sh new file mode 100755 index 0000000..b3a0af4 --- /dev/null +++ b/demo.sh @@ -0,0 +1,45 @@ +#!/usr/bin/env bash + +run() { + echo -e "\e[33m\$ $*\e[0m" + read -r + "$@" + echo "" +} + +clear +echo "imscli Demo" +echo "" +read -r + +# 1. DCR +run ./imscli dcr register \ + --url https://ims-na1-stg1.adobelogin.com \ + --clientName "My App" \ + --redirectURIs "http://localhost:8888" \ + --scopes "openid AdobeID" + +read -r -p "client_id: " CLIENT_ID +read -r -p "client_secret: " CLIENT_SECRET + +# 2. Authz +echo -e "\e[33m\$ ./imscli authz user --url https://ims-na1-stg1.adobelogin.com --clientID $CLIENT_ID --clientSecret $CLIENT_SECRET --scopes \"openid AdobeID\" --organization \"96D656605F9846070A494236@AdobeOrg\"\e[0m" +read -r +ACCESS_TOKEN=$(./imscli authz user \ + --url https://ims-na1-stg1.adobelogin.com \ + --clientID "$CLIENT_ID" \ + --clientSecret "$CLIENT_SECRET" \ + --scopes "openid AdobeID" \ + --organization "96D656605F9846070A494236@AdobeOrg") +echo "$ACCESS_TOKEN" +echo "" + +# 3. OBO +run ./imscli obo \ + --url https://ims-na1-stg1.adobelogin.com \ + --clientID "imscli" \ + --clientSecret "s8e-QZ9CnNQq2W6J_suZdqNd__BaaXokfZVq" \ + --accessToken "$ACCESS_TOKEN" \ + --scopes "openid AdobeID" + +echo -e "\e[32mDone!\e[0m" diff --git a/go.sum b/go.sum index ce3ae82..7c7cd0a 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,6 @@ github.com/adobe/ims-go v0.20.0 h1:M1OyF9xWLgsTVGbZ+c+G0EbPGpPhXQiPuLEdanNyaRo= github.com/adobe/ims-go v0.20.0/go.mod h1:zGpx0ylsumBjkgd8fYgzJ8+Ci/zFABiBTAxbCscsyR8= +github.com/adobe/ims-go v0.22.0/go.mod h1:zGpx0ylsumBjkgd8fYgzJ8+Ci/zFABiBTAxbCscsyR8= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= From e6a0a7c3b7f3f44cd67b7f0a349a54c78b938009 Mon Sep 17 00:00:00 2001 From: Halil Cankilic <141903706+Eiermitsucuk@users.noreply.github.com> Date: Tue, 7 Apr 2026 15:58:44 +0200 Subject: [PATCH 11/15] fix: go.mod --- go.mod | 2 +- go.sum | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 20c309d..224a997 100644 --- a/go.mod +++ b/go.mod @@ -15,7 +15,7 @@ go 1.23.0 toolchain go1.26.1 require ( - github.com/adobe/ims-go v0.20.0 + github.com/adobe/ims-go v0.22.0 github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c github.com/spf13/cobra v1.10.2 github.com/spf13/viper v1.21.0 diff --git a/go.sum b/go.sum index 7c7cd0a..917a7dc 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,4 @@ -github.com/adobe/ims-go v0.20.0 h1:M1OyF9xWLgsTVGbZ+c+G0EbPGpPhXQiPuLEdanNyaRo= -github.com/adobe/ims-go v0.20.0/go.mod h1:zGpx0ylsumBjkgd8fYgzJ8+Ci/zFABiBTAxbCscsyR8= +github.com/adobe/ims-go v0.22.0 h1:fN9sUoET8ytkv58aLEDCaUUTyXwsIpRjoSNy87EkbwU= github.com/adobe/ims-go v0.22.0/go.mod h1:zGpx0ylsumBjkgd8fYgzJ8+Ci/zFABiBTAxbCscsyR8= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= From ffd01b286a8043d21bc418b341b7600510e179b1 Mon Sep 17 00:00:00 2001 From: Halil Cankilic <141903706+Eiermitsucuk@users.noreply.github.com> Date: Wed, 8 Apr 2026 13:56:58 +0200 Subject: [PATCH 12/15] gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 5e01dcd..0cf12f6 100644 --- a/.gitignore +++ b/.gitignore @@ -68,3 +68,4 @@ Temporary Items dist/ /imscli go.mod.local +demo.sh \ No newline at end of file From 76757df47bf0eb0d1014fcf315573ad21466fc37 Mon Sep 17 00:00:00 2001 From: Halil Cankilic <141903706+Eiermitsucuk@users.noreply.github.com> Date: Wed, 8 Apr 2026 14:05:26 +0200 Subject: [PATCH 13/15] fix: removed demo --- .gitignore | 1 - demo.sh | 45 --------------------------------------------- 2 files changed, 46 deletions(-) delete mode 100755 demo.sh diff --git a/.gitignore b/.gitignore index 0cf12f6..5e01dcd 100644 --- a/.gitignore +++ b/.gitignore @@ -68,4 +68,3 @@ Temporary Items dist/ /imscli go.mod.local -demo.sh \ No newline at end of file diff --git a/demo.sh b/demo.sh deleted file mode 100755 index b3a0af4..0000000 --- a/demo.sh +++ /dev/null @@ -1,45 +0,0 @@ -#!/usr/bin/env bash - -run() { - echo -e "\e[33m\$ $*\e[0m" - read -r - "$@" - echo "" -} - -clear -echo "imscli Demo" -echo "" -read -r - -# 1. DCR -run ./imscli dcr register \ - --url https://ims-na1-stg1.adobelogin.com \ - --clientName "My App" \ - --redirectURIs "http://localhost:8888" \ - --scopes "openid AdobeID" - -read -r -p "client_id: " CLIENT_ID -read -r -p "client_secret: " CLIENT_SECRET - -# 2. Authz -echo -e "\e[33m\$ ./imscli authz user --url https://ims-na1-stg1.adobelogin.com --clientID $CLIENT_ID --clientSecret $CLIENT_SECRET --scopes \"openid AdobeID\" --organization \"96D656605F9846070A494236@AdobeOrg\"\e[0m" -read -r -ACCESS_TOKEN=$(./imscli authz user \ - --url https://ims-na1-stg1.adobelogin.com \ - --clientID "$CLIENT_ID" \ - --clientSecret "$CLIENT_SECRET" \ - --scopes "openid AdobeID" \ - --organization "96D656605F9846070A494236@AdobeOrg") -echo "$ACCESS_TOKEN" -echo "" - -# 3. OBO -run ./imscli obo \ - --url https://ims-na1-stg1.adobelogin.com \ - --clientID "imscli" \ - --clientSecret "s8e-QZ9CnNQq2W6J_suZdqNd__BaaXokfZVq" \ - --accessToken "$ACCESS_TOKEN" \ - --scopes "openid AdobeID" - -echo -e "\e[32mDone!\e[0m" From 18aa8d046d3ce0ea7ec943ce6d1e1a61185af4a8 Mon Sep 17 00:00:00 2001 From: Halil Cankilic <141903706+Eiermitsucuk@users.noreply.github.com> Date: Wed, 8 Apr 2026 14:52:15 +0200 Subject: [PATCH 14/15] refactor: unused constants were removed --- ims/dcr.go | 5 ----- 1 file changed, 5 deletions(-) diff --git a/ims/dcr.go b/ims/dcr.go index a2eec68..163970a 100644 --- a/ims/dcr.go +++ b/ims/dcr.go @@ -18,11 +18,6 @@ import ( "github.com/adobe/ims-go/ims" ) -const ( - dcrRegisterPath = "/ims/register" - maxDCRResponseBodySize = 1 << 20 // 1 MiB -) - func (i Config) validateDCRConfig() error { switch { case i.URL == "": From 2cd0f994a8b82cba0a6725d9f5349756b88a4e6d Mon Sep 17 00:00:00 2001 From: Halil Cankilic <141903706+Eiermitsucuk@users.noreply.github.com> Date: Thu, 9 Apr 2026 16:52:53 +0200 Subject: [PATCH 15/15] fix: syntax in dcr.go --- cmd/dcr.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/dcr.go b/cmd/dcr.go index d59f1f5..0f635e4 100644 --- a/cmd/dcr.go +++ b/cmd/dcr.go @@ -35,7 +35,7 @@ func registerCmd(imsConfig *ims.Config) *cobra.Command { Use: "register", Short: "Register a client.", Long: `Register a new OAuth client using Dynamic Client Registration.`, - RunE: func(cmd *cobra.Command, asrgs []string) error { + RunE: func(cmd *cobra.Command, args []string) error { cmd.SilenceUsage = true resp, err := imsConfig.DCRRegister()