fix(bskyweb): sanitize DID colons in Google Wallet object id
This commit is contained in:
@@ -7,6 +7,7 @@ import (
|
|||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
"errors"
|
"errors"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/golang-jwt/jwt/v5"
|
"github.com/golang-jwt/jwt/v5"
|
||||||
)
|
)
|
||||||
@@ -63,7 +64,7 @@ func BuildSaveJWT(cfg *WalletConfig, did, handle, theme string) (string, error)
|
|||||||
heroURL := cfg.HeroBaseURL + "?" + heroQuery.Encode()
|
heroURL := cfg.HeroBaseURL + "?" + heroQuery.Encode()
|
||||||
|
|
||||||
obj := map[string]any{
|
obj := map[string]any{
|
||||||
"id": cfg.IssuerID + ".bsky-" + did + "-" + theme,
|
"id": cfg.IssuerID + ".bsky-" + strings.ReplaceAll(did, ":", "_") + "-" + theme,
|
||||||
"classId": cfg.IssuerID + ".bsky_invite_v1",
|
"classId": cfg.IssuerID + ".bsky_invite_v1",
|
||||||
"logo": map[string]any{
|
"logo": map[string]any{
|
||||||
"sourceUri": map[string]any{"uri": cfg.LogoURL},
|
"sourceUri": map[string]any{"uri": cfg.LogoURL},
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ func TestBuildSaveJWT_RoundTrip(t *testing.T) {
|
|||||||
t.Fatalf("want 1 object, got %d", len(objs))
|
t.Fatalf("want 1 object, got %d", len(objs))
|
||||||
}
|
}
|
||||||
obj := objs[0].(map[string]any)
|
obj := objs[0].(map[string]any)
|
||||||
if !strings.HasSuffix(obj["id"].(string), ".bsky-did:plc:abc-dusk") {
|
if !strings.HasSuffix(obj["id"].(string), ".bsky-did_plc_abc-dusk") {
|
||||||
t.Errorf("unexpected id: %v", obj["id"])
|
t.Errorf("unexpected id: %v", obj["id"])
|
||||||
}
|
}
|
||||||
if obj["hexBackgroundColor"] != "#b15aa2" {
|
if obj["hexBackgroundColor"] != "#b15aa2" {
|
||||||
@@ -71,3 +71,45 @@ func TestBuildSaveJWT_ThemeCoerced(t *testing.T) {
|
|||||||
t.Errorf("expected coercion to day, got id %v", obj["id"])
|
t.Errorf("expected coercion to day, got id %v", obj["id"])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestBuildSaveJWT_IdSanitizesDidColons(t *testing.T) {
|
||||||
|
key, _ := rsa.GenerateKey(rand.Reader, 2048)
|
||||||
|
cfg := &WalletConfig{
|
||||||
|
IssuerEmail: "issuer@example.com",
|
||||||
|
IssuerID: "3388000000000000000",
|
||||||
|
PrivateKey: key,
|
||||||
|
HeroBaseURL: "https://bsky.app/invite/wallet/hero",
|
||||||
|
LogoURL: "https://web-cdn.bsky.app/passes/logo.png",
|
||||||
|
}
|
||||||
|
|
||||||
|
tok, err := BuildSaveJWT(cfg, "did:plc:abc123", "alice.bsky.social", "day")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("build: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
parsed, err := jwt.Parse(tok, func(t *jwt.Token) (any, error) {
|
||||||
|
return &key.PublicKey, nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("parse: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
claims := parsed.Claims.(jwt.MapClaims)
|
||||||
|
payload := claims["payload"].(map[string]any)
|
||||||
|
objs := payload["genericObjects"].([]any)
|
||||||
|
if len(objs) != 1 {
|
||||||
|
t.Fatalf("want 1 object, got %d", len(objs))
|
||||||
|
}
|
||||||
|
obj := objs[0].(map[string]any)
|
||||||
|
|
||||||
|
// Assert that the id ends with the sanitized DID (colons replaced with underscores)
|
||||||
|
if !strings.HasSuffix(obj["id"].(string), ".bsky-did_plc_abc123-day") {
|
||||||
|
t.Errorf("expected id to end with .bsky-did_plc_abc123-day, got %v", obj["id"])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assert that the barcode value still contains the original profile URL
|
||||||
|
barcode := obj["barcode"].(map[string]any)
|
||||||
|
if barcode["value"] != "https://bsky.app/profile/alice.bsky.social" {
|
||||||
|
t.Errorf("expected barcode.value to be unchanged, got %v", barcode["value"])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
# Placeholder static files
|
||||||
Reference in New Issue
Block a user