diff --git a/oauth/oauth.go b/oauth/oauth.go index c554caa..c425da9 100644 --- a/oauth/oauth.go +++ b/oauth/oauth.go @@ -8,6 +8,7 @@ import ( "math/rand" "net/http" "net/url" + "os" "os/exec" "runtime" "strconv" @@ -56,13 +57,12 @@ type AuthenticateUserFuncConfig struct { } // Initiate an *AuthorizedClient with a given APPKEY, SECRET -// TODO: Include the user's given callback URL, as if someone wants to host off-prem they should be able to -// TODO: Investigate the previous statement, localhost might work for any implementation? -func Initiate(APPKEY, SECRET string) *AuthorizedClient { +func Initiate(APPKEY, SECRET, CBURL string) *AuthorizedClient { conf := &oauth2.Config{ ClientID: APPKEY, // Schwab App Key ClientSecret: SECRET, // Schwab App Secret + RedirectURL: CBURL, Endpoint: oauth2.Endpoint{ AuthURL: "https://api.schwabapi.com/v1/oauth/authorize", @@ -127,9 +127,6 @@ func authenticateUser(oauthConfig *oauth2.Config, options ...AuthenticateUserOpt sslcli := &http.Client{Transport: tr} ctx := context.WithValue(context.Background(), oauth2.HTTPClient, sslcli) - // Redirect user to consent page to ask for permission - // for the scopes specified above. - oauthConfig.RedirectURL = fmt.Sprintf("https://%s", IP) // Some random string, random for each request oauthStateString := randSeq(16) ctx = context.WithValue(ctx, oauthStateStringContextKey, oauthStateString) @@ -191,7 +188,11 @@ func startHTTPServer(ctx context.Context, conf *oauth2.Config) (clientChan chan http.HandleFunc("/", callbackHandler(ctx, conf, clientChan)) - srv := &http.Server{} + addr, ok := os.LookupEnv("OAUTH_CALLBACK_ADDR") + if !ok { + addr = "127.0.0.1:443" + } + srv := &http.Server{Addr: addr} // handle server shutdown signal go func() { @@ -227,9 +228,11 @@ func callbackHandler(ctx context.Context, oauthConfig *oauth2.Config, clientChan return func(w http.ResponseWriter, r *http.Request) { requestStateString := ctx.Value(oauthStateStringContextKey).(string) responseStateString := r.FormValue("state") + // if the response state doesn not match the request state, return an error if responseStateString != requestStateString { fmt.Printf("invalid oauth state, expected '%s', got '%s'\n", requestStateString, responseStateString) - http.Redirect(w, r, "/", http.StatusTemporaryRedirect) + w.Write([]byte(fmt.Sprintf("invalid oauth state, expected '%s', got '%s'", requestStateString, responseStateString))) + w.WriteHeader(http.StatusBadRequest) return } @@ -237,7 +240,8 @@ func callbackHandler(ctx context.Context, oauthConfig *oauth2.Config, clientChan token, err := oauthConfig.Exchange(ctx, code) if err != nil { fmt.Printf("oauthoauthConfig.Exchange() failed with '%s'\n", err) - http.Redirect(w, r, "/", http.StatusTemporaryRedirect) + w.Write([]byte(fmt.Sprintf("oauthoauthConfig.Exchange() failed with '%s'\n", err))) + w.WriteHeader(http.StatusBadRequest) return } // The HTTP Client returned by oauthConfig.Client will refresh the token as necessary