[CEP] "Single-use" data links
See original GitHub issueAbstract
This CEP introduces a new mechanism for interfacing with CommCare data: a “single-use” link.
Single use links expose an API to:
- View data from a specific set of cases
- (optionally) update data in those cases
- (tbd) submit non-case form data?
- (tbd) create new cases
- (tbd) view other data
Unlike other data mechanisms, single-use links do not require logging into CommCare. Thus they will need to rely on a few alternate measures for privacy and security:
- Fine-grained control over what they can do/access.
- Setting explicit expiry/validity dates.
- Obfuscated non-guessable URL access.
- Expiry after being used once.
Motivation
These links will be used for consumer-facing applications and integrations where sign-in is not an option. A longer treatment on the motivation can be found in this document.
Note that this CEP represents a subset of the goals of that document. There will likely be other CEPs in the future to fully meet the document’s designed use case.
Specification
These will be implemented as a new model, which might look something like this:
class SingleUseLink(models.Model):
link_id = models.UUIDField(unique=True, db_index=True, default=uuid.uuid4)
domain = models.CharField(max_length=126, null=False, db_index=True)
created_on = models.DateTimeField(auto_now=True)
expires_on = models.DateTimeField(null=True, blank=True)
allows_submission = models.BooleanField(default=False, help_text=_('If the link allows data submission'))
submitting_user = models.ForeignKey(
User, null=True, blank=True, on_delete=models.SET_NULL,
help_text=_('For links that allow data submission, the user to be used to submit data.'),
)
is_visited = models.BooleanField(default=False)
visited_on = models.DateTimeField(null=True, blank=True)
is_used = models.BooleanField(default=False)
used_on = models.DateTimeField(null=True, blank=True)
class CaseReference(models.Model):
link = models.ForeignKey(SingleUseLink, on_delete=models.CASCADE, related_name='case_data')
case_id = models.UUIDField()
# in the future could also attach metadata
class Meta:
unique_together = ('link', 'case_id')
The link data will be accessible via an API. E.g. something like this:
GET /a/domain/api/v0.5/single-use-data/<link_id>
{ "cases": [ {case json}, {case json}] }
And can modify cases in similar fashion. E.g.:
POST /a/domain/api/v0.5/single-use-data/<link_id>
{ "cases": { "<case id 1>": {"p1": "v1"}, "<case id 2>": {"p1": "v2"}, } }
After being used to modify data once, the link will no longer be usable for data retrieval or submission.
Impact on users
No impact at the moment. This is to enable new future workflows.
Impact on hosting
No impact at the moment or forever if they choose not to use the feature.
Backwards compatibility
Fully backwards compatible.
Release Timeline
No concrete timeline yet.
Open questions and issues
- The exact API details (and requirements around what data needs to be available and modifiable) are still being worked out. Any input welcome. One random thought is whether the link should be thought of as its own API, versus like a single-use access token into other APIs. I think the former adds less complexity, but the latter would certainly be more flexible long-term.
- What obfuscation scheme should we use? Do we need more than random UUIDs for these?
- Are there any other security concerns that need to be included at this stage?
- Is “single-use” the right name? Some workflows may want multiple-use, so maybe just “data link”?
Issue Analytics
- State:
- Created 3 years ago
- Comments:11 (11 by maintainers)
Top GitHub Comments
I’m actually not 100% sure precisely what the final designs turned out to be, so they might have been more limited.
I believe that what is submitted by the known real teams is a single payload that contains N new cases (one per contact), each of which has an index pointing to the patient, and those cases need access to the correct owner_id, which they currently retrieve from a list stored in a fixture with a separate API request and then choose based on region.
I agree that this is a limitation, although none of the current Single-Use integrations actually rely on data that really changes from my understanding. For contact sharing, the only fields that are shared are the Patient DoB (used to validate the Patient) and their ID (used to create the response payload).
In some ways the limitation of not dynamically determining what data is shared feels a bit like it could be a ‘good’ limitation to me. It enables the known and safe use cases like delivering a test result or allowing contact entry without extending an unknown surface area.
I think this is just a bit like applying authentication through a session token scheme.
A lab test website I used functioned a bit like this, I think. They sent a link which I think is analogous to your single-use model, once I clicked on it they asked me for a DoB and then after sent an OTP with a short timeout to my email to complete authentication and enable the session.
fyi I’m reworking this so will close for now.