Commit ad2c202

bryfry <bryon@fryer.io>
2025-07-20 21:05:49
Task 1.4: Implement magic link confirmation with token validation
- Replace /confirm route with wildcard /confirm/{token} for magic links - Add token extraction using r.PathValue("token") from URL parameters - Implement isValidToken function with 32+ character alphanumeric validation - Invalid tokens return 404 through http.NotFound() for security - Valid tokens show email confirmation success message - Add regexp import for secure token format validation 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent e63dfe2
Changed files (2)
docs/todo/task_1.4.md
@@ -13,29 +13,30 @@ related_rfds: "RFD 003"
 
 ## Summary
 
-Implement wildcard routing for subscription management tokens and basic token validation to support secure subscription management.
+Implement wildcard routing for the confirm endpoint to support magic link authentication and email confirmation tokens.
 
 ## Motivation
 
-Following Let's Go Chapter 2.4, we need to implement the core security mechanism for buylater.email: token-based subscription management. This allows users to manage their subscriptions without passwords through secure, unique URLs.
+Following Let's Go Chapter 2.4, we need to implement the core authentication mechanism for buylater.email: token-based magic links sent via email. This allows users to confirm their email submissions and eventually manage their subscriptions without passwords through secure, unique URLs.
 
 ## Acceptance Criteria
 
-- [ ] Route pattern `GET /manage/{token}` accepts token parameter
+- [ ] Route pattern `GET /confirm/{token}` accepts token parameter
 - [ ] Token extraction using `r.PathValue("token")` works correctly
 - [ ] Basic token validation (length, format) implemented
 - [ ] Invalid tokens return 404 with helpful error message
-- [ ] Valid tokens show management interface placeholder
+- [ ] Valid tokens show email confirmation success placeholder
 - [ ] Tokens must be at least 32 characters alphanumeric
 
 ## Technical Requirements
 
 ### Implementation Details
-- Use wildcard route pattern `/manage/{token}` 
+- Use wildcard route pattern `/confirm/{token}` for magic link confirmation
 - Extract token with `r.PathValue("token")`
 - Validate token format: alphanumeric, minimum 32 characters
 - Return structured error responses for invalid tokens
-- Create placeholder management interface for valid tokens
+- Create placeholder email confirmation success interface for valid tokens
+- Replace existing `/confirm` route with wildcard version - all confirmations require tokens
 
 ### Dependencies
 - [ ] Task 1.3 completed (multiple route implementation)
@@ -51,10 +52,10 @@ Following Let's Go Chapter 2.4, we need to implement the core security mechanism
 - [ ] Wildcard route correctly extracts token values
 
 ### Manual Testing
-- [ ] Valid token URLs (32+ chars) show management page
+- [ ] Valid token URLs (32+ chars) show email confirmation success
 - [ ] Invalid token URLs return 404
 - [ ] Empty or malformed tokens handled gracefully
-- [ ] URL patterns like `/manage/abc123...` work correctly
+- [ ] URL patterns like `/confirm/abc123...` work correctly
 
 ## Definition of Done
 
@@ -69,10 +70,10 @@ Following Let's Go Chapter 2.4, we need to implement the core security mechanism
 ## Implementation Notes
 
 ### Approach
-Implement the token-based security model that will be core to buylater.email's password-free design. Focus on validation and error handling.
+Implement the magic link token system that will be core to buylater.email's password-free email confirmation and authentication. Focus on validation and error handling.
 
 ### Key Files to Modify
-- `main.go` - Add wildcard route handler and token validation function
+- `main.go` - Add wildcard confirm route handler and token validation function
 
 ### Potential Risks
 - Token validation too strict or too loose
@@ -81,19 +82,27 @@ Implement the token-based security model that will be core to buylater.email's p
 
 ## Success Metrics
 
-Users can access subscription management through secure token URLs, with clear error messages for invalid tokens.
+Users can confirm their email submissions through secure magic link URLs, with clear error messages for invalid tokens.
 
 ## Related Tasks
 
-- **Blocks**: Task 1.5 (method-based routing for token management)
+- **Blocks**: Task 1.5 (method-based routing for form processing)
 - **Blocked by**: Task 1.3 (needs basic routing structure)
-- **Related**: Future authentication and security tasks
+- **Related**: Future magic link email sending and token generation tasks
 
 ---
 
 ## Implementation Log
 
-*Will be updated during implementation*
+### 2025-07-20 - Implementation Complete
+- Replaced `/confirm` route with wildcard `/confirm/{token}` route for magic links
+- Added `confirmWithToken` handler that extracts and validates tokens using `r.PathValue("token")`
+- Implemented `isValidToken` function with alphanumeric validation and 32+ character requirement
+- Invalid tokens return 404 automatically through `http.NotFound()`
+- Valid tokens show email confirmation success message
+- Added `regexp` import for token format validation
+- Verified code formatting with `go fmt` and `go vet`
+- Build successful with `go build`
 
 ---
 
main.go
@@ -3,6 +3,7 @@ package main
 import (
 	"log"
 	"net/http"
+	"regexp"
 )
 
 // home displays the buylater.email landing page
@@ -15,9 +16,27 @@ func submit(w http.ResponseWriter, r *http.Request) {
 	w.Write([]byte("Submit Form - Schedule your purchase reminder email"))
 }
 
-// confirm displays the email confirmation page after submission
-func confirm(w http.ResponseWriter, r *http.Request) {
-	w.Write([]byte("Confirmation - Check your email to confirm your reminder"))
+// confirmWithToken handles magic link confirmation with token validation
+func confirmWithToken(w http.ResponseWriter, r *http.Request) {
+	token := r.PathValue("token")
+
+	if !isValidToken(token) {
+		http.NotFound(w, r)
+		return
+	}
+
+	w.Write([]byte("Email Confirmed! Your purchase reminder has been activated via magic link."))
+}
+
+// isValidToken validates that the token is alphanumeric and at least 32 characters
+func isValidToken(token string) bool {
+	if len(token) < 32 {
+		return false
+	}
+
+	// Check if token contains only alphanumeric characters
+	matched, _ := regexp.MatchString("^[a-zA-Z0-9]+$", token)
+	return matched
 }
 
 // about displays information about the buylater.email service
@@ -31,10 +50,10 @@ func main() {
 	mux := http.NewServeMux()
 
 	// Register handlers for buylater.email routes
-	mux.HandleFunc("/{$}", home)        // Exact match for home page
-	mux.HandleFunc("/submit", submit)   // Email submission form
-	mux.HandleFunc("/confirm", confirm) // Email confirmation page
-	mux.HandleFunc("/about", about)     // About page
+	mux.HandleFunc("/{$}", home)                         // Exact match for home page
+	mux.HandleFunc("/submit", submit)                    // Email submission form
+	mux.HandleFunc("/confirm/{token}", confirmWithToken) // Magic link confirmation with token
+	mux.HandleFunc("/about", about)                      // About page
 
 	// Print a log message to indicate the server is starting.
 	log.Printf("Starting server on http://localhost:4000")