package testpds import ( "encoding/base64" "encoding/json" "fmt" "time" ) // signServiceAuthJWT mints a JWT in the shape Hold expects, signed with the // identity's K-256 signing key. The header advertises ES256K (the algorithm // ATProto PDSes use). The Hold's ValidateServiceToken in pkg/hold/pds/auth.go // parses the JWT manually rather than via golang-jwt, so we don't need to // match golang-jwt's exact byte ordering — just produce well-formed // base64url(JSON).header.base64url(JSON).payload.base64url(sig). func (i *Identity) signServiceAuthJWT(aud, lxm string, exp time.Time) (string, error) { header := map[string]string{"alg": "ES256K", "typ": "JWT"} headerJSON, err := json.Marshal(header) if err != nil { return "", fmt.Errorf("marshal header: %w", err) } payload := map[string]any{ "iss": i.DID.String(), "aud": aud, "exp": exp.Unix(), "lxm": lxm, "iat": time.Now().Unix(), } payloadJSON, err := json.Marshal(payload) if err != nil { return "", fmt.Errorf("marshal payload: %w", err) } signingInput := base64.RawURLEncoding.EncodeToString(headerJSON) + "." + base64.RawURLEncoding.EncodeToString(payloadJSON) sig, err := i.SigningKey.HashAndSign([]byte(signingInput)) if err != nil { return "", fmt.Errorf("sign: %w", err) } return signingInput + "." + base64.RawURLEncoding.EncodeToString(sig), nil }