func (h *AuthorizationCodeHandler) Parameters() []cli.AuthParam {
	return []cli.AuthParam{
		{Name: "client_id", Required: true, Help: "OAuth 2.0 Client ID"},
		{Name: "client_secret", Required: false, Help: "OAuth 2.0 Client Secret if exists"},
		{Name: "authorize_url", Required: true, Help: "OAuth 2.0 authorization URL, e.g. https://api.example.com/oauth/authorize"},
		{Name: "token_url", Required: true, Help: "OAuth 2.0 token URL, e.g. https://api.example.com/oauth/token"},
		{Name: "scopes", Help: "Optional scopes to request in the token"},
	}
}
Parameters returns a list of OAuth2 Authorization Code inputs.
package oauth
import (
	"bufio"
	"crypto/rand"
	"crypto/sha256"
	"encoding/base64"
	"fmt"
	"net/http"
	"net/url"
	"os"
	"os/exec"
	"runtime"
	"strings"
	"time"
	"context"
	"github.com/danielgtaylor/restish/cli"
	"golang.org/x/oauth2"
)
var htmlSuccess = `
<html>
  <style>
    @keyframes bg {
      from {background: white;}
      to {background: #5fafd7;}
    }
    @keyframes x {
      from {transform: rotate(0deg) skew(30deg, 20deg);}
      to {transform: rotate(-45deg);}
    }
    @keyframes fade {
      from {opacity: 0;}
      to {opacity: 1;}
    }
    body { font-family: sans-serif; margin-top: 8%; animation: bg 1.5s ease-out; animation-fill-mode: forwards; }
    p { width: 80%; }
    .check {
      margin: auto;
      width: 18%;
      height: 15%;
      border-left: 16px solid white;
      border-bottom: 16px solid white;
      animation: x 0.7s cubic-bezier(0.175, 0.885, 0.32, 1.275);
      animation-fill-mode: forwards;
    }
    .msg {
      margin: auto;
      margin-top: 180px;
      width: 40%;
      background: white;
      padding: 20px 32px;
      border-radius: 10px;
      animation: fade 2s;
      animation-fill-mode: forwards;
      box-shadow: 0px 15px 15px -15px rgba(0, 0, 0, 0.5);
    }
  </style>
  <body>
    <div class="check"></div>
    <div class="msg">
        <h1>Login Successful!</h1>
        Please return to the terminal. You may now close this window.
      </p>
    </div>
  </body>
</html>
`
var htmlError = `
<html>
  <style>
    @keyframes bg {
      from {background: white;}
      to {background: #E94F37;}
    }
    @keyframes x {
      from {transform: scaleY(0);}
      to {transform: scaleY(1) rotate(-90deg);}
    }
    @keyframes fade {
      from {opacity: 0;}
      to {opacity: 1;}
    }
    body { font-family: sans-serif; margin-top: 15%; animation: bg 1.5s ease-out; animation-fill-mode: forwards; }
    p { width: 80%; }
    .x, .x:after {
      margin: auto;
      background: white;
      width: 20%;
      height: 16px;
      border-radius: 3px;
      transform: rotate(-45deg);
      animation: x 0.7s cubic-bezier(0.175, 0.885, 0.32, 1.275);
      animation-fill-mode: forwards;
    }
    .x:after {
      content: "";
      display: block;
      width: 100%;
      transform: rotate(90deg);
    }
    .msg {
      margin: auto;
      margin-top: 200px;
      width: 40%;
      background: white;
      padding: 20px 32px;
      border-radius: 10px;
      animation: fade 2s;
      animation-fill-mode: forwards;
      box-shadow: 0px 15px 15px -15px rgba(0, 0, 0, 0.5);
    }
  </style>
  <body>
    <div style="transform: rotate(-45deg);"><div class="x"></div></div>
    <div class="msg">
        <h1>Error: $ERROR</h1>
        $DETAILS
      </p>
    </div>
  </body>
</html>
`
func open(url string) error {
	var cmd string
	var args []string
	switch runtime.GOOS {
	case "windows":
		cmd = "cmd"
		args = []string{"/c", "start"}
	case "darwin":  
		cmd = "open"
	default:  
		cmd = "xdg-open"
	}
	args = append(args, url)
	return exec.Command(cmd, args...).Start()
}
func getInput(input chan string) {
	r := bufio.NewReader(os.Stdin)
	result, err := r.ReadString('\n')
	if err != nil {
		panic(err)
	}
	input <- strings.TrimRight(result, "\n")
}
type authHandler struct {
	c chan string
}
func (h authHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "text/html")
	if err := r.URL.Query().Get("error"); err != "" {
		details := r.URL.Query().Get("error_description")
		rendered := strings.Replace(strings.Replace(htmlError, "$ERROR", err, 1), "$DETAILS", details, 1)
		w.Write([]byte(rendered))
		h.c <- ""
		return
	}
	h.c <- r.URL.Query().Get("code")
	w.Write([]byte(htmlSuccess))
}
type AuthorizationCodeTokenSource struct {
	ClientID       string
	ClientSecret   string
	AuthorizeURL   string
	TokenURL       string
	EndpointParams *url.Values
	Scopes         []string
}
func (ac *AuthorizationCodeTokenSource) Token() (*oauth2.Token, error) {
	verifierBytes := make([]byte, 32)
	if _, err := rand.Read(verifierBytes); err != nil {
		return nil, err
	}
	verifier := base64.RawURLEncoding.EncodeToString(verifierBytes)
	shaBytes := sha256.Sum256([]byte(verifier))
	challenge := base64.RawURLEncoding.EncodeToString(shaBytes[:])
	authorizeURL, err := url.Parse(ac.AuthorizeURL)
	if err != nil {
		panic(err)
	}
	aq := authorizeURL.Query()
	aq.Set("response_type", "code")
	aq.Set("code_challenge", challenge)
	aq.Set("code_challenge_method", "S256")
	aq.Set("client_id", ac.ClientID)
	aq.Set("redirect_uri", "http://localhost:8484/")
	aq.Set("scope", strings.Join(ac.Scopes, " "))
	if ac.EndpointParams != nil {
		for k, v := range *ac.EndpointParams {
			aq.Set(k, v[0])
		}
	}
	authorizeURL.RawQuery = aq.Encode()
	codeChan := make(chan string)
	handler := authHandler{
		c: codeChan,
	}
	s := &http.Server{
		Addr:           "localhost:8484",
		Handler:        handler,
		ReadTimeout:    5 * time.Second,
		WriteTimeout:   5 * time.Second,
		MaxHeaderBytes: 1024,
	}
	go func() {
		if err := s.ListenAndServe(); err != http.ErrServerClosed {
			panic(err)
		}
	}()
	fmt.Println("Open your browser to log in using the URL:")
	fmt.Println(authorizeURL.String())
	open(authorizeURL.String())
	fmt.Print("Alternatively, enter the code manually: ")
	manualCodeChan := make(chan string)
	go getInput(manualCodeChan)
	var code string
	select {
	case code = <-codeChan:
	case code = <-manualCodeChan:
	}
	fmt.Println("")
	s.Shutdown(context.Background())
	if code == "" {
		fmt.Println("Unable to get a code. See browser for details. Aborting!")
		os.Exit(1)
	}
	payload := url.Values{}
	payload.Set("grant_type", "authorization_code")
	payload.Set("client_id", ac.ClientID)
	payload.Set("code_verifier", verifier)
	payload.Set("code", code)
	payload.Set("redirect_uri", "http://localhost:8484/")
	if ac.ClientSecret != "" {
		payload.Set("client_secret", ac.ClientSecret)
	}
	return requestToken(ac.TokenURL, payload.Encode())
}
type AuthorizationCodeHandler struct{}