Public Webforms: Authentication - #1791
Open
nospame wants to merge 4 commits into
Open
Conversation
When a request carries the `CommCare-Public-Session: true` header and a `public_form_session_key` cookie, the session auth filter now produces a typed PublicSessionCredential instead of the Django sessionid string, and HqUserDetailsService sends it to HQ's session_details endpoint as `publicSessionKey` rather than `sessionId`. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
HQ's session_details response marks a public web apps session (one-time link) with a JSON `public` field. Add a boolean publicSession field mapped via @JsonProperty("public"). Uses a primitive boolean so a missing field defaults to false, which matters because the bean is @JsonIgnoreProperties(ignoreUnknown = true) and would otherwise silently drop it. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
When HqUserDetailsBean.publicSession is true, isAuthorized() no longer requires the request's username to equal the bean's username. Public web apps sessions authenticate via a single-use key that HQ validates server-to-server, and their username is a synthetic per-session string (not a real account), so echoing it is not a meaningful membership control. The requested domain is still required to be the session's domain (domains.contains(domain)). This keeps a session key from being replayed against a different domain. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
For a public web apps session, formplayer's outbound calls to HQ must send the `public_form_session_key` cookie together with the `CommCare-Public-Session: true` header, and must NOT send the Django `sessionid`. - New `PublicFormSessionAuth` (an `HqAuth`) emits exactly that cookie+header pair and nothing else; its key is guarded and never logged. - `UserRestoreAspect.getHqAuth` now selects the credential for the request: if the authenticated user is a public session it returns a `PublicFormSessionAuth` built from the session key, otherwise the existing `DjangoAuth`/null. Gated on the HMAC-authenticated `public` field (`isPublicSession()`), never on the client-supplied header; the public credential is preferred when both signals are present. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## master #1791 +/- ##
============================================
+ Coverage 70.10% 70.31% +0.21%
- Complexity 2027 2046 +19
============================================
Files 257 259 +2
Lines 8004 8035 +31
Branches 762 767 +5
============================================
+ Hits 5611 5650 +39
+ Misses 2110 2100 -10
- Partials 283 285 +2 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
This was referenced Jul 27, 2026
nospame
marked this pull request as ready for review
July 27, 2026 18:54
kaapstorm
approved these changes
Jul 28, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Technical Summary
SAAS-19925, SAAS-19926, SAAS-19927
unauthenticated recipient fill a single pre-designated form). This PR is the authentication layer
only — recognizing and authenticating a public session, inbound and outbound.
CommCareSessionAuthFilter: a request with theCommCare-Public-Session: trueheader and apublic_form_session_keycookie is wrapped as aPublicSessionCredential.HqUserDetailsService: public sessions send the key aspublicSessionKey(notsessionId) tosession_details.HqUserDetailsBean: new HMAC-authoritativepublicfield;isAuthorizeddrops thesynthetic-username check for public sessions but still requires the request domain to match.
PublicFormSessionAuth: authenticates formplayer's outbound HQ calls with the session key,selected in
UserRestoreAspect#getHqAuth.CommCare-Public-Sessionheader is only a credential-routing hint —trust comes solely from HQ's
publicfield, returned over the HMAC-authenticatedsession_detailscall.
Code and PR description written or co-written by AI and edited by human. Review by commit.
Safety Assurance
Safety story
Purely additive and gated behind a new session type. The existing Django-cookie and HMAC auth paths
are untouched; the new branches only fire when the public header + cookie are present and HQ
confirms
public. No public session can authenticate until HQ issues one-time links. Noexisting data is read or written differently for non-public requests.
Automated test coverage
SessionAuthTests— credential routing, header-vs-cookie precedence, failure when the key is absent.HqUserDetailsServiceTests—publicSessionKeywire format vssessionId.HqUserDetailsTests— domain-onlyisAuthorizedfor public sessions,publicdeserialization.PublicFormSessionAuthTest,UserRestoreAspectTest— outbound-auth selection.QA Plan
Public Webforms will get end-to-end QA before its release.
Special deploy instructions
Though public form sessions are not yet being created in HQ, this PR should not be deployed without changes from #1792, which add required security checks to public form sessions.
Rollback instructions
Review