Status · 2026-08-24 · FULL end-to-end PROVEN in sandbox · Mint caller BUILT + eligibility-gated (Salesforce core) · Sandbox portal-user cache deployed · Enhancement §§1–2 shipped to production 2026-08-11/13 · All Salesforce-side decisions closed
One tap on a refill-reminder text and the patient
lands on their My Home page already logged in — no date of
birth to enter, no verification code to fetch. Email works differently since [D16]
(2026-08-06): emailed links land on a date-of-birth step-up
before the login runs, so the tap alone no longer completes it.
Salesforce mints the link at dispatch time; Azure runs the bridge;
SFMC renders it as a merge field. As of 2026-08-20 the whole
journey is proven in the sandbox — Salesforce minted the
link, a test patient clicked it, and they landed logged in on
/my-home with the refill wizard available. Nothing was
hand-seeded, and a replay of the same link after the old database
grant was withdrawn worked too. What remains is production: the
bridge app, the permission grant for every existing patient, the
config cutover, and the reminder-flow wiring.
Azure platform built, deployed, security-hardened (2026-06-11), and re-verified live (2026-06-22). Auth proven end-to-end in the sandbox. Direction change 2026-07-08: the mint caller moves from SFMC to Salesforce core — the send path is no longer gated on the SFMC credential rotation; the new open work is the Salesforce-side mint caller (design + build).
singleaccess bridge. SF
LoginHistory 4/4 success from NAT IP
135.119.140.149, zero failures (2026-05-26).
One live exposure + eight landmines. Adversarially verified; build + 11 tests green. State tags: live deployed to prod · repo / decided shipped in code or a recorded decision.
allow=false
2026-06-22.
usp_CreateMagicLink idempotency revive
live
(contactId, idempotencyKey) after
expiry/auto-revoke no longer hits a
UNIQUE(ShortCodeHash) 500 — the proc now matches in
any state and revives the row; 2627/2601 → THROW 50409.
HashPepperVersion; mint regenerates the
short URL with the row's stored pepper version on replay. Rotation
no longer breaks live links. Unit tests added.
/healthz WAF rate-limit + pepper cache
live
ScannerSuspected for observability;
do not gate the bridge — correct for 72h
multi-use links (a false positive would block a real patient). No
behavioral change.
MagicLink:Settings:Sentinel;
reverting the endpoint values alone won't propagate. Runbook now
says bump the sentinel (or restart) at cutover.
SyncControl bookkeeping
repo
Sync_SFDC_PortalUser_Full now records
SetSyncInProgress / UpdateSyncSuccess + an idempotent bootstrap
row (activates with Phase 2 portal-user sync).
SfmcTokenValidator OIDC caching
repo
ConfigurationManager is cached per metadata
address (it was rebuilt per request) — the validator is a DI
singleton.
Committed 2026-07-08 (plan.md revision 11; authoritative decision record in the doc-integration enhancement plan §6): the mint request originates from Salesforce core, not the Marketing Cloud journey. Azure is unchanged — it still generates, stores, redeems, and bridges the link; only the prompter changes. Nothing gets unwound: the SFMC mint wiring was never built and its Entra secret was never handed to Marketing Cloud.
HTTPPost2 to mint at render time
shortUrl; SFMC renders a merge field
maxBatchSize 2, so mint runs synchronously there.
That batch size must not be raised.
Magic_Link__c object, not a field on
Refill_Reminder__c. URL stored as Text rather than URL
(a clickable field would authenticate the clicker as the patient,
with no symptom), blanked about an hour after mint; read-only
"Magic Link: View All" permission set gates who can see it.
MagicLink-Salesforce-Client, allowlist its appId
(config-only on Azure), build the External + Named Credential;
retire the never-distributed SFMC client.
When a refill-reminder text or email goes out, the message carries a short URL. The patient taps it once and lands on their My Home page in the Salesforce portal already logged in — no date of birth to enter, no verification code to fetch. What it replaces: the normal portal login is email address + date of birth, then a one-time code sent to that address. There is no password at any point, so nothing here is "skipping a password" — a phrasing that has appeared in several documents and is wrong. On the email channel, [D16] (2026-08-06) adds a date-of-birth step-up before the login runs, so there the tap alone no longer completes it. Three systems each do one thing: Salesforce decides who gets reminded and asks Azure for the link at dispatch time (the 2026-07-08 direction change); Azure generates the link, holds the signing key, and runs the OAuth handshake at click time; SFMC renders the pre-minted URL as a merge field and delivers the message. Salesforce then establishes the session and serves the wizard.
The bulk of the build lives in the existing AdvancedRx Azure VNet — the same one that already hosts eFax, the e-script integration, and the patient-matcher service. The Salesforce footprint stays small; §4 lists exactly what's required there.
Salesforce asks for the link and fires the journey event; SFMC renders and delivers the message; Azure mints, holds the signing key, and runs the OAuth handshake at click time; Salesforce establishes the session and serves the wizard.
singleaccessRefill_Reminder__c scheduled-path flow fires at the
record's Send_Date_Time__c, and
JBSystemFlow_Refill_Reminder_c (a
Salesforce flow, not SFMC) fires the bulk event
that enters the SFMC journey. Under the 2026-07-08 direction it
also mints the link before firing that event — SFMC's
job from here is render + deliver.
@InvocableMethod Apex action behind a Named
Credential (Entra client credentials as
MagicLink-Salesforce-Client) POSTs to the
machine-only mint host
https://mint.advancedrx.dev/api/magic-links with the
patient's Contact Id and portal User Id (read directly
from Contact.Portal_User__c), channel, retPath, and
an idempotency key (the reminder Id).
{ shortUrl, expiresAtUtc } to
Salesforce.
shortUrl rides the journey-event
payload into the data extension and the template renders it as a
merge field — no AMPScript API call. The message goes out with
the patient-facing link
https://login.advancedrx.net/r/Kj3pQ9xMv7nR (a
separate host from the machine-only mint endpoint). Blank URL
(mint failed) → the plain reminder still sends — fail-open.
CryptographyClient.SignAsync handles the signature
in-vault.
/singleaccess for a
one-time-use redirect URL.
AdvancedRx already operates a HIPAA-compliant Azure VNet for eFax, e-script, and patient-matcher. Reusing it for magic-link gives wins that are hard to get inside Salesforce.
The bridge-side footprint is small and proven: the ECA, cert binding, and demo patient were all verified in the sandbox 2026-05-26; production go-live repeats it against the prod org (§8). The 2026-07-08 direction change added one new delivery — the mint caller, built 2026-08-19 and eligibility-gated 2026-08-24:
AdminApprovedPreAuthorized permitted-users policy bound
via SetupEntityAccess to a small permset, and IP
relaxation (Bypass; clicks come from arbitrary patient
IPs). Five metadata files: ECA, OAuth Settings, Global OAuth
Settings, Configurable Policies, Certificate.
Cert binding: the Key Vault keypair must exist first (Phase
1); then the public cert is bound to the ECA
inline as base64 in
GlobalOauthSettings.<certificate>, deployed via
the metadata API
— no JKS, no Setup-UI import. Salesforce holds the public cert only;
Azure signs with the non-exportable Key Vault key. (Bound + verified
in ClaudeTest 2026-05-26.) Full procedure in
cert-rotation-runbook.md.
Once created, the ECA's consumer key is handed back to Azure as App
Config MagicLink:Salesforce:EcaClientId — Azure redeem
is non-functional until that's set.
MagicLink-Salesforce-Client) + Named Credential
Magic_Link_Mint +
MagicLinkMintInvocable (13/13 tests). The contract
requires an eligibility verdict (2026-08-24):
calls without one are refused and logged
(RefusedNoVerdict); an ineligible patient is skipped
quietly (SkippedIneligible). Since revision 23,
Magic_Link__c is the mint-attempt
record — every outcome upserts a row carrying
Mint_Status__c, with the URL written on
Minted only, so a new channel needs no new fields and
one query covers every lane. The URL stays FLS-gated to
Magic_Link_View_All and auto-blanked ~1 h per [D4].
Fails open per [D2-am] to
B2B_Custom_Exception__c; batch of 2 is the [D2]
tripwire. Remaining: the reminder-flow wiring itself — no longer
[D22]-gated, since D22 was deferred to v2 on 2026-08-25 [D22-am].
Explicitly not in scope on the Salesforce side: community-reachable endpoints, Custom Objects, Big Objects, LWC, public Experience Cloud routes, CMDT allowlists, AMPScript of any kind (the 2026-07-08 direction change removed the last SFMC API call). Apex + a Named Credential are now in scope — the one addition the direction change brought onto the Salesforce side.
Three calls shape the architecture. Each was evaluated explicitly against alternatives; each is reversible if practice proves the trade wrong.
The architecture depends on two things working that aren't documented for our exact configuration. Both spikes are small, parallelizable, and gating — neither requires a single line of production code.
/singleaccess (returned
frontdoor_uri JSON), followed the redirect chain
(sid session cookie set, HTTP 200 on the storefront
page).
SetupEntityAccess.SetupEntityType is
platform-inferred not writable. All folded into plan.md §6 Phase 0
so the production build doesn't re-derive them.
href values
completely unmodified — no wrapping through
click.advancedrx.email. Caveat: SFMC Test Sends often
skip click-tracking link wrapping; real journey/triggered-send
wrapping behavior deferred to Phase 6a A/B rollout for definitive
verification. V2 design accommodates either outcome.
Order-of-magnitude monthly estimate at expected refill-reminder volume. SQL adds are near-zero (reuses existing DB); Key Vault and App Insights are small even at scale.
The auth flow is proven end-to-end in the sandbox and the Azure plane was security-hardened on 2026-06-11 (see "Where we are now"). What remains is the Salesforce-prod go-live plus the one open storefront-render gap (prod-only data, not a flow defect).
Bridge_Success ×2 and SF LoginHistory 4/4
successful logins all from the Azure NAT IP, zero impersonation. The
JWT bridge is exonerated.
Magic_Link_Bridge ECA + permset +
SetupEntityAccess, bind its public cert inline, and hand the
consumer key to Azure. ⚠ SetupEntityAccess is NOT carried by source control
— proven by test 2026-08-25:
a clean five-component deploy into an empty sandbox produced
0 rows against ClaudeTest's 1. The post-deploy Apex
insert (§7.6 step 3) is mandatory, the deploy stays
green without it, and the only symptom is a patient seeing “the login
link has expired” — so run the
SetupEntityAccess verification query before any patient
traffic. Consumer key goes to Azure as
MagicLink:Salesforce:EcaClientId; Azure then reverts
its 3 sandbox App Config values (TokenEndpoint,
SingleAccessEndpoint, EcaClientId) back to prod
and bumps MagicLink:Settings:Sentinel
so running instances pick them up (audit finding #6). Watch-out: the
ECA source has the sandbox org Id baked into
orgScopedExternalApp — swap it for the prod org
Id before deploy. Doesn't touch SFMC creds, so it's not gated. See §4
+ plan §6 Phase 4.
MagicLink-Salesforce-Client (+ allowlist its appId in
Azure App Config — config-only on the Azure side), External + Named
Credential, an @InvocableMethod mint action called
from the reminder pipeline, and a home for the minted URL. Design
open: batch fan-out vs the 100-callout cap, TTL vs dispatch→send
lag, URL field FLS, fail-open. Salesforce reads the portal User Id
directly from Contact.Portal_User__c at mint time — no
data-extension hop (the 2026-05-26 audit facts carry over).
No longer blocked on the SFMC credential rotation
— that stays open for SFMC's own hygiene
(MARISSA-HANDOFF.md §2 #2) but gates nothing
magic-link.
MagicLinkUrl; SMS + OMM email templates
render it as a merge field (blank → plain-reminder fallback).
Send-classification check, one real-send URL-wrapping verification,
then the 10% → 50% → 100% A/B rollout.
If the prod CCP Login + singleaccess exchange fails, an alternative Salesforce-only architecture is available as a fallback design record at ../magic-link-portal-login.md.
The architecture is reviewed against the existing Azure infrastructure repo and the live Salesforce + SFMC landscape.
fn-magiclink-prod-east2 Running + HTTPS-only; SCM and FTP
basic-publishing-credentials both allow=false (finding #1
confirmed live). App Config endpoint values are readable only from
inside the VNet (jumpbox) — confirm / revert at go-live.singleaccess bridge was exercised in
ClaudeTest against the demo portal user. Proof on both
sides: Azure SQL Bridge_Success ×2 /
RedeemCount=2 with zero failures, and Salesforce
LoginHistory showing
4/4 successful logins, all from the Azure NAT IP
135.119.140.149
(2 Magic_Link_Bridge ECA token exchanges + 2 storefront
session handoffs), with zero human/impersonation logins — the JWT
bridge is fully exonerated. The only post-auth failure is the
storefront render (prod-only commerce data; §8 + plan §10 Risk
#19).advancedrx-document-integration. The mint contract in
§7.1 is reconciled directly against the source
(MintMagicLinkFunction.cs) — confirming mint requires
both Contact Id and portal User Id, returns
{ shortUrl, expiresAtUtc }, and authenticates via Entra
OAuth client credentials. Split hosts: machine-only
mint.advancedrx.dev + patient
login.advancedrx.net, both fronted by Azure Front Door.
As-built facts in plan.md §4.6.storefront-data-audit.md (B2B storefront serving at
https://advancedrx.net/vforcesite/ on the CCP Login
license profile), integrations/sfmc.md (the existing
refill-reminder send pipeline at
JBSystemFlow_Refill_Reminder_c), and the in-progress SFMC
credential rotation per MARISSA-HANDOFF.md §2 #2.ClaudeTest 2026-05-18 — full
handshake (token endpoint → singleaccess → frontdoor redirect →
sid cookie set) confirmed for the CCP Login license.hello@advancedrx.email, vanity click-tracking host
click.advancedrx.email, dual-DKIM signed, DMARC pass)
captured in plan.md §4.5.ClaudeTest sandbox on
2026-05-21 to validate the deployable shape before the production
build. Three execution gotchas surfaced — cert binding requires an
imported public cert (not a Salesforce-generated one); ECA policy
enums differ from legacy ConnectedApp (AdminApprovedPreAuthorized
+ Bypass);
SetupEntityAccess.SetupEntityType is platform-inferred,
not writable via Apex. All three folded into plan.md §6 Phase 0 + §7.6
+ the Phase 4 build steps so the production deploy doesn't re-derive
them. The sandbox artifacts are throwaway; production deploy is still
pending. Cert-import procedure:
cert-rotation-runbook.md.MintMagicLinkFunction.cs (requires
contactId + userId + channel +
retPath + idempotencyKey; returns
{ shortUrl, expiresAtUtc }); the Azure-side change is
allowlist-config-only. The SFMC mint wiring was never built and the
MagicLink-SFMC-Client secret was never distributed, so
nothing gets unwound.