Something went wrong. Try again.
Monorepo for Tangled
Something went wrong. Try again.
3.5 kB · 114 lines
Go
at master
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115package serviceauth
import ( "io" "log/slog" "net/http" "net/http/httptest" "testing" "time"
"github.com/bluesky-social/indigo/atproto/atcrypto" "github.com/bluesky-social/indigo/atproto/auth" "github.com/bluesky-social/indigo/atproto/identity" "github.com/bluesky-social/indigo/atproto/syntax")
const ( testIssuer = "did:plc:boltless" testAudience = "did:web:knot.example" testLxm = "sh.tangled.repo.create")
func newTestServiceAuth(t *testing.T) (*ServiceAuth, atcrypto.PrivateKey) { t.Helper() priv, err := atcrypto.GeneratePrivateKeyP256() if err != nil { t.Fatalf("generate key: %v", err) } pub, err := priv.PublicKey() if err != nil { t.Fatalf("derive pubkey: %v", err) } dir := identity.NewMockDirectory() dir.Insert(identity.Identity{ DID: syntax.DID(testIssuer), Keys: map[string]identity.VerificationMethod{ "atproto": {Type: "Multikey", PublicKeyMultibase: pub.Multibase()}, }, }) logger := slog.New(slog.NewTextHandler(io.Discard, nil)) return NewServiceAuth(logger, dir, testAudience), priv}
func signed(t *testing.T, priv atcrypto.PrivateKey, lxm *syntax.NSID) string { t.Helper() token, err := auth.SignServiceAuth(syntax.DID(testIssuer), testAudience, time.Minute, lxm, priv) if err != nil { t.Fatalf("sign service auth: %v", err) } return token}
func serve(sa *ServiceAuth, path, token string) (*httptest.ResponseRecorder, *syntax.DID) { var seen *syntax.DID next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if did, ok := r.Context().Value(ActorDid).(syntax.DID); ok { seen = &did } w.WriteHeader(http.StatusOK) }) req := httptest.NewRequest(http.MethodPost, path, nil) if token != "" { req.Header.Set("Authorization", "Bearer "+token) } rec := httptest.NewRecorder() sa.VerifyServiceAuth(next).ServeHTTP(rec, req) return rec, seen}
func TestVerifyServiceAuth_MatchingLxmPasses(t *testing.T) { sa, priv := newTestServiceAuth(t) lxm := syntax.NSID(testLxm) rec, seen := serve(sa, "/xrpc/"+testLxm, signed(t, priv, &lxm)) if rec.Code != http.StatusOK { t.Fatalf("status = %d, want 200; body=%s", rec.Code, rec.Body.String()) } if seen == nil || seen.String() != testIssuer { t.Fatalf("ActorDid = %v, want %s", seen, testIssuer) }}
func TestVerifyServiceAuth_MismatchedLxmRejected(t *testing.T) { sa, priv := newTestServiceAuth(t) other := syntax.NSID("sh.tangled.knot.addMember") rec, _ := serve(sa, "/xrpc/"+testLxm, signed(t, priv, &other)) if rec.Code != http.StatusForbidden { t.Fatalf("status = %d, want 403 for lxm bound to a different method", rec.Code) }}
func TestVerifyServiceAuth_NoLxmClaimRejected(t *testing.T) { sa, priv := newTestServiceAuth(t) rec, _ := serve(sa, "/xrpc/"+testLxm, signed(t, priv, nil)) if rec.Code != http.StatusForbidden { t.Fatalf("status = %d, want 403 for a token carrying no lxm claim", rec.Code) }}
func TestVerifyServiceAuth_UnparseablePathRejected(t *testing.T) { sa, priv := newTestServiceAuth(t) lxm := syntax.NSID(testLxm) rec, _ := serve(sa, "/xrpc/notansid", signed(t, priv, &lxm)) if rec.Code != http.StatusForbidden { t.Fatalf("status = %d, want 403 when the path tail is not a valid NSID", rec.Code) }}
func TestVerifyServiceAuth_GarbageTokenRejected(t *testing.T) { sa, _ := newTestServiceAuth(t) rec, _ := serve(sa, "/xrpc/"+testLxm, "not.a.jwt") if rec.Code != http.StatusForbidden { t.Fatalf("status = %d, want 403 for an unverifiable token", rec.Code) }}