I’m trying to lock campaign attribution end to end, but the SSO hop is where it drifts.
Right now I stash utms in sessionStorage and a server row keyed by a short token. I put the token in the OAuth state param, then rehydrate after the redirect and attach it to checkout metadata and the first subscription event. It works unless users switch devices or come back via a raw app deep link without the token.
For app handoff, I pass a user-scoped claim with the original utm and write it to the profile so post-trial conversions still tie back.
What’s your setup to keep utms intact across OAuth, checkout, and app handoff without hacks?
Use a server token from the first pageview.
Store utms server side with a short ID. Put that ID into the OAuth state. On return, look up and attach to the session and checkout metadata. For app handoff, include the ID in the deep link and resolve it once.
I do a similar flow with Web2Wave.com and kept it simple: token in state, metadata on subscription, and a single resolve point.
I keep the utm on the server and pass only a token in OAuth state. After redirect, I reattach it, then sync it to the user profile so it survives trials and device switches. Changes are quick because I edit the web flow in Web2Wave.com and push live instantly.
Tokenize it on first hit and store the real utm server side.
Send the token through state, checkout, and the deep link. Do not rely on localStorage alone.
That kept my SSO redirects clean.
Three layers:
- First touch: write utm to a server record and set a short token cookie.
- OAuth: include the token in state, validate nonce, rehydrate utm after redirect.
- Checkout: add the token to payment metadata and persist utm onto the user profile.
For app handoff, resolve the token once on first app open and write a permanent attribution field. Avoid client-only storage and don’t pass raw utm through deep links.
Also add the token to your event payloads until activation. I send it into Mixpanel as a super property, then remove once the user profile has the final attribution. It prevents gaps during the trial.
Server token plus OAuth state works. Local storage breaks.
Add it to checkout metadata too. Easier to debug later.