Update README and OAuth response handling for improved user flow

This commit is contained in:
2026-10-29 00:20:31 -04:00
parent 7e9f488a5f
commit 8fd73e0d8e
2 changed files with 19 additions and 3 deletions
+1 -1
View File
@@ -81,7 +81,7 @@ Use a dedicated public hostname such as `mcp.ryankegel.com` and terminate HTTPS
The MCP endpoint is `https://mcp.ryankegel.com/mcp`. The OAuth discovery and login endpoints share that hostname. The MCP allowed-host setting also prevents requests to unexpected hostnames. The MCP endpoint is `https://mcp.ryankegel.com/mcp`. The OAuth discovery and login endpoints share that hostname. The MCP allowed-host setting also prevents requests to unexpected hostnames.
5. In ChatGPT on the web, add a custom MCP server with that `/mcp` URL and choose OAuth authentication. The server supports Dynamic Client Registration (DCR). On the first authorization, sign in with `MCP_AUTH_USERNAME` and the password stored in `secrets/mcp_auth_password`, then approve the requested vault access. 5. In ChatGPT on the web, add a custom MCP server with that `/mcp` URL and choose OAuth authentication. The server supports Dynamic Client Registration (DCR). On the first authorization, sign in with `MCP_AUTH_USERNAME` and the password stored in `secrets/mcp_auth_password`, approve the requested vault access, then click **Continue** to return to ChatGPT.
## Enable writes ## Enable writes
+18 -2
View File
@@ -27,7 +27,7 @@ from mcp.server.auth.provider import (
from mcp.shared.auth import OAuthClientInformationFull, OAuthToken from mcp.shared.auth import OAuthClientInformationFull, OAuthToken
from starlette.exceptions import HTTPException from starlette.exceptions import HTTPException
from starlette.requests import Request from starlette.requests import Request
from starlette.responses import HTMLResponse, RedirectResponse, Response from starlette.responses import HTMLResponse, Response
ACCESS_TOKEN_SECONDS = 60 * 60 ACCESS_TOKEN_SECONDS = 60 * 60
@@ -543,7 +543,23 @@ def make_login_routes(provider: LocalOAuthProvider) -> list[Any]:
await provider.clear_login_failures(client_ip) await provider.clear_login_failures(client_ip)
redirect_url = await provider.finish_login(flow_id, username) redirect_url = await provider.finish_login(flow_id, username)
response = _protect_login_response(RedirectResponse(redirect_url, status_code=303)) safe_redirect_url = html.escape(redirect_url, quote=True)
success_page = f"""<!doctype html>
<html lang="en">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Authorization complete</title>
<style>
body {{ font: 16px system-ui, sans-serif; max-width: 34rem; margin: 4rem auto; padding: 0 1rem; color: #17202a; }}
a {{ display: inline-block; padding: .7rem 1rem; border-radius: .35rem; background: #1457c5; color: white; text-decoration: none; }}
</style>
<h1>Authorization complete</h1>
<p>Continue to return to the app that requested access.</p>
<a href="{safe_redirect_url}" rel="noreferrer">Continue</a>
</html>"""
response = _protect_login_response(
HTMLResponse(success_page)
)
response.delete_cookie(_csrf_cookie_name(flow_id), path="/", secure=True, httponly=True, samesite="strict") response.delete_cookie(_csrf_cookie_name(flow_id), path="/", secure=True, httponly=True, samesite="strict")
return response return response