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)
}
}