A community based topic aggregation platform built on atproto
1package integration 2 3import ( 4 "Coves/internal/api/middleware" 5 "fmt" 6 "net/http" 7 "net/http/httptest" 8 "os" 9 "testing" 10 "time" 11) 12 13// TestOAuthTokenVerification tests end-to-end OAuth token verification 14// with real PDS-issued OAuth tokens. This replaces the old JWT verification test 15// since we now use OAuth sealed session tokens instead of raw JWTs. 16// 17// Flow: 18// 1. Create account on local PDS (or use existing) 19// 2. Authenticate to get OAuth tokens and create sealed session token 20// 3. Verify our auth middleware can unseal and validate the token 21// 4. Test token validation and session retrieval 22// 23// NOTE: This test uses the E2E OAuth middleware which mocks the session unsealing 24// for testing purposes. Real OAuth tokens from PDS would be sealed using the 25// OAuth client's seal secret. 26func TestOAuthTokenVerification(t *testing.T) { 27 // Skip in short mode since this requires real PDS 28 if testing.Short() { 29 t.Skip("Skipping OAuth token verification test in short mode") 30 } 31 32 pdsURL := os.Getenv("PDS_URL") 33 if pdsURL == "" { 34 pdsURL = "http://localhost:3001" 35 } 36 37 // Check if PDS is running 38 healthResp, err := http.Get(pdsURL + "/xrpc/_health") 39 if err != nil { 40 t.Skipf("PDS not running at %s: %v", pdsURL, err) 41 } 42 _ = healthResp.Body.Close() 43 44 t.Run("OAuth token validation and middleware integration", func(t *testing.T) { 45 // Step 1: Create a test account on PDS 46 // Keep handle short to avoid PDS validation errors 47 timestamp := time.Now().Unix() % 100000 // Last 5 digits 48 handle := fmt.Sprintf("oauth%d.local.coves.dev", timestamp) 49 password := "testpass123" 50 email := fmt.Sprintf("oauth%d@test.com", timestamp) 51 52 _, did, err := createPDSAccount(pdsURL, handle, email, password) 53 if err != nil { 54 t.Fatalf("Failed to create PDS account: %v", err) 55 } 56 t.Logf("✓ Created test account: %s (DID: %s)", handle, did) 57 58 // Step 2: Create OAuth middleware with mock unsealer for testing 59 // In production, this would unseal real OAuth tokens from PDS 60 t.Log("Testing OAuth middleware with sealed session tokens...") 61 62 e2eAuth := NewE2EOAuthMiddleware() 63 testToken := e2eAuth.AddUser(did) 64 65 handlerCalled := false 66 var extractedDID string 67 68 testHandler := e2eAuth.RequireAuth(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 69 handlerCalled = true 70 extractedDID = middleware.GetUserDID(r) 71 w.WriteHeader(http.StatusOK) 72 _, _ = w.Write([]byte(`{"success": true}`)) 73 })) 74 75 req := httptest.NewRequest("GET", "/test", nil) 76 req.Header.Set("Authorization", "Bearer "+testToken) 77 w := httptest.NewRecorder() 78 79 testHandler.ServeHTTP(w, req) 80 81 if !handlerCalled { 82 t.Errorf("Handler was not called - auth middleware rejected valid token") 83 t.Logf("Response status: %d", w.Code) 84 t.Logf("Response body: %s", w.Body.String()) 85 } 86 87 if w.Code != http.StatusOK { 88 t.Errorf("Expected status 200, got %d", w.Code) 89 t.Logf("Response body: %s", w.Body.String()) 90 } 91 92 if extractedDID != did { 93 t.Errorf("Middleware extracted wrong DID: expected %s, got %s", did, extractedDID) 94 } 95 96 t.Logf("✅ OAuth middleware with token validation working correctly!") 97 t.Logf(" Handler called: %v", handlerCalled) 98 t.Logf(" Extracted DID: %s", extractedDID) 99 t.Logf(" Response status: %d", w.Code) 100 }) 101 102 t.Run("Rejects tampered/invalid sealed tokens", func(t *testing.T) { 103 // Create valid user 104 timestamp := time.Now().Unix() % 100000 105 handle := fmt.Sprintf("tamp%d.local.coves.dev", timestamp) 106 password := "testpass456" 107 email := fmt.Sprintf("tamp%d@test.com", timestamp) 108 109 _, did, err := createPDSAccount(pdsURL, handle, email, password) 110 if err != nil { 111 t.Fatalf("Failed to create PDS account: %v", err) 112 } 113 114 // Create OAuth middleware 115 e2eAuth := NewE2EOAuthMiddleware() 116 validToken := e2eAuth.AddUser(did) 117 118 // Create various invalid tokens to test 119 testCases := []struct { 120 name string 121 token string 122 }{ 123 {"Empty token", ""}, 124 {"Invalid base64", "not-valid-base64!!!"}, 125 {"Tampered token", "dGFtcGVyZWQtdG9rZW4tZGF0YQ=="}, // Valid base64 but not a real sealed session 126 {"Short token", "abc"}, 127 {"Modified valid token", validToken + "extra"}, 128 } 129 130 for _, tc := range testCases { 131 t.Run(tc.name, func(t *testing.T) { 132 handlerCalled := false 133 testHandler := e2eAuth.RequireAuth(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 134 handlerCalled = true 135 w.WriteHeader(http.StatusOK) 136 })) 137 138 req := httptest.NewRequest("GET", "/test", nil) 139 if tc.token != "" { 140 req.Header.Set("Authorization", "Bearer "+tc.token) 141 } 142 w := httptest.NewRecorder() 143 144 testHandler.ServeHTTP(w, req) 145 146 if handlerCalled { 147 t.Error("Handler was called for invalid token - should have been rejected") 148 } 149 150 if w.Code != http.StatusUnauthorized { 151 t.Errorf("Expected status 401 for invalid token, got %d", w.Code) 152 } 153 154 t.Logf("✓ Middleware correctly rejected %s with status %d", tc.name, w.Code) 155 }) 156 } 157 158 t.Logf("✅ All invalid token types correctly rejected") 159 }) 160 161 t.Run("Session expiration handling", func(t *testing.T) { 162 // OAuth session expiration is handled at the database level 163 // See TestOAuthE2E_TokenExpiration in oauth_e2e_test.go for full expiration testing 164 t.Log("ℹ️ Session expiration testing is covered in oauth_e2e_test.go") 165 t.Log(" OAuth sessions expire based on database timestamps and are cleaned up periodically") 166 t.Log(" This is different from JWT expiration which was timestamp-based in the token itself") 167 t.Skip("Session expiration is tested in oauth_e2e_test.go - see TestOAuthE2E_TokenExpiration") 168 }) 169}