diff --git a/cmd/dcr.go b/cmd/dcr.go new file mode 100644 index 0000000..1516fea --- /dev/null +++ b/cmd/dcr.go @@ -0,0 +1,47 @@ +package cmd + +import ( + "fmt" + + "github.com/adobe/imscli/ims" + "github.com/spf13/cobra" +) + +func dcrCmd(imsConfig *ims.Config) *cobra.Command { + cmd := &cobra.Command{ + Use: "dcr", + Short: "Dynamic Client Registration operations.", + Long: `The dcr command enables Dynamic Client Registration operations.`, + } + cmd.AddCommand( + registerCmd(imsConfig), + ) + return cmd +} + +func registerCmd(imsConfig *ims.Config) *cobra.Command { + cmd := &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 { + cmd.SilenceUsage = true + cmd.SilenceErrors = true + + resp, err := imsConfig.Register() + if err != nil { + return fmt.Errorf("error during client registration: %v", err) + } + + fmt.Printf("Status Code: %d\n", resp.StatusCode) + fmt.Println(resp.Body) + return nil + }, + } + + cmd.Flags().StringVarP(&imsConfig.RegisterURL, "url", "u", "", "Registration endpoint URL.") + 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).") + + return cmd +} diff --git a/cmd/root.go b/cmd/root.go index 4e0004e..85a3a02 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -55,6 +55,7 @@ func RootCmd(version string) *cobra.Command { decodeCmd(imsConfig), refreshCmd(imsConfig), adminCmd(imsConfig), + dcrCmd(imsConfig), ) return cmd } diff --git a/ims/config.go b/ims/config.go index 4b8e196..b54bff0 100644 --- a/ims/config.go +++ b/ims/config.go @@ -43,6 +43,9 @@ type Config struct { Guid string AuthSrc string DecodeFulfillableData bool + RegisterURL string + ClientName string + RedirectURIs []string } // Access token information diff --git a/ims/register.go b/ims/register.go new file mode 100644 index 0000000..88e94d8 --- /dev/null +++ b/ims/register.go @@ -0,0 +1,71 @@ +package ims + +import ( + "fmt" + "io" + "net/http" + "strings" +) + +// RegisterResponse contains the response from DCR registration +type RegisterResponse struct { + StatusCode int + Body string +} + +func (i Config) validateRegisterConfig() error { + switch { + case i.RegisterURL == "": + return fmt.Errorf("missing registration endpoint 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() (RegisterResponse, error) { + if err := i.validateRegisterConfig(); err != nil { + return RegisterResponse{}, fmt.Errorf("invalid parameters for client registration: %v", err) + } + + // Build redirect URIs JSON array + redirectURIsJSON := "[" + for idx, uri := range i.RedirectURIs { + if idx > 0 { + redirectURIsJSON += "," + } + redirectURIsJSON += fmt.Sprintf(`"%s"`, uri) + } + redirectURIsJSON += "]" + + payload := strings.NewReader(fmt.Sprintf(`{ + "client_name": "%s", + "redirect_uris": %s +}`, i.ClientName, redirectURIsJSON)) + + req, err := http.NewRequest("POST", i.RegisterURL, payload) + if err != nil { + return RegisterResponse{}, fmt.Errorf("error creating request: %v", err) + } + req.Header.Add("content-type", "application/json") + + res, err := http.DefaultClient.Do(req) + if err != nil { + return RegisterResponse{}, fmt.Errorf("error making registration request: %v", err) + } + defer res.Body.Close() + + body, err := io.ReadAll(res.Body) + if err != nil { + return RegisterResponse{}, fmt.Errorf("error reading response body: %v", err) + } + + return RegisterResponse{ + StatusCode: res.StatusCode, + Body: string(body), + }, nil +}