diff --git a/esi_sso.go b/esi_sso.go index a8e6bbb..4a70973 100644 --- a/esi_sso.go +++ b/esi_sso.go @@ -44,6 +44,7 @@ type SSO struct { db DB mu sync.Mutex server *http.Server + mux *http.ServeMux state string callbackChan chan struct { code string @@ -78,6 +79,12 @@ func NewSSO(clientID, redirectURI string, scopes []string) (*SSO, error) { return s, nil } +// SetMuxer allows the SSO to use an existing HTTP muxer instead of creating its own server +func (s *SSO) SetMuxer(mux *http.ServeMux) { + s.mux = mux + logger.Debug("SSO configured to use existing HTTP muxer") +} + func (s *SSO) initDB() error { logger.Debug("Initializing SSO database schema") err := s.db.AutoMigrate(&Token{}) @@ -173,15 +180,20 @@ func (s *SSO) startAuthFlow(ctx context.Context, characterName string) error { logger.Info("Please visit this URL to authenticate: \n%s", authURL) logger.Info("Waiting for authentication...") - // Start callback server - logger.Debug("Starting callback server") - server, err := s.startCallbackServer() - if err != nil { - logger.Error("Failed to start callback server: %v", err) - return err + // Setup callback handling + if s.mux != nil { + logger.Debug("Using existing HTTP muxer for callback handling") + s.setupCallbackHandler() + } else { + logger.Debug("Starting dedicated callback server") + server, err := s.startCallbackServer() + if err != nil { + logger.Error("Failed to start callback server: %v", err) + return err + } + s.server = server + defer server.Shutdown(ctx) } - s.server = server - defer server.Shutdown(ctx) // Wait for callback logger.Debug("Waiting for authentication callback") @@ -231,8 +243,55 @@ func (s *SSO) buildAuthURL(challenge, state string) string { return issuerAuthorizeURL + "?" + q.Encode() } +func (s *SSO) setupCallbackHandler() { + u, err := url.Parse(s.redirectURI) + if err != nil { + logger.Error("Failed to parse redirect URI for callback handler: %v", err) + return + } + + logger.Debug("Setting up callback handler on path: %s", u.Path) + s.mux.HandleFunc(u.Path, s.handleCallback) +} + +func (s *SSO) handleCallback(w http.ResponseWriter, r *http.Request) { + logger.Debug("Received callback request: %s %s", r.Method, r.URL.String()) + if r.Method != http.MethodGet { + logger.Warning("Invalid callback method: %s", r.Method) + w.WriteHeader(http.StatusMethodNotAllowed) + s.callbackChan <- struct { + code string + state string + err error + }{"", "", errors.New("method not allowed")} + return + } + q := r.URL.Query() + code := q.Get("code") + st := q.Get("state") + if code == "" || st == "" || st != s.state { + logger.Error("Invalid SSO response: code=%s, state=%s, expected_state=%s", code, st, s.state) + w.WriteHeader(http.StatusBadRequest) + _, _ = w.Write([]byte("Invalid SSO response")) + s.callbackChan <- struct { + code string + state string + err error + }{"", "", errors.New("invalid state")} + return + } + logger.Info("Received valid callback, exchanging token for code: %s", code) + w.Header().Set("Content-Type", "text/html") + _, _ = w.Write([]byte("
You can close this window.
")) + s.callbackChan <- struct { + code string + state string + err error + }{code, st, nil} +} + func (s *SSO) startCallbackServer() (*http.Server, error) { - logger.Debug("Starting callback server for redirect URI: %s", s.redirectURI) + logger.Debug("Starting dedicated callback server for redirect URI: %s", s.redirectURI) u, err := url.Parse(s.redirectURI) if err != nil { logger.Error("Failed to parse redirect URI: %v", err) @@ -253,45 +312,7 @@ func (s *SSO) startCallbackServer() (*http.Server, error) { logger.Debug("Callback server will listen on %s", hostPort) mux := http.NewServeMux() - mux.HandleFunc(u.Path, func(w http.ResponseWriter, r *http.Request) { - logger.Debug("Received callback request: %s %s", r.Method, r.URL.String()) - if r.Method != http.MethodGet { - logger.Warning("Invalid callback method: %s", r.Method) - w.WriteHeader(http.StatusMethodNotAllowed) - s.callbackChan <- struct { - code string - state string - err error - }{"", "", errors.New("method not allowed")} - return - } - q := r.URL.Query() - code := q.Get("code") - st := q.Get("state") - if code == "" || st == "" || st != s.state { - logger.Error("Invalid SSO response: code=%s, state=%s, expected_state=%s", code, st, s.state) - w.WriteHeader(http.StatusBadRequest) - _, _ = w.Write([]byte("Invalid SSO response")) - s.callbackChan <- struct { - code string - state string - err error - }{"", "", errors.New("invalid state")} - return - } - logger.Info("Received valid callback, exchanging token for code: %s", code) - w.Header().Set("Content-Type", "text/html") - _, _ = w.Write([]byte("You can close this window.
")) - s.callbackChan <- struct { - code string - state string - err error - }{code, st, nil} - go func() { - time.Sleep(200 * time.Millisecond) - _ = s.server.Shutdown(context.Background()) - }() - }) + mux.HandleFunc(u.Path, s.handleCallback) ln, err := net.Listen("tcp", hostPort) if err != nil { diff --git a/main.go b/main.go index fb41432..682bd30 100644 --- a/main.go +++ b/main.go @@ -4,7 +4,7 @@ import ( "context" "flag" "fmt" - + "net/http" "os" "strings" @@ -33,7 +33,7 @@ func main() { logger.Error("Failed to load options %v", err) return } - + // Create SSO instance sso, err := NewSSO( options.ClientID, @@ -45,7 +45,32 @@ func main() { return } - // Get token for character + // Setup HTTP server + mux := http.NewServeMux() + + // Configure SSO to use existing muxer + sso.SetMuxer(mux) + + // Add your own routes + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + w.Write([]byte("EVE PI Server Running")) + }) + + // Start server + server := &http.Server{ + Addr: ":3000", + Handler: mux, + } + + logger.Info("Starting web server on :3000") + go func() { + if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed { + logger.Error("Server failed: %v", err) + } + }() + + // Get token for character (this will add callback route temporarily) token, err := sso.GetToken(context.Background(), "PhatPhuckDave") if err != nil { logger.Error("Failed to get token %v", err)