A community based topic aggregation platform built on atproto
1package auth 2 3import ( 4 "testing" 5 "time" 6 7 "github.com/golang-jwt/jwt/v5" 8) 9 10func TestParseJWT(t *testing.T) { 11 // Create a test JWT token 12 claims := &Claims{ 13 RegisteredClaims: jwt.RegisteredClaims{ 14 Subject: "did:plc:test123", 15 Issuer: "https://test-pds.example.com", 16 ExpiresAt: jwt.NewNumericDate(time.Now().Add(1 * time.Hour)), 17 IssuedAt: jwt.NewNumericDate(time.Now()), 18 }, 19 Scope: "atproto transition:generic", 20 } 21 22 token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) 23 tokenString, err := token.SignedString([]byte("test-secret")) 24 if err != nil { 25 t.Fatalf("Failed to create test token: %v", err) 26 } 27 28 // Test parsing 29 parsedClaims, err := ParseJWT(tokenString) 30 if err != nil { 31 t.Fatalf("ParseJWT failed: %v", err) 32 } 33 34 if parsedClaims.Subject != "did:plc:test123" { 35 t.Errorf("Expected subject 'did:plc:test123', got '%s'", parsedClaims.Subject) 36 } 37 38 if parsedClaims.Issuer != "https://test-pds.example.com" { 39 t.Errorf("Expected issuer 'https://test-pds.example.com', got '%s'", parsedClaims.Issuer) 40 } 41 42 if parsedClaims.Scope != "atproto transition:generic" { 43 t.Errorf("Expected scope 'atproto transition:generic', got '%s'", parsedClaims.Scope) 44 } 45} 46 47func TestParseJWT_MissingSubject(t *testing.T) { 48 // Create a token without subject 49 claims := &Claims{ 50 RegisteredClaims: jwt.RegisteredClaims{ 51 Issuer: "https://test-pds.example.com", 52 ExpiresAt: jwt.NewNumericDate(time.Now().Add(1 * time.Hour)), 53 }, 54 } 55 56 token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) 57 tokenString, err := token.SignedString([]byte("test-secret")) 58 if err != nil { 59 t.Fatalf("Failed to create test token: %v", err) 60 } 61 62 // Test parsing - should fail 63 _, err = ParseJWT(tokenString) 64 if err == nil { 65 t.Error("Expected error for missing subject, got nil") 66 } 67} 68 69func TestParseJWT_MissingIssuer(t *testing.T) { 70 // Create a token without issuer 71 claims := &Claims{ 72 RegisteredClaims: jwt.RegisteredClaims{ 73 Subject: "did:plc:test123", 74 ExpiresAt: jwt.NewNumericDate(time.Now().Add(1 * time.Hour)), 75 }, 76 } 77 78 token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) 79 tokenString, err := token.SignedString([]byte("test-secret")) 80 if err != nil { 81 t.Fatalf("Failed to create test token: %v", err) 82 } 83 84 // Test parsing - should fail 85 _, err = ParseJWT(tokenString) 86 if err == nil { 87 t.Error("Expected error for missing issuer, got nil") 88 } 89} 90 91func TestParseJWT_WithBearerPrefix(t *testing.T) { 92 // Create a test JWT token 93 claims := &Claims{ 94 RegisteredClaims: jwt.RegisteredClaims{ 95 Subject: "did:plc:test123", 96 Issuer: "https://test-pds.example.com", 97 ExpiresAt: jwt.NewNumericDate(time.Now().Add(1 * time.Hour)), 98 }, 99 } 100 101 token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) 102 tokenString, err := token.SignedString([]byte("test-secret")) 103 if err != nil { 104 t.Fatalf("Failed to create test token: %v", err) 105 } 106 107 // Test parsing with Bearer prefix 108 parsedClaims, err := ParseJWT("Bearer " + tokenString) 109 if err != nil { 110 t.Fatalf("ParseJWT failed with Bearer prefix: %v", err) 111 } 112 113 if parsedClaims.Subject != "did:plc:test123" { 114 t.Errorf("Expected subject 'did:plc:test123', got '%s'", parsedClaims.Subject) 115 } 116} 117 118func TestValidateClaims_Expired(t *testing.T) { 119 claims := &Claims{ 120 RegisteredClaims: jwt.RegisteredClaims{ 121 Subject: "did:plc:test123", 122 Issuer: "https://test-pds.example.com", 123 ExpiresAt: jwt.NewNumericDate(time.Now().Add(-1 * time.Hour)), // Expired 124 }, 125 } 126 127 err := validateClaims(claims) 128 if err == nil { 129 t.Error("Expected error for expired token, got nil") 130 } 131} 132 133func TestValidateClaims_InvalidDID(t *testing.T) { 134 claims := &Claims{ 135 RegisteredClaims: jwt.RegisteredClaims{ 136 Subject: "invalid-did-format", 137 Issuer: "https://test-pds.example.com", 138 ExpiresAt: jwt.NewNumericDate(time.Now().Add(1 * time.Hour)), 139 }, 140 } 141 142 err := validateClaims(claims) 143 if err == nil { 144 t.Error("Expected error for invalid DID format, got nil") 145 } 146} 147 148func TestExtractKeyID(t *testing.T) { 149 // Create a test JWT token with kid in header 150 token := jwt.New(jwt.SigningMethodRS256) 151 token.Header["kid"] = "test-key-id" 152 token.Claims = &Claims{ 153 RegisteredClaims: jwt.RegisteredClaims{ 154 Subject: "did:plc:test123", 155 Issuer: "https://test-pds.example.com", 156 }, 157 } 158 159 // Sign with a dummy RSA key (we just need a valid token structure) 160 tokenString, err := token.SignedString([]byte("dummy")) 161 if err == nil { 162 // If it succeeds (shouldn't with wrong key type, but let's handle it) 163 kid, err := ExtractKeyID(tokenString) 164 if err != nil { 165 t.Logf("ExtractKeyID failed (expected if signing fails): %v", err) 166 } else if kid != "test-key-id" { 167 t.Errorf("Expected kid 'test-key-id', got '%s'", kid) 168 } 169 } 170}