Problem Description
Hello Discourse community,
We are trying to integrate QQ OAuth2 login with our Discourse instance, but we are encountering persistent issues. Specifically, we are seeing two main errors: ERR_INVALID_REDIRECT
when oauth2_authorize_url
and oauth2_token_url
are empty, and invalid_credentials
when these fields are set. We have a proxy script to handle the OAuth2 flow, but Discourse does not seem to work as expected with QQ’s API. We need help to resolve this issue.
Special Case: QQ OAuth2 Flow and API Response Format
**QQ OAuth2 Flow Requires an Extra Step
Unlike standard OAuth2 flows, QQ’s OAuth2 process requires an additional step to fetch the openid
. After obtaining the access_token
from the token endpoint (https://graph.qq.com/oauth2.0/token
), you must make a request to the /me
endpoint (https://graph.qq.com/oauth2.0/me?access_token=xxx
) to get the openid
. This openid
is then required to fetch user information via the /user/get_user_info
endpoint. The omniauth-oauth2
plugin in Discourse does not natively support this extra step, which makes integration challenging.
**QQ API Response Format **
Another issue is that QQ’s API does not return a standard JSON response for the access_token
. Instead, it returns a string in the format:
access_token=xxx&expires_in=5184000&refresh_token=xxx
This non-JSON format seems to cause issues with Discourse’s omniauth-oauth2
plugin, which likely expects a JSON response (e.g., {"access_token": "xxx", "expires_in": 5184000}
). This mismatch may be the root cause of the invalid_credentials
error.
Case and Debugging Feedback
Here’s a detailed breakdown of our setup, the errors we encountered, and the debugging steps we’ve already taken:
-
**Setup **
-
Discourse instance: Running in a Docker container.
-
We created a proxy script (
qq_oauth_proxy.py
) running on a local server to handle the OAuth2 flow, as QQ’s API requires additional steps (e.g., fetchingopenid
and user info). -
Initial Discourse OAuth2 settings:
oauth2_enabled: true oauth2_client_id: [REDACTED] oauth2_client_secret: [REDACTED] oauth2_authorize_url: https://graph.qq.com/oauth2.0/authorize oauth2_token_url: https://graph.qq.com/oauth2.0/token oauth2_user_json_url: http://[local-server]:5001/oauth2/qq/callback?code=:code oauth2_fetch_user_details: true oauth2_json_user_id_path: id oauth2_json_username_path: username oauth2_json_email_path: email oauth2_scope: get_user_info
-
-
**Error 1: invalid_credentials **
-
When
oauth2_authorize_url
andoauth2_token_url
are set, Discourse successfully gets theaccess_token
from QQ:access_token=xxx&expires_in=5184000&refresh_token=xxx
-
However, Discourse then fails with the following error:
(oauth2_basic) Authentication failure! invalid_credentials: OAuth2::Error, access_token=xxx&expires_in=5184000&refresh_token=xxx
-
We suspect this is because QQ’s non-JSON response format is not compatible with
omniauth-oauth2
. Additionally, Discourse does not call our proxy script (http://[local-server]:5001/oauth2/qq/callback
) after getting theaccess_token
, which means our proxy script cannot handle the user info retrieval.
-
-
Error 2: ERR_INVALID_REDIRECT / 错误 2:ERR_INVALID_REDIRECT
-
To force Discourse to rely on our proxy script, we tried setting
oauth2_authorize_url
andoauth2_token_url
to empty. -
However, this caused a new error when clicking the “Login with QQ” button:
当前无法使用此页面 [domain] 已发送了无效的响应。 ERR_INVALID_REDIRECT
-
It seems that
omniauth-oauth2
requiresoauth2_authorize_url
to initiate the OAuth2 flow, and without it, Discourse cannot generate a valid redirect URL.
-
-
**Proxy Script Details **
-
Our proxy script (
qq_oauth_proxy.py
) works fine when tested independently. For example:curl "http://[local-server]:5001/oauth2/qq/callback?code=[test-code]"
Returns:
返回:{"access_token":"xxx","id":"xxx","username":"TDY","name":"TDY","email":null,"avatar":"http://thirdqq.qlogo.cn/[path]"}
-
The proxy script handles the following steps:
- Exchanges the
code
for anaccess_token
. - Fetches the
openid
using theaccess_token
via the/me
endpoint. - Fetches user info using the
access_token
andopenid
. - Returns a JSON response in the format Discourse expects.
- Exchanges the
-
-
**Debugging Steps Taken **
-
Tested the proxy script independently, and it works as expected.
-
Tried setting
oauth2_authorize_url
andoauth2_token_url
to empty to force Discourse to rely on the proxy script, but this causedERR_INVALID_REDIRECT
. -
Added debug logging to Discourse (
log_level: debug
) and confirmed that Discourse does not call the proxy script whenoauth2_authorize_url
andoauth2_token_url
are set. -
Considered modifying the proxy script to handle the entire OAuth2 flow (including the authorize step), but we are unsure if this is the best approach.
-
Questions
-
How can we make Discourse work with QQ’s non-JSON
access_token
response and the extra/me
step? Is there a way to customizeomniauth-oauth2
to handle these differences? -
Is there a way to configure Discourse to completely rely on our proxy script for the entire OAuth2 flow, including the authorize step?
-
Should we create a custom OAuth2 strategy for QQ in Discourse? If so, can you provide an example of how to handle QQ’s API (including the
/me
step) in a Discourse plugin? -
Any other suggestions to resolve the
ERR_INVALID_REDIRECT
andinvalid_credentials
errors?
Additional Information
-
Discourse version: Latest stable version as of April 2025 (running in Docker).
-
Proxy script logs and Discourse debug logs are available if needed.
Thank you for your help!