+5
.env.dev
+5
.env.dev
···
+28
.env.prod.example
+28
.env.prod.example
···
···
+24
-3
cmd/server/main.go
+24
-3
cmd/server/main.go
······
······
+6
docker-compose.prod.yml
+6
docker-compose.prod.yml
···
+52
internal/atproto/auth/combined_key_fetcher.go
+52
internal/atproto/auth/combined_key_fetcher.go
···
···+func NewCombinedKeyFetcher(directory indigoIdentity.Directory, jwksFetcher JWKSFetcher) *CombinedKeyFetcher {+func (f *CombinedKeyFetcher) FetchPublicKey(ctx context.Context, issuer, token string) (interface{}, error) {
+116
internal/atproto/auth/did_key_fetcher.go
+116
internal/atproto/auth/did_key_fetcher.go
···
···+func (f *DIDKeyFetcher) FetchPublicKey(ctx context.Context, issuer, token string) (interface{}, error) {
+209
-29
internal/atproto/auth/jwt.go
+209
-29
internal/atproto/auth/jwt.go
······func VerifyJWT(ctx context.Context, tokenString string, keyFetcher JWKSFetcher) (*Claims, error) {token, err := jwt.ParseWithClaims(tokenString, &Claims{}, func(token *jwt.Token) (interface{}, error) {·········
······func VerifyJWT(ctx context.Context, tokenString string, keyFetcher JWKSFetcher) (*Claims, error) {+return nil, fmt.Errorf("expected HS256 for issuer %s but token uses %s", claims.Issuer, header.Alg)+return nil, fmt.Errorf("HS256 not allowed for issuer %s (not in HS256_ISSUERS whitelist)", claims.Issuer)+token, err := jwt.ParseWithClaims(tokenString, &Claims{}, func(token *jwt.Token) (interface{}, error) {+func verifyAsymmetricToken(ctx context.Context, tokenString, issuer string, keyFetcher JWKSFetcher) (*Claims, error) {token, err := jwt.ParseWithClaims(tokenString, &Claims{}, func(token *jwt.Token) (interface{}, error) {·········
+367
internal/atproto/auth/jwt_test.go
+367
internal/atproto/auth/jwt_test.go
······+func (m *mockJWKSFetcher) FetchPublicKey(ctx context.Context, issuer, token string) (interface{}, error) {+func createHS256Token(t *testing.T, subject, issuer, secret string, expiry time.Duration) string {+tokenString := createHS256Token(t, "did:plc:attacker", "https://victim-pds.example.com", "some-secret", 1*time.Hour)+// SECURITY TEST: When no issuers are whitelisted for HS256, all HS256 tokens should be rejected+tokenString := createHS256Token(t, "did:plc:test123", "https://any-pds.example.com", "some-secret", 1*time.Hour)+// Create RS256-signed token (can't actually sign without RSA key, but we can test the header check)+tokenString := createHS256Token(t, "did:plc:test123", "https://test.example.com", "secret", 1*time.Hour)+tokenString := createHS256Token(t, "did:plc:test123", "https://test.example.com", "secret", 1*time.Hour)