Problem
The CommCare Connect OAuth provider (/o/introspect/, /o/userinfo/) does not return user profile information (email, name) to OAuth clients. The labs environment (labs.connect.dimagi.com) authenticates users via OAuth against Connect production, but cannot determine the user's email address.
This matters because:
- Labs needs email to identify
@dimagi.com staff users and optimize the UI accordingly
- Without email, multiple users without emails were stored with
email="", causing unique_user_email constraint violations and 500 errors on login (now worked around by storing NULL)
- The current fallback chain (checking
sub field for @ character, checking org data endpoint) is fragile and incomplete
Current State
- OIDC is not enabled —
OAUTH2_PROVIDER settings do not include OIDC_ENABLED: True
- No custom validator — uses default Django OAuth Toolkit validator, which only returns
active, scope, client_id, username, token_type, exp, user_id on introspection
- No
openid scope available in the scopes configuration
- The
User model on Connect does have an email field that is populated for users who authenticate via CommCare HQ — the data exists, it's just not exposed via OAuth
Proposed Solution
Follow the same pattern already used in dimagi/connect-id (users/oauth.py), which has a custom ConnectOAuth2Validator that adds claims via get_additional_claims().
On commcare-connect:
-
Enable OIDC in config/settings/base.py:
OAUTH2_PROVIDER = {
...
"OIDC_ENABLED": True,
"OIDC_RSA_PRIVATE_KEY": env("OIDC_RSA_PRIVATE_KEY", default=""),
"SCOPES": {
"read": "Read scope",
"write": "Write scope",
"export": "Allow exporting data to other platforms using export API's.",
"openid": "OpenID Connect scope",
},
}
-
Add a custom OAuth validator:
from oauth2_provider.oauth2_validators import OAuth2Validator
class ConnectOAuth2Validator(OAuth2Validator):
oidc_claim_scope = OAuth2Validator.oidc_claim_scope
oidc_claim_scope.update({"email": "openid", "name": "openid"})
def get_additional_claims(self, request):
return {
"sub": request.user.username,
"email": request.user.email or "",
"name": request.user.name or "",
}
-
Set the validator in settings:
OAUTH2_PROVIDER = {
...
"OAUTH2_VALIDATOR_CLASS": "path.to.ConnectOAuth2Validator",
}
-
Generate an RSA private key for OIDC token signing and add to environment/secrets.
On labs (no changes needed from Connect side):
Labs would add openid to LABS_OAUTH_SCOPES and email would arrive natively via introspection/userinfo.
Context
- Labs OAuth flow:
oauth_views.py in commcare_connect/labs/integrations/connect/
- ConnectID example:
users/oauth.py in dimagi/connect-id repo
- Related labs fix: dimagi/commcare-connect-labs@cf15f97 (store NULL instead of empty string for missing emails)
Problem
The CommCare Connect OAuth provider (
/o/introspect/,/o/userinfo/) does not return user profile information (email, name) to OAuth clients. The labs environment (labs.connect.dimagi.com) authenticates users via OAuth against Connect production, but cannot determine the user's email address.This matters because:
@dimagi.comstaff users and optimize the UI accordinglyemail="", causingunique_user_emailconstraint violations and 500 errors on login (now worked around by storing NULL)subfield for@character, checking org data endpoint) is fragile and incompleteCurrent State
OAUTH2_PROVIDERsettings do not includeOIDC_ENABLED: Trueactive,scope,client_id,username,token_type,exp,user_idon introspectionopenidscope available in the scopes configurationUsermodel on Connect does have anemailfield that is populated for users who authenticate via CommCare HQ — the data exists, it's just not exposed via OAuthProposed Solution
Follow the same pattern already used in
dimagi/connect-id(users/oauth.py), which has a customConnectOAuth2Validatorthat adds claims viaget_additional_claims().On commcare-connect:
Enable OIDC in
config/settings/base.py:Add a custom OAuth validator:
Set the validator in settings:
Generate an RSA private key for OIDC token signing and add to environment/secrets.
On labs (no changes needed from Connect side):
Labs would add
openidtoLABS_OAUTH_SCOPESand email would arrive natively via introspection/userinfo.Context
oauth_views.pyincommcare_connect/labs/integrations/connect/users/oauth.pyindimagi/connect-idrepo