A community based topic aggregation platform built on atproto
1package auth 2 3import ( 4 "context" 5 "fmt" 6 "strings" 7 8 indigoIdentity "github.com/bluesky-social/indigo/atproto/identity" 9) 10 11// CombinedKeyFetcher handles JWT public key fetching for both: 12// - DID issuers (did:plc:, did:web:) → resolves via DID document 13// - URL issuers (https://) → fetches via JWKS endpoint (legacy/fallback) 14// 15// For atproto service authentication, the issuer is typically the user's DID, 16// and the signing key is published in their DID document. 17type CombinedKeyFetcher struct { 18 didFetcher *DIDKeyFetcher 19 jwksFetcher JWKSFetcher 20} 21 22// NewCombinedKeyFetcher creates a key fetcher that supports both DID and URL issuers. 23// Parameters: 24// - directory: Indigo's identity directory for DID resolution 25// - jwksFetcher: fallback JWKS fetcher for URL issuers (can be nil if not needed) 26func NewCombinedKeyFetcher(directory indigoIdentity.Directory, jwksFetcher JWKSFetcher) *CombinedKeyFetcher { 27 return &CombinedKeyFetcher{ 28 didFetcher: NewDIDKeyFetcher(directory), 29 jwksFetcher: jwksFetcher, 30 } 31} 32 33// FetchPublicKey fetches the public key for verifying a JWT. 34// Routes to the appropriate fetcher based on issuer format: 35// - DID (did:plc:, did:web:) → DIDKeyFetcher 36// - URL (https://) → JWKSFetcher 37func (f *CombinedKeyFetcher) FetchPublicKey(ctx context.Context, issuer, token string) (interface{}, error) { 38 // Check if issuer is a DID 39 if strings.HasPrefix(issuer, "did:") { 40 return f.didFetcher.FetchPublicKey(ctx, issuer, token) 41 } 42 43 // Check if issuer is a URL (https:// or http:// in dev) 44 if strings.HasPrefix(issuer, "https://") || strings.HasPrefix(issuer, "http://") { 45 if f.jwksFetcher == nil { 46 return nil, fmt.Errorf("URL issuer %s requires JWKS fetcher, but none configured", issuer) 47 } 48 return f.jwksFetcher.FetchPublicKey(ctx, issuer, token) 49 } 50 51 return nil, fmt.Errorf("unsupported issuer format: %s (expected DID or URL)", issuer) 52}