[GH-ISSUE #18010] issue: MCP OAuth 2.1 flow doesn't match standard (missing code_challenge and resource_url) #57130

Closed
opened 2026-05-05 20:38:19 -05:00 by GiteaMirror · 17 comments
Owner

Originally created by @hsuyuming on GitHub (Oct 2, 2025).
Original GitHub issue: https://github.com/open-webui/open-webui/issues/18010

Check Existing Issues

  • I have searched for any existing and/or related issues.
  • I have searched for any existing and/or related discussions.
  • I am using the latest version of Open WebUI.

Installation Method

Docker

Open WebUI Version

v0.6.32

Ollama Version (if applicable)

No response

Operating System

Debian 12

Browser (if applicable)

Chrome 140.0.7339.133

Confirmation

  • I have read and followed all instructions in README.md.
  • I am using the latest version of both Open WebUI and Ollama.
  • I have included the browser console logs.
  • I have included the Docker container logs.
  • I have provided every relevant configuration, setting, and environment variable used in my setup.
  • I have clearly listed every relevant configuration, custom setting, environment variable, and command-line option that influences my setup (such as Docker Compose overrides, .env values, browser settings, authentication configurations, etc).
  • I have documented step-by-step reproduction instructions that are precise, sequential, and leave nothing to interpretation. My steps:
  • Start with the initial platform/version/OS and dependencies used,
  • Specify exact install/launch/configure commands,
  • List URLs visited, user input (incl. example values/emails/passwords if needed),
  • Describe all options and toggles enabled or changed,
  • Include any files or environmental changes,
  • Identify the expected and actual result at each stage,
  • Ensure any reasonably skilled user can follow and hit the same issue.

Expected Behavior

Authorization URL

  1. If the MCP server metadata includes code_challenge_methods, the generated authorization URL should include both code_challenge and code_challenge_method to comply with PKCE (RFC 7636).
  2. The resource parameter should be included within authorization url as well.

Token URL

  1. client_id and client_secret should be included within request params.

Actual Behavior

Authorization URL

The current implementation for generating authorization URLs is missing several required parameters, leading to security vulnerabilities and non-compliance with the MCP server's requirements.

  1. Missing PKCE Parameters
    The code_challenge and code_challenge_method parameters are not being included in the authorization URL. These are essential for implementing PKCE (RFC 7636), which protects against authorization code interception attacks. This is a critical security measure and a requirement from the MCP server.
> /app/backend/open_webui/utils/oauth.py(555)handle_authorize()-> return await client.authorize_redirect(request, str(redirect_uri))
(Pdb) client
<authlib.integrations.starlette_client.apps.StarletteOAuth2App object at 0x7f69141b9c10>
........
> /usr/local/lib/python3.11/site-packages/authlib/integrations/base_client/sync_app.py(297)_create_oauth2_authorization_url()
-> rv["url"] = url
(Pdb) url
'https://<domain>/abehsu-mcp-test/authorize?response_type=code&client_id=3xxxxxxxxx9&redirect_uri=http%3A%2F%2Flocalhost%3A3000%2Foauth%2Fclients%2Fmcp%3Amcp-test%2Fcallback&scope=<scope>&state=lwr9pnULWSdDRr9MxLXNaa8uDl80J6'
Image
  1. Missing resource Parameter
    The resource parameter is also missing from the URL's query string. According to the latest MCP specification, this parameter is mandatory.
    https://modelcontextprotocol.io/specification/draft/basic/authorization#resource-parameter-implementation

token URL

The current implementation for generating token URLs is missing several required parameters (client_id, client_secret), after i included code_challenge_method when performing oauth.register (refer Additional Information)

(Pdb) token_endpoint
'https://<domain>/abehsu-mcp-test/token'
(Pdb) params
{'code': 'Lc_Cwg07UVuJ3_G8p3PbgEU3zvLvFwzFq89A1YyyocQ', 'state': 'xb9EuIcXeUIJnp0AGcIN6AtQ82Jcd3', 'code_verifier': 'xCeSXFAlGcTQTUO5yF8ivXSomRGcRiJkio9vjMDoi6dQztBM'}
Image Image

Steps to Reproduce

Setup a secured MCP server

  1. Follow this example: https://github.com/jlowin/fastmcp/tree/main/examples/auth/azure_oauth

Setup open-webui

  1. docker run -d -p 3000:8080 -v open-webui:/app/backend/data --name open-webui --add-host=host.docker.internal:host-gateway --restart always ghcr.io/open-webui/open-webui:dev
  2. Go to http://localhost:3000/
  3. Click to Admin Panel -> Settings -> External Tools -> Add tool -> MCP type -> Provide MCP server url -> select OAuth2.1 type -> provide Id and name -> click Register Client -> Save
    Image
  4. Create new chat -> Click integration -> Tools -> enable mcp tool
Image 5. Get error. Image

Logs & Screenshots

Error log within console

2025-10-02 17:58:50.321 | WARNING  | open_webui.utils.oauth:handle_callback:598 - OAuth callback error: invalid_request: code_challenge: Field required
2025-10-02 17:58:50.322 | INFO     | uvicorn.protocols.http.httptools_impl:send:476 - 172.17.0.1:44336 - "GET /oauth/clients/mcp%3Amcp-test/callback?error=invalid_request&error_description=code_challenge%3A+Field+required&state=lwr9pnULWSdDRr9MxLXNaa8uDl80J6 HTTP/1.1" 307
2025-10-02 18:11:39.253 | INFO     | httpx._client:_send_single_request:1740 - HTTP Request: POST https://<domain>/abehsu-mcp-test/token "HTTP/1.1 400 Bad Request"
2025-10-02 18:11:39.254 | WARNING  | open_webui.utils.oauth:handle_callback:600 - OAuth callback error: invalid_request: authorization_code.client_id: Field required
2025-10-02 18:11:39.255 | INFO     | uvicorn.protocols.http.httptools_impl:send:476 - 172.17.0.1:33332 - "GET /oauth/clients/mcp%3Amcp-test/callback?code=Lc_Cwg07UVuJ3_G8p3PbgEU3zvLvFwzFq89A1YyyocQ&state=xb9EuIcXeUIJnp0AGcIN6AtQ82Jcd3 HTTP/1.1" 307

Additional Information

Suggestion change

1.include code_challenge_method when performing oauth.register. I'm not sure if there's a way to avoid hardcoding the value here. Normally we can get this data from mcp server metadata

def add_client(self, client_id, oauth_client_info: OAuthClientInformationFull):
        self.clients[client_id] = {
            "client": self.oauth.register(
                name=client_id,
                client_id=oauth_client_info.client_id,
                client_secret=oauth_client_info.client_secret,
                code_challenge_method="S256",
                client_kwargs=(
                    {"scope": oauth_client_info.scope}
                    if oauth_client_info.scope
                    else {}
                ),
                server_metadata_url=(
                    oauth_client_info.issuer if oauth_client_info.issuer else None
                ),
            ),
            "client_info": oauth_client_info,
        }
        return self.clients[client_id]
Image

2.Since we can obtain the client_id and client_secret, I recommend retrieving client_info before calling authorize_access_token. If client_id and client_secret are available, we should include them in the request, so these two parameters can be added accordingly.

async def handle_callback(self, request, client_id: str, user_id: str, response):
        client = self.get_client(client_id)
        if client is None:
            raise HTTPException(404)

        error_message = None
        try:
            client_info = self.get_client_info(client_id)
            token_params = {}
            if client_info and hasattr(client_info, 'client_id') and hasattr(client_info, 'client_secret'):
                token_params['client_id'] = client_info.client_id
                token_params['client_secret'] = client_info.client_secret
            token = await client.authorize_access_token(request, **token_params)
            if token:
                try:
                    # Add timestamp for tracking
                    token["issued_at"] = datetime.now().timestamp()

                    # Calculate expires_at if we have expires_in
                    if "expires_in" in token and "expires_at" not in token:
                        token["expires_at"] = (
                            datetime.now().timestamp() + token["expires_in"]
                        )

                    # Clean up any existing sessions for this user/client_id first
                    sessions = OAuthSessions.get_sessions_by_user_id(user_id)
                    for session in sessions:
                        if session.provider == client_id:
                            OAuthSessions.delete_session_by_id(session.id)

                    session = OAuthSessions.create_session(
                        user_id=user_id,
                        provider=client_id,
                        token=token,
                    )
                    log.info(
                        f"Stored OAuth session server-side for user {user_id}, client_id {client_id}"
                    )
                except Exception as e:
                    error_message = "Failed to store OAuth session server-side"
                    log.error(f"Failed to store OAuth session server-side: {e}")
            else:
                error_message = "Failed to obtain OAuth token"
                log.warning(error_message)
        except Exception as e:
            error_message = "OAuth callback error"
            log.warning(f"OAuth callback error: {e}")

        redirect_url = (
            str(request.app.state.config.WEBUI_URL or request.base_url)
        ).rstrip("/")

        if error_message:
            log.debug(error_message)
            redirect_url = f"{redirect_url}/?error={error_message}"
            return RedirectResponse(url=redirect_url, headers=response.headers)

        response = RedirectResponse(url=redirect_url, headers=response.headers)
        return response

After change

> /app/backend/open_webui/utils/oauth.py(567)handle_callback()
-> token_params = {}
(Pdb) client_info
OAuthClientInformationFull(redirect_uris=[AnyUrl('http://localhost:3000/oauth/clients/mcp:mcp-test/callback')], token_endpoint_auth_method='client_secret_post', grant_types=['authorization_code', 'refresh_token'], response_types=['code'], scope='<scope>', client_name='Open WebUI', client_uri=None, logo_uri=None, contacts=None, tos_uri=None, policy_uri=None, jwks_uri=None, jwks=None, software_id=None, software_version=None, issuer='https://<domain>/.well-known/oauth-authorization-server/abehsu-mcp-test/mcp', client_id='3xxxxxxxx9', client_secret='exxxxxxxef', client_id_issued_at=1759427562, client_secret_expires_at=None)
(Pdb) n
> /app/backend/open_webui/utils/oauth.py(568)handle_callback()
-> if client_info and hasattr(client_info, 'client_id') and hasattr(client_info, 'client_secret'):
(Pdb) n
> /app/backend/open_webui/utils/oauth.py(569)handle_callback()
-> token_params['client_id'] = client_info.client_id
(Pdb) n
> /app/backend/open_webui/utils/oauth.py(570)handle_callback()
-> token_params['client_secret'] = client_info.client_secret
(Pdb) n
> /app/backend/open_webui/utils/oauth.py(571)handle_callback()
-> token = await client.authorize_access_token(request, **token_params)
......
> /usr/local/lib/python3.11/site-packages/authlib/integrations/base_client/async_app.py(133)fetch_access_token()
-> token = await client.fetch_token(token_endpoint, **params)
(Pdb) token_endpoint
'https://<domain>/abehsu-mcp-test/token'
(Pdb) params
{'code': 'NIbDIJvm2YsoQIWV_QAw_MzstTzOLsNcfcXHm178cYg', 'state': 'vOMqYjs7jmXk3ImLxrM0DzUSObEsc5', 'code_verifier': 'sGvR4f1MJjL3InSFUMvzJFUDZggqcWh53DeiAy2ORyhxUSIU', 'client_id': '3xxxxxxxx9', 'client_secret': 'exxxxxxxf'}

console log

2025-10-02 18:29:11.244 | INFO     | httpx._client:_send_single_request:1740 - HTTP Request: POST https://<domain>/abehsu-mcp-test/token "HTTP/1.1 200 OK"
2025-10-02 18:29:11.306 | INFO     | open_webui.utils.oauth:handle_callback:594 - Stored OAuth session server-side for user 5de6b285-17bf-40d1-adba-180834bcabff, client_id mcp:mcp-test

Provide the reference on how the MCP inspector handles this.

Image Image
Originally created by @hsuyuming on GitHub (Oct 2, 2025). Original GitHub issue: https://github.com/open-webui/open-webui/issues/18010 ### Check Existing Issues - [x] I have searched for any existing and/or related issues. - [x] I have searched for any existing and/or related discussions. - [x] I am using the latest version of Open WebUI. ### Installation Method Docker ### Open WebUI Version v0.6.32 ### Ollama Version (if applicable) _No response_ ### Operating System Debian 12 ### Browser (if applicable) Chrome 140.0.7339.133 ### Confirmation - [x] I have read and followed all instructions in `README.md`. - [x] I am using the latest version of **both** Open WebUI and Ollama. - [x] I have included the browser console logs. - [x] I have included the Docker container logs. - [x] I have **provided every relevant configuration, setting, and environment variable used in my setup.** - [x] I have clearly **listed every relevant configuration, custom setting, environment variable, and command-line option that influences my setup** (such as Docker Compose overrides, .env values, browser settings, authentication configurations, etc). - [x] I have documented **step-by-step reproduction instructions that are precise, sequential, and leave nothing to interpretation**. My steps: - Start with the initial platform/version/OS and dependencies used, - Specify exact install/launch/configure commands, - List URLs visited, user input (incl. example values/emails/passwords if needed), - Describe all options and toggles enabled or changed, - Include any files or environmental changes, - Identify the expected and actual result at each stage, - Ensure any reasonably skilled user can follow and hit the same issue. ### Expected Behavior ### Authorization URL 1. If the MCP server metadata includes code_challenge_methods, the generated authorization URL should include both code_challenge and code_challenge_method to comply with PKCE (RFC 7636). 2. The resource parameter should be included within authorization url as well. ### Token URL 1. client_id and client_secret should be included within request params. ### Actual Behavior ### Authorization URL The current implementation for generating authorization URLs is missing several required parameters, leading to security vulnerabilities and non-compliance with the MCP server's requirements. 1. Missing PKCE Parameters The code_challenge and code_challenge_method parameters are not being included in the authorization URL. These are essential for implementing PKCE (RFC 7636), which protects against authorization code interception attacks. This is a critical security measure and a requirement from the MCP server. ```cmd > /app/backend/open_webui/utils/oauth.py(555)handle_authorize()-> return await client.authorize_redirect(request, str(redirect_uri)) (Pdb) client <authlib.integrations.starlette_client.apps.StarletteOAuth2App object at 0x7f69141b9c10> ........ > /usr/local/lib/python3.11/site-packages/authlib/integrations/base_client/sync_app.py(297)_create_oauth2_authorization_url() -> rv["url"] = url (Pdb) url 'https://<domain>/abehsu-mcp-test/authorize?response_type=code&client_id=3xxxxxxxxx9&redirect_uri=http%3A%2F%2Flocalhost%3A3000%2Foauth%2Fclients%2Fmcp%3Amcp-test%2Fcallback&scope=<scope>&state=lwr9pnULWSdDRr9MxLXNaa8uDl80J6' ``` <img width="1200" height="860" alt="Image" src="https://github.com/user-attachments/assets/39385e3f-0885-42bb-bd54-f27029df53fc" /> 2. Missing resource Parameter The resource parameter is also missing from the URL's query string. According to the latest MCP specification, this parameter is mandatory. https://modelcontextprotocol.io/specification/draft/basic/authorization#resource-parameter-implementation ### token URL The current implementation for generating token URLs is missing several required parameters (client_id, client_secret), after i included code_challenge_method when performing oauth.register (refer Additional Information) ```cmd (Pdb) token_endpoint 'https://<domain>/abehsu-mcp-test/token' (Pdb) params {'code': 'Lc_Cwg07UVuJ3_G8p3PbgEU3zvLvFwzFq89A1YyyocQ', 'state': 'xb9EuIcXeUIJnp0AGcIN6AtQ82Jcd3', 'code_verifier': 'xCeSXFAlGcTQTUO5yF8ivXSomRGcRiJkio9vjMDoi6dQztBM'} ``` <img width="842" height="126" alt="Image" src="https://github.com/user-attachments/assets/bd756488-fdee-4674-9f31-c93617f4045f" /> <img width="1179" height="134" alt="Image" src="https://github.com/user-attachments/assets/74648ac4-fff0-4a99-82b8-93bc893e2984" /> ### Steps to Reproduce ### Setup a secured MCP server 1. Follow this example: https://github.com/jlowin/fastmcp/tree/main/examples/auth/azure_oauth ### Setup open-webui 1. docker run -d -p 3000:8080 -v open-webui:/app/backend/data --name open-webui --add-host=host.docker.internal:host-gateway --restart always ghcr.io/open-webui/open-webui:dev 2. Go to http://localhost:3000/ 3. Click to Admin Panel -> Settings -> External Tools -> Add tool -> MCP type -> Provide MCP server url -> select OAuth2.1 type -> provide Id and name -> click Register Client -> Save <img width="648" height="786" alt="Image" src="https://github.com/user-attachments/assets/f81b3a0d-410d-4967-92bf-b43e8654329b" /> 4. Create new chat -> Click integration -> Tools -> enable mcp tool <img width="1523" height="917" alt="Image" src="https://github.com/user-attachments/assets/6e145251-79ef-4627-9a29-a0c862363d20" /> 5. Get error. <img width="1728" height="870" alt="Image" src="https://github.com/user-attachments/assets/91e66f87-54d0-4584-905e-6c2b9449e060" /> ### Logs & Screenshots ### Error log within console ```cmd 2025-10-02 17:58:50.321 | WARNING | open_webui.utils.oauth:handle_callback:598 - OAuth callback error: invalid_request: code_challenge: Field required 2025-10-02 17:58:50.322 | INFO | uvicorn.protocols.http.httptools_impl:send:476 - 172.17.0.1:44336 - "GET /oauth/clients/mcp%3Amcp-test/callback?error=invalid_request&error_description=code_challenge%3A+Field+required&state=lwr9pnULWSdDRr9MxLXNaa8uDl80J6 HTTP/1.1" 307 ``` ```cmd 2025-10-02 18:11:39.253 | INFO | httpx._client:_send_single_request:1740 - HTTP Request: POST https://<domain>/abehsu-mcp-test/token "HTTP/1.1 400 Bad Request" 2025-10-02 18:11:39.254 | WARNING | open_webui.utils.oauth:handle_callback:600 - OAuth callback error: invalid_request: authorization_code.client_id: Field required 2025-10-02 18:11:39.255 | INFO | uvicorn.protocols.http.httptools_impl:send:476 - 172.17.0.1:33332 - "GET /oauth/clients/mcp%3Amcp-test/callback?code=Lc_Cwg07UVuJ3_G8p3PbgEU3zvLvFwzFq89A1YyyocQ&state=xb9EuIcXeUIJnp0AGcIN6AtQ82Jcd3 HTTP/1.1" 307 ``` ### Additional Information ### Suggestion change 1.include code_challenge_method when performing oauth.register. I'm not sure if there's a way to avoid hardcoding the value here. Normally we can get this data from mcp server metadata ``` def add_client(self, client_id, oauth_client_info: OAuthClientInformationFull): self.clients[client_id] = { "client": self.oauth.register( name=client_id, client_id=oauth_client_info.client_id, client_secret=oauth_client_info.client_secret, code_challenge_method="S256", client_kwargs=( {"scope": oauth_client_info.scope} if oauth_client_info.scope else {} ), server_metadata_url=( oauth_client_info.issuer if oauth_client_info.issuer else None ), ), "client_info": oauth_client_info, } return self.clients[client_id] ``` <img width="916" height="604" alt="Image" src="https://github.com/user-attachments/assets/ae657fba-10bd-4a0a-a796-f0d70cfc64d7" /> 2.Since we can obtain the client_id and client_secret, I recommend retrieving client_info before calling authorize_access_token. If client_id and client_secret are available, we should include them in the request, so these two parameters can be added accordingly. ``` async def handle_callback(self, request, client_id: str, user_id: str, response): client = self.get_client(client_id) if client is None: raise HTTPException(404) error_message = None try: client_info = self.get_client_info(client_id) token_params = {} if client_info and hasattr(client_info, 'client_id') and hasattr(client_info, 'client_secret'): token_params['client_id'] = client_info.client_id token_params['client_secret'] = client_info.client_secret token = await client.authorize_access_token(request, **token_params) if token: try: # Add timestamp for tracking token["issued_at"] = datetime.now().timestamp() # Calculate expires_at if we have expires_in if "expires_in" in token and "expires_at" not in token: token["expires_at"] = ( datetime.now().timestamp() + token["expires_in"] ) # Clean up any existing sessions for this user/client_id first sessions = OAuthSessions.get_sessions_by_user_id(user_id) for session in sessions: if session.provider == client_id: OAuthSessions.delete_session_by_id(session.id) session = OAuthSessions.create_session( user_id=user_id, provider=client_id, token=token, ) log.info( f"Stored OAuth session server-side for user {user_id}, client_id {client_id}" ) except Exception as e: error_message = "Failed to store OAuth session server-side" log.error(f"Failed to store OAuth session server-side: {e}") else: error_message = "Failed to obtain OAuth token" log.warning(error_message) except Exception as e: error_message = "OAuth callback error" log.warning(f"OAuth callback error: {e}") redirect_url = ( str(request.app.state.config.WEBUI_URL or request.base_url) ).rstrip("/") if error_message: log.debug(error_message) redirect_url = f"{redirect_url}/?error={error_message}" return RedirectResponse(url=redirect_url, headers=response.headers) response = RedirectResponse(url=redirect_url, headers=response.headers) return response ``` ### After change ``` > /app/backend/open_webui/utils/oauth.py(567)handle_callback() -> token_params = {} (Pdb) client_info OAuthClientInformationFull(redirect_uris=[AnyUrl('http://localhost:3000/oauth/clients/mcp:mcp-test/callback')], token_endpoint_auth_method='client_secret_post', grant_types=['authorization_code', 'refresh_token'], response_types=['code'], scope='<scope>', client_name='Open WebUI', client_uri=None, logo_uri=None, contacts=None, tos_uri=None, policy_uri=None, jwks_uri=None, jwks=None, software_id=None, software_version=None, issuer='https://<domain>/.well-known/oauth-authorization-server/abehsu-mcp-test/mcp', client_id='3xxxxxxxx9', client_secret='exxxxxxxef', client_id_issued_at=1759427562, client_secret_expires_at=None) (Pdb) n > /app/backend/open_webui/utils/oauth.py(568)handle_callback() -> if client_info and hasattr(client_info, 'client_id') and hasattr(client_info, 'client_secret'): (Pdb) n > /app/backend/open_webui/utils/oauth.py(569)handle_callback() -> token_params['client_id'] = client_info.client_id (Pdb) n > /app/backend/open_webui/utils/oauth.py(570)handle_callback() -> token_params['client_secret'] = client_info.client_secret (Pdb) n > /app/backend/open_webui/utils/oauth.py(571)handle_callback() -> token = await client.authorize_access_token(request, **token_params) ...... > /usr/local/lib/python3.11/site-packages/authlib/integrations/base_client/async_app.py(133)fetch_access_token() -> token = await client.fetch_token(token_endpoint, **params) (Pdb) token_endpoint 'https://<domain>/abehsu-mcp-test/token' (Pdb) params {'code': 'NIbDIJvm2YsoQIWV_QAw_MzstTzOLsNcfcXHm178cYg', 'state': 'vOMqYjs7jmXk3ImLxrM0DzUSObEsc5', 'code_verifier': 'sGvR4f1MJjL3InSFUMvzJFUDZggqcWh53DeiAy2ORyhxUSIU', 'client_id': '3xxxxxxxx9', 'client_secret': 'exxxxxxxf'} ``` console log ```cmd 2025-10-02 18:29:11.244 | INFO | httpx._client:_send_single_request:1740 - HTTP Request: POST https://<domain>/abehsu-mcp-test/token "HTTP/1.1 200 OK" 2025-10-02 18:29:11.306 | INFO | open_webui.utils.oauth:handle_callback:594 - Stored OAuth session server-side for user 5de6b285-17bf-40d1-adba-180834bcabff, client_id mcp:mcp-test ``` ### Provide the reference on how the MCP inspector handles this. <img width="1727" height="935" alt="Image" src="https://github.com/user-attachments/assets/fb8f1ff5-691f-42ea-b3d1-4da2fee3e0d0" /> <img width="1716" height="1002" alt="Image" src="https://github.com/user-attachments/assets/8e30f280-ca20-47fe-a0be-01e80db8c7ce" />
GiteaMirror added the bug label 2026-05-05 20:38:19 -05:00
Author
Owner

@hsuyuming commented on GitHub (Oct 2, 2025):

@tjbck I would like to get your feedback to better understand your perspective on this.

<!-- gh-comment-id:3362526048 --> @hsuyuming commented on GitHub (Oct 2, 2025): @tjbck I would like to get your feedback to better understand your perspective on this.
Author
Owner

@tjbck commented on GitHub (Oct 2, 2025):

@hsuyuming would be happy to review a PR!

<!-- gh-comment-id:3362827156 --> @tjbck commented on GitHub (Oct 2, 2025): @hsuyuming would be happy to review a PR!
Author
Owner

@hsuyuming commented on GitHub (Oct 2, 2025):

sure, i can create a PR to kick off the discussion

<!-- gh-comment-id:3363360961 --> @hsuyuming commented on GitHub (Oct 2, 2025): sure, i can create a PR to kick off the discussion
Author
Owner

@schveiguy commented on GitHub (Oct 4, 2025):

I ran into this same issue!

The two things I had to do to fix it were:

  1. Add the code_challenge_method to the kwargs (slightly different than your recommendation, I didn't know you could do that):
@@ -337,9 +342,10 @@ class OAuthClientManager:
                 client_id=oauth_client_info.client_id,
                 client_secret=oauth_client_info.client_secret,
                 client_kwargs=(
-                    {"scope": oauth_client_info.scope}
+                    {"scope": oauth_client_info.scope,
+                     "code_challenge_method" : "S256"}
                     if oauth_client_info.scope
-                    else {}
+                    else {"code_challenge_method" : "S256"}
                 ),
                 server_metadata_url=(
                     oauth_client_info.issuer if oauth_client_info.issuer else None
  1. Include client_id and client_secret when requesting the token (these were required by my MCP server, which is based on fastmcp2, at least the client_id seems mandatory per the RFC):
@@ -560,7 +569,7 @@ class OAuthClientManager:

         error_message = None
         try:
-            token = await client.authorize_access_token(request)
+            token = await client.authorize_access_token(request, client_id=client.client_id, client_secret = client.client_secret)
             if token:
                 try:
                     # Add timestamp for tracking

With these changes, my oauth2 worked. I see your changes are similar, and got to roughly the same spot.

<!-- gh-comment-id:3367740286 --> @schveiguy commented on GitHub (Oct 4, 2025): I ran into this same issue! The two things I had to do to fix it were: 1. Add the code_challenge_method to the kwargs (slightly different than your recommendation, I didn't know you could do that): ```diff @@ -337,9 +342,10 @@ class OAuthClientManager: client_id=oauth_client_info.client_id, client_secret=oauth_client_info.client_secret, client_kwargs=( - {"scope": oauth_client_info.scope} + {"scope": oauth_client_info.scope, + "code_challenge_method" : "S256"} if oauth_client_info.scope - else {} + else {"code_challenge_method" : "S256"} ), server_metadata_url=( oauth_client_info.issuer if oauth_client_info.issuer else None ``` 2. Include `client_id` and `client_secret` when requesting the token (these were required by my MCP server, which is based on fastmcp2, at least the client_id seems [mandatory per the RFC](https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.3)): ```diff @@ -560,7 +569,7 @@ class OAuthClientManager: error_message = None try: - token = await client.authorize_access_token(request) + token = await client.authorize_access_token(request, client_id=client.client_id, client_secret = client.client_secret) if token: try: # Add timestamp for tracking ``` With these changes, my oauth2 worked. I see your changes are similar, and got to roughly the same spot.
Author
Owner

@schveiguy commented on GitHub (Oct 6, 2025):

@hsuyuming if you don't mind, I might pick up the PR submission, as I am kind of blocked without this fix.

<!-- gh-comment-id:3372376009 --> @schveiguy commented on GitHub (Oct 6, 2025): @hsuyuming if you don't mind, I might pick up the PR submission, as I am kind of blocked without this fix.
Author
Owner

@hsuyuming commented on GitHub (Oct 6, 2025):

@schveiguy I'm creating a PR for this.

<!-- gh-comment-id:3372500104 --> @hsuyuming commented on GitHub (Oct 6, 2025): @schveiguy I'm creating a PR for this.
Author
Owner

@schveiguy commented on GitHub (Oct 6, 2025):

Great, thanks. I look forward to it getting fixed!

<!-- gh-comment-id:3372508451 --> @schveiguy commented on GitHub (Oct 6, 2025): Great, thanks. I look forward to it getting fixed!
Author
Owner

@hsuyuming commented on GitHub (Oct 6, 2025):

PR: https://github.com/open-webui/open-webui/pull/18087

<!-- gh-comment-id:3373048237 --> @hsuyuming commented on GitHub (Oct 6, 2025): PR: https://github.com/open-webui/open-webui/pull/18087
Author
Owner

@taylorwilsdon commented on GitHub (Oct 7, 2025):

Ran into the same today with a new fastmcp native oauth provider setup, thanks @hsuyuming!

<!-- gh-comment-id:3377581861 --> @taylorwilsdon commented on GitHub (Oct 7, 2025): Ran into the same today with a new fastmcp native oauth provider setup, thanks @hsuyuming!
Author
Owner

@hsuyuming commented on GitHub (Oct 8, 2025):

Hi @tjbck :
I tested the v0.6.33 version from main branch and was able to complete the OAuth flow successfully. However, I noticed that the resource parameter is still missing from both the authorize and token endpoints, which is required by the MCP specification. Is there a plan to add this?

https://modelcontextprotocol.io/specification/draft/basic/authorization#resource-parameter-implementation

This is the the value of current authorize url within v0.6.33

https://<apigee>/abehsu-mcp-test/authorize?response_type=code&client_id=<client_id>
&redirect_uri=http%3A%2F%2Flocalhost%3A3000%2Foauth%2Fclients%2Fmcp%3Aabehsu%2Fcallback
&scope=<scope>&state=<state>&code_challenge=<code_challenge>&code_challenge_method=S256
<!-- gh-comment-id:3382587960 --> @hsuyuming commented on GitHub (Oct 8, 2025): Hi @tjbck : I tested the v0.6.33 version from main branch and was able to complete the OAuth flow successfully. However, I noticed that the resource parameter is still missing from both the authorize and token endpoints, which is required by the MCP specification. Is there a plan to add this? https://modelcontextprotocol.io/specification/draft/basic/authorization#resource-parameter-implementation This is the the value of current authorize url within v0.6.33 ``` https://<apigee>/abehsu-mcp-test/authorize?response_type=code&client_id=<client_id> &redirect_uri=http%3A%2F%2Flocalhost%3A3000%2Foauth%2Fclients%2Fmcp%3Aabehsu%2Fcallback &scope=<scope>&state=<state>&code_challenge=<code_challenge>&code_challenge_method=S256 ```
Author
Owner

@mjp0 commented on GitHub (Oct 11, 2025):

For me the issue with MCP oAuth flow is that once I've authorized, redirect url tries to go back to localhost:3000 instead of WEBUI_URL.

Am I missing some env that I need to set for the redirect url?

<!-- gh-comment-id:3393061230 --> @mjp0 commented on GitHub (Oct 11, 2025): For me the issue with MCP oAuth flow is that once I've authorized, redirect url tries to go back to localhost:3000 instead of WEBUI_URL. Am I missing some env that I need to set for the redirect url?
Author
Owner

@taylorwilsdon commented on GitHub (Oct 12, 2025):

For me the issue with MCP oAuth flow is that once I've authorized, redirect url tries to go back to localhost:3000 instead of WEBUI_URL.

Am I missing some env that I need to set for the redirect url?

It's actually the base URL for Open WebUI in the general admin settings tab that dictates where the callback goes iirc

<!-- gh-comment-id:3394434537 --> @taylorwilsdon commented on GitHub (Oct 12, 2025): > For me the issue with MCP oAuth flow is that once I've authorized, redirect url tries to go back to localhost:3000 instead of WEBUI_URL. > > Am I missing some env that I need to set for the redirect url? It's actually the base URL for Open WebUI in the general admin settings tab that dictates where the callback goes iirc
Author
Owner

@schveiguy commented on GitHub (Oct 12, 2025):

For me the issue with MCP oAuth flow is that once I've authorized, redirect url tries to go back to localhost:3000 instead of WEBUI_URL.

Am I missing some env that I need to set for the redirect url?

@taylorwilsdon is right that the redirect url is dictated by the settings tab. But that is also set by setting WEBUI_URL, so not sure what is happening on your end. Double check and make sure the setting is being taken.

If that is correct, I suggest to open the dev tools of your browser, and watch the requests, see what the requested redirect url is. Things go by quickly, so you have to tick the box that keeps the dev tools open, and keeps the request history on refreshes. Watching what is sending what at which stage was very helpful to me.

In my case, my MCP server is authenticating against azure, so there are 2 callbacks. The first callback is to the MCP server, which stores the azure token, and then the second callback is to the OWUI server to store the MCP server token.

<!-- gh-comment-id:3395027056 --> @schveiguy commented on GitHub (Oct 12, 2025): > For me the issue with MCP oAuth flow is that once I've authorized, redirect url tries to go back to localhost:3000 instead of WEBUI_URL. > > Am I missing some env that I need to set for the redirect url? @taylorwilsdon is right that the redirect url is dictated by the settings tab. But that is also set by setting `WEBUI_URL`, so not sure what is happening on your end. Double check and make sure the setting is being taken. If that is correct, I suggest to open the dev tools of your browser, and watch the requests, see what the requested redirect url is. Things go by quickly, so you have to tick the box that keeps the dev tools open, and keeps the request history on refreshes. Watching what is sending what at which stage was very helpful to me. In my case, my MCP server is authenticating against azure, so there are 2 callbacks. The first callback is to the MCP server, which stores the azure token, and then the second callback is to the OWUI server to store the MCP server token.
Author
Owner

@mjp0 commented on GitHub (Oct 15, 2025):

For me the issue with MCP oAuth flow is that once I've authorized, redirect url tries to go back to localhost:3000 instead of WEBUI_URL.
Am I missing some env that I need to set for the redirect url?

@taylorwilsdon is right that the redirect url is dictated by the settings tab. But that is also set by setting WEBUI_URL, so not sure what is happening on your end. Double check and make sure the setting is being taken.

If that is correct, I suggest to open the dev tools of your browser, and watch the requests, see what the requested redirect url is. Things go by quickly, so you have to tick the box that keeps the dev tools open, and keeps the request history on refreshes. Watching what is sending what at which stage was very helpful to me.

In my case, my MCP server is authenticating against azure, so there are 2 callbacks. The first callback is to the MCP server, which stores the azure token, and then the second callback is to the OWUI server to store the MCP server token.

I made a bit of progress on this but still no cigar.

Now the redirect URL is being set right but once it hits back to my server, I get oAuth error.

I get a request like https://domain.com/oauth/clients/mcp:x7896yChLKO4RKcQTdkdDs04jIA_uPZup_Htc0uF043/callback?code=Hqr26iNlnxUlSicbvXAtPXsSDTdXLbyIrLg8AxFeI12&state=BPzf74et2PX3GS9nJtfPH6ZoN1lJ51 which gets 307 Temporary Redirect to https://domain.com/?error=OAuth%20callback%20error. The only error I see is OAuth callback error which doesn't tell me anything, so I'm having a bit hard time debugging this.

Any ideas?

<!-- gh-comment-id:3405106756 --> @mjp0 commented on GitHub (Oct 15, 2025): > > For me the issue with MCP oAuth flow is that once I've authorized, redirect url tries to go back to localhost:3000 instead of WEBUI_URL. > > Am I missing some env that I need to set for the redirect url? > > [@taylorwilsdon](https://github.com/taylorwilsdon) is right that the redirect url is dictated by the settings tab. But that is also set by setting `WEBUI_URL`, so not sure what is happening on your end. Double check and make sure the setting is being taken. > > If that is correct, I suggest to open the dev tools of your browser, and watch the requests, see what the requested redirect url is. Things go by quickly, so you have to tick the box that keeps the dev tools open, and keeps the request history on refreshes. Watching what is sending what at which stage was very helpful to me. > > In my case, my MCP server is authenticating against azure, so there are 2 callbacks. The first callback is to the MCP server, which stores the azure token, and then the second callback is to the OWUI server to store the MCP server token. I made a bit of progress on this but still no cigar. Now the redirect URL is being set right but once it hits back to my server, I get oAuth error. I get a request like `https://domain.com/oauth/clients/mcp:x7896yChLKO4RKcQTdkdDs04jIA_uPZup_Htc0uF043/callback?code=Hqr26iNlnxUlSicbvXAtPXsSDTdXLbyIrLg8AxFeI12&state=BPzf74et2PX3GS9nJtfPH6ZoN1lJ51` which gets `307 Temporary Redirect` to `https://domain.com/?error=OAuth%20callback%20error`. The only error I see is `OAuth callback error` which doesn't tell me anything, so I'm having a bit hard time debugging this. Any ideas?
Author
Owner

@schveiguy commented on GitHub (Oct 15, 2025):

Check the log and network console. This is OWUI rejecting the callback for some reason.

<!-- gh-comment-id:3406214973 --> @schveiguy commented on GitHub (Oct 15, 2025): Check the log and network console. This is OWUI rejecting the callback for some reason.
Author
Owner

@hsuyuming commented on GitHub (Oct 15, 2025):

Hi @tjbck:

Maybe you miss this question or should I ask this within discussion?

I tested the v0.6.33 version from main branch and was able to complete the OAuth flow successfully. However, I noticed that the resource parameter is still missing from both the authorize and token endpoints, which is required by the MCP specification. Is there a plan to add this?

https://modelcontextprotocol.io/specification/draft/basic/authorization#resource-parameter-implementation

This is the the value of current authorize url within v0.6.33

https://<apigee>/abehsu-mcp-test/authorize?response_type=code&client_id=<client_id>
&redirect_uri=http%3A%2F%2Flocalhost%3A3000%2Foauth%2Fclients%2Fmcp%3Aabehsu%2Fcallback
&scope=<scope>&state=<state>&code_challenge=<code_challenge>&code_challenge_method=S256
<!-- gh-comment-id:3407320099 --> @hsuyuming commented on GitHub (Oct 15, 2025): Hi @tjbck: Maybe you miss this question or should I ask this within discussion? I tested the v0.6.33 version from main branch and was able to complete the OAuth flow successfully. However, I noticed that the resource parameter is still missing from both the authorize and token endpoints, which is required by the MCP specification. Is there a plan to add this? https://modelcontextprotocol.io/specification/draft/basic/authorization#resource-parameter-implementation This is the the value of current authorize url within v0.6.33 ``` https://<apigee>/abehsu-mcp-test/authorize?response_type=code&client_id=<client_id> &redirect_uri=http%3A%2F%2Flocalhost%3A3000%2Foauth%2Fclients%2Fmcp%3Aabehsu%2Fcallback &scope=<scope>&state=<state>&code_challenge=<code_challenge>&code_challenge_method=S256 ```
Author
Owner

@tjbck commented on GitHub (Oct 15, 2025):

PR welcome here!

<!-- gh-comment-id:3407579297 --> @tjbck commented on GitHub (Oct 15, 2025): PR welcome here!
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/open-webui#57130