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}