171 lines
5.2 KiB
Go
171 lines
5.2 KiB
Go
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)
|
|
}
|
|
}
|