this repo has no description
1package main
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
14// The contents of this file have been borrowed from here: https://github.com/orthanc/bluesky-go-feeds/blob/f719f113f1afc9080e50b4b1f5ca239aa3073c79/web/auth.go#L20-L46
15// It essentially allows the signing method that atproto uses for JWT to be used when verifying the JWT that they send in requests
16
17const (
18 ES256K = "ES256K"
19 ES256 = "ES256"
20)
21
22type AtProtoSigningMethod struct {
23 alg string
24}
25
26func (m *AtProtoSigningMethod) Alg() string {
27 return m.alg
28}
29
30func (m *AtProtoSigningMethod) Verify(signingString string, signature []byte, key interface{}) error {
31 err := key.(crypto.PublicKey).HashAndVerifyLenient([]byte(signingString), signature)
32 return err
33}
34
35func (m *AtProtoSigningMethod) Sign(signingString string, key interface{}) ([]byte, error) {
36 return nil, fmt.Errorf("unimplemented")
37}
38
39func init() {
40 ES256K := AtProtoSigningMethod{alg: "ES256K"}
41 jwt.RegisterSigningMethod(ES256K.Alg(), func() jwt.SigningMethod {
42 return &ES256K
43 })
44
45 ES256 := AtProtoSigningMethod{alg: "ES256"}
46 jwt.RegisterSigningMethod(ES256.Alg(), func() jwt.SigningMethod {
47 return &ES256
48 })
49
50}
51
52var directory = identity.DefaultDirectory()
53
54func getRequestUserDID(r *http.Request) (string, error) {
55 headerValues := r.Header["Authorization"]
56
57 if len(headerValues) != 1 {
58 return "", fmt.Errorf("missing authorization header")
59 }
60 token := strings.TrimSpace(strings.Replace(headerValues[0], "Bearer ", "", 1))
61
62 keyfunc := func(token *jwt.Token) (interface{}, error) {
63 did := syntax.DID(token.Claims.(jwt.MapClaims)["iss"].(string))
64 identity, err := directory.LookupDID(r.Context(), did)
65 if err != nil {
66 return nil, fmt.Errorf("unable to resolve did %s: %s", did, err)
67 }
68 key, err := identity.PublicKey()
69 if err != nil {
70 return nil, fmt.Errorf("signing key not found for did %s: %s", did, err)
71 }
72 return key, nil
73 }
74
75 validMethods := jwt.WithValidMethods([]string{ES256, ES256K})
76
77 parsedToken, err := jwt.ParseWithClaims(token, jwt.MapClaims{}, keyfunc, validMethods)
78 if err != nil {
79 return "", fmt.Errorf("invalid token: %s", err)
80 }
81
82 claims, ok := parsedToken.Claims.(jwt.MapClaims)
83 if !ok {
84 return "", fmt.Errorf("token contained no claims")
85 }
86
87 issVal, ok := claims["iss"].(string)
88 if !ok {
89 return "", fmt.Errorf("iss claim missing")
90 }
91
92 return string(syntax.DID(issVal)), nil
93}