Initial commit: Custom Start Page application with authentication and DynamoDB storage
This commit is contained in:
170
internal/handlers/auth_handler.go
Normal file
170
internal/handlers/auth_handler.go
Normal file
@@ -0,0 +1,170 @@
|
||||
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)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user