Skip to content

Add gated provider JWT and voice support; harden single-provider delivery - #9

Closed
Hou (SciencePotato) wants to merge 4 commits into
mainfrom
feature/soprano-voice-provider-token
Closed

Add gated provider JWT and voice support; harden single-provider delivery#9
Hou (SciencePotato) wants to merge 4 commits into
mainfrom
feature/soprano-voice-provider-token

Conversation

@SciencePotato

Copy link
Copy Markdown
Collaborator

Summary

Adds gated Soprano provider JWT and structured omnimsg voice support across JavaScript, Python and .NET, with single-provider startup improvements and fail-closed provider response handling. Includes the main changes from merged PR #8; this PR targets main and does not undo its parser/decrypted-payload regressions.

Authentication and voice

  • API-key authentication remains the default. EPP_PROVIDER_JWT_ENABLED=false performs no token lookup.
  • With the gate enabled, apiKey retains required API ID/key headers and attaches a provider JWT when available. oauth2 requires a JWT and never falls back to API keys.
  • Azure Identity acquires provider tokens using a Key Vault-backed client secret or managed-identity assertion federation. The incoming caller token is never forwarded.
  • Configuration checks are not JWT signature validation. The checks are grouped for readability; JavaScript shares its HTTPS checker, and regex assertions were replaced with literal checks.
  • Soprano voice sends explicit encrypted voice.text2voice fields; no OTP is extracted from the rendered message. SMS remains top-level text.

Single-provider deployments

  • Only the selected JavaScript/Python adapter is imported for live delivery. Unused files may be omitted without breaking startup or evaluation.
  • Missing/broken selected modules return a sanitized 502 before credential/provider I/O; unknown IDs return 400. Provider IDs resolve through a fixed allowlist, never a caller-supplied import path.
  • .NET retains simple typed registrations. If adapter sources are removed, remove their registrations and adapter-specific test references too. Keeping the bundled adapters does not require configuring their credentials.

Bug-bash fix

Previously a 2xx HTML/empty response could be acknowledged by non-Soprano adapters without evidence of submission. Malformed successful JSON now fails with 502 and no nonce. Status-based adapters require a recognized typed status; Sinch requires a nonblank message/call identifier. Non-2xx mappings, redirects, timeout behavior and no-retry behavior are preserved.

Validation

  • 116 local tests pass: JavaScript 27, Python 45, .NET 44.
  • .NET build succeeds with zero warnings/errors; whitespace, editor diagnostics, JSON examples and documentation links checked.
  • Isolated copies test startup with only selected provider source present, no providers present, and broken selected dependencies. Real JWE/handler tests retain auth gates, acceptance waiting, nonce, privacy, and malformed-response checks.
  • Fresh non-delivering evaluation passed on all three existing deployed apps: signed bearer authentication plus payload decryption returned 200 with matching nonce.
  • Original Easy Auth allowlists restored, temporary client credential removed with all six originals preserved, and final unauthenticated 401 / temporary-caller 403 verified on all three apps. No OTPs sent during this review.
  • Historical cloud verification of the earlier feature build passed 12 acceptance tests: API-key/JWT × SMS/voice × three languages. This is not deployment verification of the latest lazy-loading or provider-response changes.

Rollout caveats

  • Latest code has not been redeployed. Deploy and verify the exact reviewed package before rollout; local tests do not certify Easy Auth or provider compatibility by themselves.
  • Structured voice is an opt-in caller-contract extension: SAS must supply voice.text2voice inside the JWE. Message-only Soprano live voice fails closed; no password inference is performed.
  • Client-secret JWT acceptance has been tested against QA4. Managed-identity federation and server precedence with both API-key and Bearer headers remain unverified. Optional JWT is off by default.
  • Acceptance is not handset receipt or correct audio playback. No delivery confirmation/callback workflow is added.
  • Authentication and provider HTTP have separate timeout budgets; Python uses connect/read inactivity limits, not a hard end-to-end deadline. Cold calls have exceeded 3.2 seconds.
  • Caller trust remains in Easy Auth. Entra tokenEncryptionKeyId is distinct from payload JWE; no inbound token-validation fallback or authentication bypass is introduced.

Supersedes the implementation intent of closed, unmerged PR #4 without merging its unrelated/older history. Please squash-and-merge after review and passing CI; no automatic merge is requested.

Hou Chi Chan added 4 commits September 10, 2026 14:58
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.

1 participant