An atproto PDS written in Go
at v0.5.1 2.8 kB view raw
1package server 2 3import ( 4 "context" 5 "fmt" 6 "strings" 7 8 "github.com/bluesky-social/indigo/atproto/atcrypto" 9 "github.com/bluesky-social/indigo/atproto/identity" 10 atproto_identity "github.com/bluesky-social/indigo/atproto/identity" 11 "github.com/bluesky-social/indigo/atproto/syntax" 12 "github.com/golang-jwt/jwt/v4" 13) 14 15type ES256KSigningMethod struct { 16 alg string 17} 18 19func (m *ES256KSigningMethod) Alg() string { 20 return m.alg 21} 22 23func (m *ES256KSigningMethod) Verify(signingString string, signature string, key interface{}) error { 24 signatureBytes, err := jwt.DecodeSegment(signature) 25 if err != nil { 26 return err 27 } 28 return key.(atcrypto.PublicKey).HashAndVerifyLenient([]byte(signingString), signatureBytes) 29} 30 31func (m *ES256KSigningMethod) Sign(signingString string, key interface{}) (string, error) { 32 return "", fmt.Errorf("unimplemented") 33} 34 35func init() { 36 ES256K := ES256KSigningMethod{alg: "ES256K"} 37 jwt.RegisterSigningMethod(ES256K.Alg(), func() jwt.SigningMethod { 38 return &ES256K 39 }) 40} 41 42func (s *Server) validateServiceAuth(ctx context.Context, rawToken string, nsid string) (string, error) { 43 token := strings.TrimSpace(rawToken) 44 45 parsedToken, err := jwt.ParseWithClaims(token, jwt.MapClaims{}, func(token *jwt.Token) (interface{}, error) { 46 did := syntax.DID(token.Claims.(jwt.MapClaims)["iss"].(string)) 47 didDoc, err := s.passport.FetchDoc(ctx, did.String()); 48 if err != nil { 49 return nil, fmt.Errorf("unable to resolve did %s: %s", did, err) 50 } 51 52 verificationMethods := make([]atproto_identity.DocVerificationMethod, len(didDoc.VerificationMethods)) 53 for i, verificationMethod := range didDoc.VerificationMethods { 54 verificationMethods[i] = atproto_identity.DocVerificationMethod{ 55 ID: verificationMethod.Id, 56 Type: verificationMethod.Type, 57 PublicKeyMultibase: verificationMethod.PublicKeyMultibase, 58 Controller: verificationMethod.Controller, 59 } 60 } 61 services := make([]atproto_identity.DocService, len(didDoc.Service)) 62 for i, service := range didDoc.Service { 63 services[i] = atproto_identity.DocService{ 64 ID: service.Id, 65 Type: service.Type, 66 ServiceEndpoint: service.ServiceEndpoint, 67 } 68 } 69 parsedIdentity := atproto_identity.ParseIdentity(&identity.DIDDocument{ 70 DID: did, 71 AlsoKnownAs: didDoc.AlsoKnownAs, 72 VerificationMethod: verificationMethods, 73 Service: services, 74 }) 75 76 key, err := parsedIdentity.PublicKey() 77 if err != nil { 78 return nil, fmt.Errorf("signing key not found for did %s: %s", did, err) 79 } 80 return key, nil 81 }) 82 if err != nil { 83 return "", fmt.Errorf("invalid token: %s", err) 84 } 85 86 claims := parsedToken.Claims.(jwt.MapClaims) 87 if claims["lxm"] != nsid { 88 return "", fmt.Errorf("bad jwt lexicon method (\"lxm\"). must match: %s", nsid) 89 } 90 return claims["iss"].(string), nil 91}