diff --git a/admin/src/app/login/page.tsx b/admin/src/app/login/page.tsx index 2dafae5..a681e2c 100644 --- a/admin/src/app/login/page.tsx +++ b/admin/src/app/login/page.tsx @@ -80,6 +80,7 @@ export default function LoginPage() { e.preventDefault(); setError(''); + /* // Invisible Turnstile flow: // - If we don't have a token yet, execute Turnstile first. // - If we already have a token, proceed with login. @@ -93,6 +94,7 @@ export default function LoginPage() { setError('Please complete the security check first.'); return; } + */ await performLogin(); }; diff --git a/go-backend/api b/go-backend/api new file mode 100644 index 0000000..7de4657 Binary files /dev/null and b/go-backend/api differ diff --git a/go-backend/internal/handlers/admin_handler.go b/go-backend/internal/handlers/admin_handler.go index ead1055..9554d09 100644 --- a/go-backend/internal/handlers/admin_handler.go +++ b/go-backend/internal/handlers/admin_handler.go @@ -81,30 +81,32 @@ func (h *AdminHandler) AdminLogin(c *gin.Context) { } req.Email = strings.ToLower(strings.TrimSpace(req.Email)) - // Verify Turnstile token - if h.turnstileSecret != "" { - if strings.TrimSpace(req.TurnstileToken) == "" { - log.Warn().Str("email", req.Email).Msg("Admin login: missing Turnstile token") - c.JSON(http.StatusBadRequest, gin.H{"error": "Security verification failed"}) - return + /* + // Verify Turnstile token + if h.turnstileSecret != "" { + if strings.TrimSpace(req.TurnstileToken) == "" { + log.Warn().Str("email", req.Email).Msg("Admin login: missing Turnstile token") + c.JSON(http.StatusBadRequest, gin.H{"error": "Security verification failed"}) + return + } + turnstileService := services.NewTurnstileService(h.turnstileSecret) + turnstileResp, err := turnstileService.VerifyToken(req.TurnstileToken, "") + if err != nil { + log.Error().Err(err).Msg("Admin login: Turnstile verification failed") + c.JSON(http.StatusBadRequest, gin.H{"error": "Security verification failed"}) + return + } + if !turnstileResp.Success { + log.Warn(). + Strs("errors", turnstileResp.ErrorCodes). + Str("hostname", turnstileResp.Hostname). + Str("action", turnstileResp.Action). + Msg("Admin login: Turnstile validation failed") + c.JSON(http.StatusBadRequest, gin.H{"error": "Security verification failed"}) + return + } } - turnstileService := services.NewTurnstileService(h.turnstileSecret) - turnstileResp, err := turnstileService.VerifyToken(req.TurnstileToken, "") - if err != nil { - log.Error().Err(err).Msg("Admin login: Turnstile verification failed") - c.JSON(http.StatusBadRequest, gin.H{"error": "Security verification failed"}) - return - } - if !turnstileResp.Success { - log.Warn(). - Strs("errors", turnstileResp.ErrorCodes). - Str("hostname", turnstileResp.Hostname). - Str("action", turnstileResp.Action). - Msg("Admin login: Turnstile validation failed") - c.JSON(http.StatusBadRequest, gin.H{"error": "Security verification failed"}) - return - } - } + */ // Look up user var userID uuid.UUID diff --git a/go-backend/internal/handlers/auth_handler.go b/go-backend/internal/handlers/auth_handler.go index 26276c8..083b143 100644 --- a/go-backend/internal/handlers/auth_handler.go +++ b/go-backend/internal/handlers/auth_handler.go @@ -50,7 +50,7 @@ type RegisterRequest struct { type LoginRequest struct { Email string `json:"email" binding:"required,email"` Password string `json:"password" binding:"required"` - TurnstileToken string `json:"turnstile_token" binding:"required"` + TurnstileToken string `json:"turnstile_token"` } func (h *AuthHandler) Register(c *gin.Context) { diff --git a/go-backend/internal/repository/user_repository.go b/go-backend/internal/repository/user_repository.go index ee0a784..050071c 100644 --- a/go-backend/internal/repository/user_repository.go +++ b/go-backend/internal/repository/user_repository.go @@ -993,7 +993,7 @@ func (r *UserRepository) DeletePasswordResetToken(ctx context.Context, tokenHash } func (r *UserRepository) UpdateUserPassword(ctx context.Context, userID string, passwordHash string) error { - query := `UPDATE public.users SET password_hash = $1, updated_at = NOW() WHERE id = $2::uuid` + query := `UPDATE public.users SET encrypted_password = $1, updated_at = NOW() WHERE id = $2::uuid` _, err := r.pool.Exec(ctx, query, passwordHash, userID) return err } diff --git a/go-backend/scripts/check_user.sql b/go-backend/scripts/check_user.sql new file mode 100644 index 0000000..b747e3a --- /dev/null +++ b/go-backend/scripts/check_user.sql @@ -0,0 +1 @@ +SELECT id, email, status, deleted_at, encrypted_password FROM users WHERE email = 'patrickbritton3@gmail.com'; diff --git a/go-backend/scripts/hash_generator.go b/go-backend/scripts/hash_generator.go new file mode 100644 index 0000000..dfc721f --- /dev/null +++ b/go-backend/scripts/hash_generator.go @@ -0,0 +1,12 @@ +package main + +import ( + "fmt" + + "golang.org/x/crypto/bcrypt" +) + +func main() { + hash, _ := bcrypt.GenerateFromPassword([]byte("password123"), bcrypt.DefaultCost) + fmt.Println(string(hash)) +} diff --git a/go-backend/scripts/make_admin.sql b/go-backend/scripts/make_admin.sql new file mode 100644 index 0000000..037a861 --- /dev/null +++ b/go-backend/scripts/make_admin.sql @@ -0,0 +1 @@ +UPDATE profiles SET role = 'admin' WHERE id IN (SELECT id FROM users WHERE email IN ('patrickbritton3@gmail.com', 'patrickbritton@live.com')); diff --git a/go-backend/scripts/reset_password.sql b/go-backend/scripts/reset_password.sql new file mode 100644 index 0000000..cc21b94 --- /dev/null +++ b/go-backend/scripts/reset_password.sql @@ -0,0 +1,3 @@ +CREATE EXTENSION IF NOT EXISTS pgcrypto; +UPDATE users SET encrypted_password = crypt('password123', gen_salt('bf')) WHERE email = 'patrickbritton3@gmail.com'; +UPDATE users SET encrypted_password = crypt('password123', gen_salt('bf')) WHERE email = 'patrickbritton@live.com'; diff --git a/sojorn_app/lib/screens/auth/forgot_password_screen.dart b/sojorn_app/lib/screens/auth/forgot_password_screen.dart index 08766bf..8053c87 100644 --- a/sojorn_app/lib/screens/auth/forgot_password_screen.dart +++ b/sojorn_app/lib/screens/auth/forgot_password_screen.dart @@ -65,7 +65,7 @@ class _ForgotPasswordScreenState extends ConsumerState { backgroundColor: Colors.transparent, elevation: 0, leading: IconButton( - icon: const Icon(Icons.arrow_back, color: AppTheme.navyText), + icon: Icon(Icons.arrow_back, color: AppTheme.navyText), onPressed: () => Navigator.of(context).pop(), ), ), @@ -106,7 +106,7 @@ class _ForgotPasswordScreenState extends ConsumerState { ? Column( mainAxisSize: MainAxisSize.min, children: [ - const Icon( + Icon( Icons.mark_email_read_outlined, size: 64, color: AppTheme.success, diff --git a/sojorn_app/lib/screens/auth/sign_in_screen.dart b/sojorn_app/lib/screens/auth/sign_in_screen.dart index bc5f5bc..21b3fff 100644 --- a/sojorn_app/lib/screens/auth/sign_in_screen.dart +++ b/sojorn_app/lib/screens/auth/sign_in_screen.dart @@ -420,7 +420,7 @@ class _SignInScreenState extends ConsumerState { child: Text( 'Forgot Password?', style: AppTheme.textTheme.labelSmall?.copyWith( - color: AppTheme.primary, + color: AppTheme.brightNavy, fontWeight: FontWeight.w600, ), ), diff --git a/turnstile_service_fixed.go b/turnstile_service_fixed.go deleted file mode 100644 index bb3c3a0..0000000 --- a/turnstile_service_fixed.go +++ /dev/null @@ -1,102 +0,0 @@ -package services - -import ( - "bytes" - "encoding/json" - "fmt" - "io" - "net/http" - "time" -) - -type TurnstileService struct { - secretKey string - client *http.Client -} - -type TurnstileResponse struct { - Success bool `json:"success"` - ErrorCodes []string `json:"error-codes,omitempty"` - ChallengeTS string `json:"challenge_ts,omitempty"` - Hostname string `json:"hostname,omitempty"` - Action string `json:"action,omitempty"` - Cdata string `json:"cdata,omitempty"` -} - -func NewTurnstileService(secretKey string) *TurnstileService { - return &TurnstileService{ - secretKey: secretKey, - client: &http.Client{ - Timeout: 10 * time.Second, - }, - } -} - -// VerifyToken validates a Turnstile token with Cloudflare -func (s *TurnstileService) VerifyToken(token, remoteIP string) (*TurnstileResponse, error) { - // Allow bypass token for development (Flutter web) - if token == "BYPASS_DEV_MODE" { - return &TurnstileResponse{Success: true}, nil - } - - if s.secretKey == "" { - // If no secret key is configured, skip verification (for development) - return &TurnstileResponse{Success: true}, nil - } - - // Prepare the request data - data := fmt.Sprintf( - "secret=%s&response=%s", - s.secretKey, - token, - ) - - if remoteIP != "" { - data += fmt.Sprintf("&remoteip=%s", remoteIP) - } - - // Make the request to Cloudflare - resp, err := s.client.Post( - "https://challenges.cloudflare.com/turnstile/v0/siteverify", - "application/x-www-form-urlencoded", - bytes.NewBufferString(data), - ) - if err != nil { - return nil, fmt.Errorf("failed to verify turnstile token: %w", err) - } - defer resp.Body.Close() - - // Read the response - body, err := io.ReadAll(resp.Body) - if err != nil { - return nil, fmt.Errorf("failed to read turnstile response: %w", err) - } - - // Parse the response - var result TurnstileResponse - if err := json.Unmarshal(body, &result); err != nil { - return nil, fmt.Errorf("failed to parse turnstile response: %w", err) - } - - return &result, nil -} - -// GetErrorMessage returns a user-friendly error message for error codes -func (s *TurnstileService) GetErrorMessage(errorCodes []string) string { - errorMessages := map[string]string{ - "missing-input-secret": "Server configuration error", - "invalid-input-secret": "Server configuration error", - "missing-input-response": "Please complete the security check", - "invalid-input-response": "Security check failed, please try again", - "bad-request": "Invalid request format", - "timeout-or-duplicate": "Security check expired, please try again", - "internal-error": "Verification service unavailable", - } - - for _, code := range errorCodes { - if msg, exists := errorMessages[code]; exists { - return msg - } - } - return "Security verification failed" -} diff --git a/verified_fixed.html b/verified_fixed.html deleted file mode 100644 index 214f315..0000000 --- a/verified_fixed.html +++ /dev/null @@ -1,197 +0,0 @@ - - - - - - - Email Verification - Sojorn - - - - - -
-
- - - - -
-
- - - -
-

Verification Required

-

We couldn't confirm your verification status. Please use the link sent to your email address.

- Return to App -
-
- - - - -