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}