A deployable markdown editor that connects with your self hosted files and lets you edit in a beautiful interface
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Fix OAuth session cookie persistence by using goth directly

- Replace gothic.BeginAuthHandler/CompleteUserAuth with direct goth provider calls
- This gives us better control over session storage and state management
- Add detailed logging to debug cookie flow
- Fix gothic session store options initialization
- Add compiled binary to .gitignore
- Session cookies now properly persist between login and callback

+95 -29
+1
backend/.gitignore
··· 1 1 # Binaries 2 2 bin/ 3 + server 3 4 *.exe 4 5 *.dll 5 6 *.so
+87 -24
backend/internal/api/handlers/auth.go
··· 8 8 "os" 9 9 "time" 10 10 11 + "github.com/markbates/goth" 11 12 "github.com/markbates/goth/gothic" 12 13 "github.com/yourusername/markedit/internal/auth" 13 14 "github.com/yourusername/markedit/internal/database" ··· 31 32 Provider string `json:"provider"` 32 33 } 33 34 35 + // min returns the minimum of two integers 36 + func min(a, b int) int { 37 + if a < b { 38 + return a 39 + } 40 + return b 41 + } 42 + 34 43 // BeginAuth starts the OAuth flow 35 44 func (h *AuthHandler) BeginAuth(w http.ResponseWriter, r *http.Request) { 36 - // Set the provider in the query string for gothic 37 - q := r.URL.Query() 38 - q.Add("provider", "github") 39 - r.URL.RawQuery = q.Encode() 45 + log.Printf("BeginAuth - Starting OAuth flow for URL: %s", r.URL.String()) 40 46 41 - log.Printf("BeginAuth - Starting OAuth flow") 47 + // Get the GitHub provider 48 + provider, err := goth.GetProvider("github") 49 + if err != nil { 50 + log.Printf("Failed to get GitHub provider: %v", err) 51 + http.Error(w, "Authentication provider not configured", http.StatusInternalServerError) 52 + return 53 + } 42 54 43 - // Get the auth URL from goth 44 - gothic.BeginAuthHandler(w, r) 55 + // Get a new session from the provider 56 + sess, err := provider.BeginAuth(gothic.SetState(r)) 57 + if err != nil { 58 + log.Printf("Failed to begin auth: %v", err) 59 + http.Error(w, "Failed to begin authentication", http.StatusInternalServerError) 60 + return 61 + } 45 62 46 - log.Printf("BeginAuth - Cookies set: %v", w.Header().Get("Set-Cookie")) 63 + // Get the auth URL 64 + authURL, err := sess.GetAuthURL() 65 + if err != nil { 66 + log.Printf("Failed to get auth URL: %v", err) 67 + http.Error(w, "Failed to get authentication URL", http.StatusInternalServerError) 68 + return 69 + } 70 + 71 + // Store the session state in gothic's session store 72 + if err := gothic.StoreInSession("github", sess.Marshal(), r, w); err != nil { 73 + log.Printf("Failed to store session: %v", err) 74 + http.Error(w, "Failed to store session", http.StatusInternalServerError) 75 + return 76 + } 77 + 78 + log.Printf("BeginAuth - Session stored, redirecting to: %s", authURL) 79 + log.Printf("BeginAuth - Response Set-Cookie headers: %v", w.Header()["Set-Cookie"]) 80 + 81 + // Redirect to GitHub 82 + http.Redirect(w, r, authURL, http.StatusTemporaryRedirect) 47 83 } 48 84 49 85 // CallbackAuth handles the OAuth callback 50 86 func (h *AuthHandler) CallbackAuth(w http.ResponseWriter, r *http.Request) { 51 - // Set the provider in the query string for gothic 52 - q := r.URL.Query() 53 - q.Add("provider", "github") 54 - r.URL.RawQuery = q.Encode() 87 + log.Printf("Callback - Full URL: %s", r.URL.String()) 88 + log.Printf("Callback - Cookies received:") 89 + for _, cookie := range r.Cookies() { 90 + log.Printf(" - %s: %s (Path: %s, Domain: %s)", 91 + cookie.Name, cookie.Value[:min(20, len(cookie.Value))], 92 + cookie.Path, cookie.Domain) 93 + } 55 94 56 - // Log cookies for debugging 57 - log.Printf("Callback - Cookies received: %v", r.Cookies()) 95 + // Get the provider 96 + provider, err := goth.GetProvider("github") 97 + if err != nil { 98 + log.Printf("Failed to get GitHub provider: %v", err) 99 + http.Error(w, "Authentication provider not configured", http.StatusInternalServerError) 100 + return 101 + } 58 102 59 - // Complete OAuth flow 60 - gothUser, err := gothic.CompleteUserAuth(w, r) 103 + // Retrieve the session from gothic's store 104 + sessionData, err := gothic.GetFromSession("github", r) 105 + if err != nil { 106 + log.Printf("Failed to get session from store: %v", err) 107 + http.Redirect(w, r, "http://localhost:4321/?error=session_not_found", http.StatusTemporaryRedirect) 108 + return 109 + } 110 + 111 + // Unmarshal the session 112 + sess, err := provider.UnmarshalSession(sessionData) 61 113 if err != nil { 62 - log.Printf("OAuth callback error: %v", err) 114 + log.Printf("Failed to unmarshal session: %v", err) 115 + http.Redirect(w, r, "http://localhost:4321/?error=session_invalid", http.StatusTemporaryRedirect) 116 + return 117 + } 63 118 64 - // Try to start auth again if session not found 65 - if err.Error() == "could not find a matching session for this request" { 66 - log.Printf("Session not found, redirecting to begin auth") 67 - http.Redirect(w, r, "http://localhost:4321/?error=session_expired", http.StatusTemporaryRedirect) 68 - return 69 - } 119 + // Get user data from GitHub 120 + params := r.URL.Query() 121 + _, err = sess.Authorize(provider, params) 122 + if err != nil { 123 + log.Printf("Failed to authorize session: %v", err) 124 + http.Redirect(w, r, "http://localhost:4321/?error=authorization_failed", http.StatusTemporaryRedirect) 125 + return 126 + } 70 127 71 - http.Error(w, fmt.Sprintf("Authentication failed: %v", err), http.StatusInternalServerError) 128 + // Fetch user details 129 + gothUser, err := provider.FetchUser(sess) 130 + if err != nil { 131 + log.Printf("Failed to fetch user: %v", err) 132 + http.Error(w, "Failed to fetch user details", http.StatusInternalServerError) 72 133 return 73 134 } 135 + 136 + log.Printf("OAuth successful for user: %s", gothUser.NickName) 74 137 75 138 // Create or update user in database 76 139 user := &database.User{
+7 -5
backend/internal/auth/github.go
··· 41 41 42 42 store := sessions.NewCookieStore(key) 43 43 store.MaxAge(maxAge) 44 - store.Options.Path = "/" 45 - store.Options.HttpOnly = true 46 - store.Options.Secure = isProd 47 - store.Options.SameSite = http.SameSiteLaxMode // Important for OAuth redirects 48 - // Don't set Domain for localhost - let browser handle it 44 + store.Options = &sessions.Options{ 45 + Path: "/", 46 + HttpOnly: true, 47 + Secure: isProd, 48 + SameSite: http.SameSiteLaxMode, // Important for OAuth redirects 49 + MaxAge: maxAge, 50 + } 49 51 50 52 gothic.Store = store 51 53