A demo of a Bluesky feed generator in Go
at main 88 lines 2.2 kB view raw
1package feed 2 3import ( 4 "fmt" 5 "net/http" 6 "strings" 7 8 "github.com/bluesky-social/indigo/atproto/crypto" 9 "github.com/bluesky-social/indigo/atproto/identity" 10 "github.com/bluesky-social/indigo/atproto/syntax" 11 "github.com/golang-jwt/jwt/v5" 12) 13 14const ( 15 ES256K = "ES256K" 16 ES256 = "ES256" 17) 18 19func init() { 20 ES256K := AtProtoSigningMethod{alg: "ES256K"} 21 jwt.RegisterSigningMethod(ES256K.Alg(), func() jwt.SigningMethod { 22 return &ES256K 23 }) 24 25 ES256 := AtProtoSigningMethod{alg: "ES256"} 26 jwt.RegisterSigningMethod(ES256.Alg(), func() jwt.SigningMethod { 27 return &ES256 28 }) 29 30} 31 32type AtProtoSigningMethod struct { 33 alg string 34} 35 36func (m *AtProtoSigningMethod) Alg() string { 37 return m.alg 38} 39 40func (m *AtProtoSigningMethod) Verify(signingString string, signature []byte, key interface{}) error { 41 err := key.(crypto.PublicKey).HashAndVerifyLenient([]byte(signingString), signature) 42 return err 43} 44 45func (m *AtProtoSigningMethod) Sign(signingString string, key interface{}) ([]byte, error) { 46 return nil, fmt.Errorf("unimplemented") 47} 48 49func getRequestUserDID(r *http.Request) (string, error) { 50 headerValues := r.Header["Authorization"] 51 52 if len(headerValues) != 1 { 53 return "", fmt.Errorf("missing authorization header") 54 } 55 token := strings.TrimSpace(strings.Replace(headerValues[0], "Bearer ", "", 1)) 56 57 keyfunc := func(token *jwt.Token) (any, error) { 58 did := syntax.DID(token.Claims.(jwt.MapClaims)["iss"].(string)) 59 identity, err := identity.DefaultDirectory().LookupDID(r.Context(), did) 60 if err != nil { 61 return nil, fmt.Errorf("unable to resolve did %s: %s", did, err) 62 } 63 key, err := identity.PublicKey() 64 if err != nil { 65 return nil, fmt.Errorf("signing key not found for did %s: %s", did, err) 66 } 67 return key, nil 68 } 69 70 validMethods := jwt.WithValidMethods([]string{ES256, ES256K}) 71 72 parsedToken, err := jwt.ParseWithClaims(token, jwt.MapClaims{}, keyfunc, validMethods) 73 if err != nil { 74 return "", fmt.Errorf("invalid token: %s", err) 75 } 76 77 claims, ok := parsedToken.Claims.(jwt.MapClaims) 78 if !ok { 79 return "", fmt.Errorf("token contained no claims") 80 } 81 82 issVal, ok := claims["iss"].(string) 83 if !ok { 84 return "", fmt.Errorf("iss claim missing") 85 } 86 87 return string(syntax.DID(issVal)), nil 88}