package handlers import ( "html/template" "log" "net/http" "path/filepath" "custom-start-page/internal/auth" ) // AuthHandler handles authentication-related HTTP requests type AuthHandler struct { oauthService *auth.OAuthService userService *auth.UserService sessionStore SessionStore templates *template.Template } // SessionStore manages user sessions type SessionStore interface { CreateSession(w http.ResponseWriter, r *http.Request, userID string) error GetUserID(r *http.Request) (string, error) DestroySession(w http.ResponseWriter, r *http.Request) error } // NewAuthHandler creates a new auth handler func NewAuthHandler(oauthService *auth.OAuthService, userService *auth.UserService, sessionStore SessionStore) *AuthHandler { return NewAuthHandlerWithTemplates(oauthService, userService, sessionStore, nil) } // NewAuthHandlerWithTemplates creates a new auth handler with custom templates func NewAuthHandlerWithTemplates(oauthService *auth.OAuthService, userService *auth.UserService, sessionStore SessionStore, templates *template.Template) *AuthHandler { if templates == nil { // Parse templates templates = template.Must(template.ParseGlob(filepath.Join("templates", "*.html"))) template.Must(templates.ParseGlob(filepath.Join("templates", "layouts", "*.html"))) } return &AuthHandler{ oauthService: oauthService, userService: userService, sessionStore: sessionStore, templates: templates, } } // HandleOAuthInitiate initiates the OAuth flow // GET /auth/oauth/:provider func (h *AuthHandler) HandleOAuthInitiate(w http.ResponseWriter, r *http.Request) { // Extract provider from URL path provider := r.PathValue("provider") if provider == "" { http.Error(w, "Provider not specified", http.StatusBadRequest) return } // Generate OAuth redirect URL redirectURL, err := h.oauthService.InitiateOAuth(provider) if err != nil { log.Printf("Failed to initiate OAuth: %v", err) http.Error(w, "Failed to initiate OAuth", http.StatusInternalServerError) return } // Redirect to OAuth provider http.Redirect(w, r, redirectURL, http.StatusTemporaryRedirect) } // HandleOAuthCallback handles the OAuth callback // GET /auth/callback/:provider func (h *AuthHandler) HandleOAuthCallback(w http.ResponseWriter, r *http.Request) { // Extract provider from URL path provider := r.PathValue("provider") if provider == "" { http.Error(w, "Provider not specified", http.StatusBadRequest) return } // Get code and state from query parameters code := r.URL.Query().Get("code") state := r.URL.Query().Get("state") if code == "" { // Check for error from OAuth provider if errMsg := r.URL.Query().Get("error"); errMsg != "" { log.Printf("OAuth error: %s", errMsg) http.Redirect(w, r, "/login?error=oauth_failed", http.StatusTemporaryRedirect) return } http.Error(w, "Authorization code not provided", http.StatusBadRequest) return } if state == "" { http.Error(w, "State parameter not provided", http.StatusBadRequest) return } // Exchange code for token token, err := h.oauthService.HandleOAuthCallback(r.Context(), provider, code, state) if err != nil { log.Printf("Failed to handle OAuth callback: %v", err) http.Redirect(w, r, "/login?error=oauth_failed", http.StatusTemporaryRedirect) return } // Get or create user from OAuth provider user, err := h.userService.GetOrCreateUserFromGoogle(r.Context(), token, h.oauthService.GetGoogleConfig()) if err != nil { log.Printf("Failed to get or create user: %v", err) http.Redirect(w, r, "/login?error=user_creation_failed", http.StatusTemporaryRedirect) return } // Create session if err := h.sessionStore.CreateSession(w, r, user.ID); err != nil { log.Printf("Failed to create session: %v", err) http.Error(w, "Failed to create session", http.StatusInternalServerError) return } // Redirect to dashboard http.Redirect(w, r, "/dashboard", http.StatusTemporaryRedirect) } // HandleLogout logs out the user // POST /logout func (h *AuthHandler) HandleLogout(w http.ResponseWriter, r *http.Request) { if err := h.sessionStore.DestroySession(w, r); err != nil { log.Printf("Failed to destroy session: %v", err) } // Redirect to login page http.Redirect(w, r, "/login", http.StatusTemporaryRedirect) } // HandleLogin displays the login page // GET /login func (h *AuthHandler) HandleLogin(w http.ResponseWriter, r *http.Request) { // Check if user is already logged in if userID, err := h.sessionStore.GetUserID(r); err == nil && userID != "" { http.Redirect(w, r, "/dashboard", http.StatusTemporaryRedirect) return } // Get error message if any errorMsg := "" if errParam := r.URL.Query().Get("error"); errParam != "" { switch errParam { case "oauth_failed": errorMsg = "Authentication failed. Please try again." case "user_creation_failed": errorMsg = "Failed to create user account. Please try again." default: errorMsg = "An error occurred. Please try again." } } // Render login template data := map[string]interface{}{ "Error": errorMsg, "OAuthProviders": []map[string]string{}, // Empty for now, can be extended } if err := h.templates.ExecuteTemplate(w, "login.html", data); err != nil { log.Printf("Failed to render login template: %v", err) http.Error(w, "Internal server error", http.StatusInternalServerError) } }