Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 13 additions & 9 deletions oauth/oauth.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"math/rand"
"net/http"
"net/url"
"os"
"os/exec"
"runtime"
"strconv"
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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() {
Expand Down Expand Up @@ -227,17 +228,20 @@ 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
}

code := r.FormValue("code")
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
Expand Down