Skip to content

endpoints to receive/forward hq invites #51

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Dec 11, 2024
Merged

endpoints to receive/forward hq invites #51

merged 5 commits into from
Dec 11, 2024

Conversation

sravfeyn
Copy link
Member

@sravfeyn sravfeyn requested a review from calellowitz November 11, 2024 12:02
user_token = request.data["user_token"]
invite_code = unquote_plus(callback_url)
try:
response = requests.post(callback_url, data={"invite_code": invite_code, "token": user_token})

Check failure

Code scanning / CodeQL

Full server-side request forgery Critical

The full URL of this request depends on a
user-provided value
.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you planning to address this? Given your answer below about where this points, it should be doable to check it against an allowed list (only hq instances?)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I have addressed this

Copy link
Collaborator

@calellowitz calellowitz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couple small questions

users/views.py Outdated
authentication_classes = [ClientProtectedResourceAuth]

def post(self, request, *args, **kwargs):
callback_url = quote_plus(request.data["callback_url"])
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

where does callback_url come from and point to?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is all according to the modified spec.

This comes from HQ and it points to HQ's invite confirmation view. It's for actual invite confirmation, when mobile confirms it to ConnectID, ConnectID calls back HQ on callback_url with the user's token.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the HQ change dimagi/commcare-hq@407303f

users/views.py Outdated


class ConfirmHQInviteCallback(APIView):
authentication_classes = [ClientProtectedResourceAuth]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Who makes this call? Is this not from mobile?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this is made by mobile.

From spec

To accept invitation (when user confirms), call below endpoint

‘connectid.dimagi.com/users/confirm_hq_invite’

POST params
callback_url: callback_url (forwarded in deep link)
invite_code: invite_code (forwarded in deep link)
user_token: user’s active auth token

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In that case, this auth is incorrect. It only works with oauth client credentials grants (what we use for connect or other servers). I think the default auth classes should be fine.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right! I will remove those classes.

@sravfeyn
Copy link
Member Author

@calellowitz Please let me know if this is good to go.

Copy link
Collaborator

@calellowitz calellowitz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left a few questions on the new code in addition to the previous comments

@@ -28,3 +28,10 @@ def assetlinks_json(request):
},
]
return JsonResponse(assetfile, safe=False)


def deeplink(request, subpath):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we able to direct them directly to the play store? That is what we discussed on the call and seems to make the most sense if it is possible. Mobile seemed to believe that it would be.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, I have updated it to redirect to the app.

Screenshot 2024-11-22 at 12 31 38 PM

@@ -434,7 +434,8 @@ def post(self, request, *args, **kwargs):
# We don't want to make this a user lookup service
# So fake a success message
return JsonResponse({"success": True})
deeplink = f"https://connectid.dimagi.com/hq_invite/{callback_url}/{username}/{invite_code}/{user_domain}"
details = f"{callback_url}/{username}/{invite_code}/{user_domain}/{user.username}"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we spoke about this a while ago, but why is this encoded as a path rather than using query params (which libraries on web and mobile could parse much more easily)?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I think there was some issue regarding formatting this using query params on mobile, this format was specified by mobile.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I spoke to @OrangeAndGreen and he seemed to believe query params would be better for them as well. Let's offline on slack about this with a mobile dev.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, I have updated this to use queryparams.

@sravfeyn
Copy link
Member Author

@calellowitz Thanks for the review, I have followed up.

# and the URL is opened in the browser, ask the
# user to install the app and try again
return HttpResponse("Please install CommCare and re-open this link to accept invite")
html_content = """
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why inline HTML instead of using a separate file

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have moved it to its own dir.

@@ -80,7 +80,10 @@
WSGI_APPLICATION = 'connectid.wsgi.application'



TRUSTED_COMMCAREHQ_HOSTS = [
"commcarehq.org",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how does this work? Specifically, if commcarehq.org is enough to catch www.commcarehq.org I would expect it to also catch staging, and if we need to be more specific, I would expect www to be required.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, I will add www to the list to be specific.

@@ -434,7 +434,8 @@ def post(self, request, *args, **kwargs):
# We don't want to make this a user lookup service
# So fake a success message
return JsonResponse({"success": True})
deeplink = f"https://connectid.dimagi.com/hq_invite/{callback_url}/{username}/{invite_code}/{user_domain}"
details = f"{callback_url}/{username}/{invite_code}/{user_domain}/{user.username}"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I spoke to @OrangeAndGreen and he seemed to believe query params would be better for them as well. Let's offline on slack about this with a mobile dev.

Copy link
Collaborator

@calellowitz calellowitz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couple small questions

@sravfeyn
Copy link
Member Author

@calellowitz I have addressed your feedback, please have a look. I think the CodeQL failure is incorrect as it is not able to detect the trusted URL check that is added.

@sravfeyn
Copy link
Member Author

@calellowitz Bumping for review please.

@sravfeyn sravfeyn merged commit ef65275 into main Dec 11, 2024
1 of 2 checks passed
@sravfeyn
Copy link
Member Author

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.

2 participants