Skip to content

fix(tts): use the v2 Sec-MS-GEC token for Microsoft Edge TTS - #285

Merged
grinev merged 1 commit into
grinev:mainfrom
Realmagnum:fix/edge-tts-sec-ms-gec-v2
Oct 27, 2026
Merged

grinev merged 1 commit into
grinev:mainfrom
Realmagnum:fix/edge-tts-sec-ms-gec-v2

Conversation

@Realmagnum

Copy link
Copy Markdown
Contributor

fix(tts): use the v2 Sec-MS-GEC token for Microsoft Edge TTS

Fixes the edge TTS provider, which currently fails with HTTP 403 on every request.

Why

src/app/services/edge-tts.ts generates the v1 Sec-MS-GEC token
(SHA256(ticks + TRUSTED_CLIENT_TOKEN), Sec-MS-GEC-Version=1-…). Microsoft's Read Aloud
service now rejects that with HTTP 403 and expects the v2 token: Sec-MS-GEC-Version=2-<Chromium>
and a Sec-MS-GEC value shaped <16 hex>-<64 hex> — a CS64 keyed digest joined by a dash to a
modified SHA-256 digest. Bumping the version string alone does not help; the token algorithm has
to change too.

What

  • New src/app/services/edge-tts-drm.ts — the v2 token generator, ported from
    rany2/edge-tts src/edge_tts/drm.py
    (generate_sec_ms_gec_v2): modified SHA-256 (custom IV derived from the 7-byte salt, one altered
    round constant, two adjusted schedule words) plus the CS64 keyed hash over the same
    <ticks><TRUSTED_CLIENT_TOKEN> input, Windows file-time ticks rounded down to 5 minutes.
    No new dependencies; 32-bit lane arithmetic uses Math.imul / >>> 0 to mirror the Python
    & 0xFFFFFFFF semantics. Exports generateSecMsGecV2(unixSeconds: number): string.
  • src/app/services/edge-tts.ts — SEC_MS_GEC_VERSION is now `2-${CHROMIUM_FULL_VERSION}`
    and generateSecMsGec(now) delegates to the v2 generator (exported name and signature
    unchanged; _resetClockSkew() still applies). Removed the now-unused v1 constants and the
    createHash import.
  • Tests:
    • tests/app/services/edge-tts-drm.test.ts — new; pins nine vectors produced by the Python
      reference (edge_tts.drm.generate_sec_ms_gec_v2) including 5-minute-window rounding and
      sub-second timestamps.
    • tests/app/services/edge-tts.test.ts — updated token shape/vector/version expectations.

Verification

  • npm test — 2818 passed, 7 skipped (203 files).
  • npm run typecheck and eslint on the changed files — clean.
  • Live synthesis against the real service with the patched code:
    const buf = await synthesizeWithEdgeTts("Проверка после исправления. Один, два, три.", {
      voice: "ru-RU-DmitryNeural",
    }); // -> 45216-byte MP3, 7.5 s of Russian speech, ~1.1 s wall time
    Before the change the same call failed with HTTP 403.

Notes

  • Behaviour change for anyone consuming SEC_MS_GEC_VERSION: it now reports 2-<version>,
    matching what is sent on the wire.
  • generateSecMsGec still returns an uppercase hex string; its shape is now
    <16 hex>-<64 hex>.
  • No config, env var or public API besides that constant changed.

Closes #284

Microsoft's Read Aloud service now rejects the v1 token with HTTP 403, so the
edge provider was dead for every voice. Generate the v2 token
(`<16 hex>-<64 hex>`: a CS64 keyed digest joined to a modified SHA-256) and
send `Sec-MS-GEC-Version=2-<Chromium version>`, ported from edge-tts 7.3.1.
@grinev

grinev commented Oct 27, 2026

Copy link
Copy Markdown
Owner

@Realmagnum thanks for contribution!

@grinev
grinev merged commit 77ce4d1 into grinev:main Oct 27, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: TTS_PROVIDER=edge always fails with HTTP 403 (Microsoft moved Sec-MS-GEC DRM to v2)

2 participants