Environment
| Detail | Value |
|---|---|
| Open edX Release | Ulmo |
| Tutor Version | v21.0.5 |
| Deployment | AWS EC2, native Open edX (no custom plugins) |
Summary
Google SSO works correctly on the web, but fails on the Android mobile app at the exchange_access_token endpoint with {"error":"invalid_grant","error_description":"access_token is not valid"}. Interestingly, the server logs confirm that the Google access token is successfully validated upstream and user details are resolved — yet the endpoint returns HTTP 400.
Setup
- One Google Cloud project with two OAuth 2.0 credentials: one for the Web platform, one for the Android platform.
- Email/password login on Android works without issues.
Request Being Made (from Android app)
bash
curl 'https://ourlms.com/oauth2/exchange_access_token/google-oauth2/' \
-X POST \
-H 'accept: application/json' \
-H 'content-type: application/x-www-form-urlencoded' \
-H 'user-agent: Dalvik/2.1.0 (Linux; U; Android 12; ...)' \
--data-raw 'access_token=<GOOGLE_ACCESS_TOKEN>&client_id=<DJANGO_OAUTH_CLIENT_ID>&token_type=JWT&asymmetric_jwt=true'
Response
json
{"error": "invalid_grant", "error_description": "access_token is not valid"}
HTTP Status: 400 Bad Request
Server Logs
lms-1 | 2026-06-24 22:40:26,804 INFO 13 [tracking] [user None] [ip 43.241.146.120] logger.py:41 - {"name": "/oauth2/exchange_access_token/google-oauth2/", "context": {"user_id": null, "path": "/oauth2/exchange_access_token/google-oauth2/", "course_id": "", "org_id": "", "enterprise_uuid": ""}, "username": "", "session": "", "ip": "43.241.146.120", "agent": "Dalvik/2.1.0 (Linux; U; Android 12; Redmi Note 9 Pro Max Build/SKQ1.211019.001) 1.0.0", "host": "ourlms.com", "referer": "", "accept_language": "", "event": "{\"GET\": {}, \"POST\": {\"access_token\": [\"<ACCESS TOKEN GENERATED BY GOOGLE>\"], \"client_id\": [\"<CLIENT id>\"], \"token_type\": [\"JWT\"], \"asymmetric_jwt\": [\"true\"]}}", "time": "2026-06-24T17:10:26.803941+00:00", "event_type": "/oauth2/exchange_access_token/google-oauth2/", "event_source": "server", "page": null}
lms-1 | 2026-06-24 22:40:27,020 INFO 13 [common.djangoapps.third_party_auth.pipeline] [user None] [ip 43.241.146.120] pipeline.py:1048 - [THIRD_PARTY_AUTH] get_username complete: details={'username': 'mahendra', 'email': 'mahendra@gmail.com', 'fullname': 'Mahendra', 'first_name': 'Mahendra', 'last_name': 'Chaudhari'}, final_username=mahendra
lms-1 | [pid: 13|app: 0|req: 102/318] 172.18.0.9 () {44 vars in 758 bytes} [Wed Jun 24 22:40:26 2026] POST /oauth2/exchange_access_token/google-oauth2/ => generated 73 bytes in 246 msecs (HTTP/1.1 400) 8 headers in 536 bytes (1 switches on core 0)
The pipeline successfully reaches get_username and resolves user details from the Google token. The failure happens after this step during the actual token exchange / grant validation.
What I’ve Verified
- Google access token is fresh and valid (confirmed via Google’s tokeninfo endpoint)
- Web SSO with the same Google account works correctly
- Android OAuth credentials in Google Console are correctly configured (package name + SHA-1 fingerprint)
- The
client_idused in the request matches the Django OAuth Toolkit application created for the Android app - Email/password login on Android works
Has anyone faced this issue?
Any guidance or pointers to resolve this issue would be greatly appreciated.
Thanks