Monorepo for Tangled
tangled.org
1package serviceauth
2
3import (
4 "context"
5 "encoding/json"
6 "log/slog"
7 "net/http"
8 "strings"
9
10 "github.com/bluesky-social/indigo/atproto/auth"
11 "github.com/bluesky-social/indigo/atproto/syntax"
12 "tangled.org/core/idresolver"
13 "tangled.org/core/log"
14 xrpcerr "tangled.org/core/xrpc/errors"
15)
16
17const ActorDid string = "ActorDid"
18
19func DidWeb(hostname string) syntax.DID {
20 return syntax.DID("did:web:" + strings.ReplaceAll(hostname, ":", "%3A"))
21}
22
23type ServiceAuth struct {
24 logger *slog.Logger
25 resolver *idresolver.Resolver
26 audienceDid string
27}
28
29func NewServiceAuth(logger *slog.Logger, resolver *idresolver.Resolver, audienceDid string) *ServiceAuth {
30 return &ServiceAuth{
31 logger: log.SubLogger(logger, "serviceauth"),
32 resolver: resolver,
33 audienceDid: audienceDid,
34 }
35}
36
37func (sa *ServiceAuth) VerifyServiceAuth(next http.Handler) http.Handler {
38 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
39 token := r.Header.Get("Authorization")
40 token = strings.TrimPrefix(token, "Bearer ")
41
42 s := auth.ServiceAuthValidator{
43 Audience: sa.audienceDid,
44 Dir: sa.resolver.Directory(),
45 }
46
47 did, err := s.Validate(r.Context(), token, nil)
48 if err != nil {
49 sa.logger.Error("signature verification failed", "err", err)
50 writeError(w, xrpcerr.AuthError(err), http.StatusForbidden)
51 return
52 }
53
54 sa.logger.Debug("valid signature", ActorDid, did)
55
56 r = r.WithContext(
57 context.WithValue(r.Context(), ActorDid, did),
58 )
59
60 next.ServeHTTP(w, r)
61 })
62}
63
64// this is slightly different from http_util::write_error to follow the spec:
65//
66// the json object returned must include an "error" and a "message"
67func writeError(w http.ResponseWriter, e xrpcerr.XrpcError, status int) {
68 w.Header().Set("Content-Type", "application/json")
69 w.WriteHeader(status)
70 json.NewEncoder(w).Encode(e)
71}