Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions hermes_cli/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -4149,6 +4149,19 @@ def _minimax_pkce_pair() -> tuple:
return verifier, challenge, state


def _normalize_minimax_verification_uri(uri: str) -> str:
"""Rewrite stale MiniMax approval URLs while preserving query parameters."""
verification_uri = str(uri)
parsed = urlparse(verification_uri)
if (
parsed.scheme in {"http", "https"}
and parsed.netloc.lower() == "www.minimax.io"
and parsed.path.rstrip("/") == "/oauth-authorize"
):
return parsed._replace(netloc="platform.minimax.io").geturl()
return verification_uri


def _minimax_request_user_code(
client: httpx.Client, *, portal_base_url: str, client_id: str,
code_challenge: str, state: str,
Expand Down Expand Up @@ -4186,6 +4199,9 @@ def _minimax_request_user_code(
"MiniMax OAuth state mismatch (possible CSRF).",
provider="minimax-oauth", code="state_mismatch",
)
payload["verification_uri"] = _normalize_minimax_verification_uri(
str(payload["verification_uri"])
)
return payload


Expand Down
43 changes: 43 additions & 0 deletions tests/test_minimax_oauth.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
MINIMAX_OAUTH_REFRESH_SKEW_SECONDS,
_minimax_pkce_pair,
_minimax_request_user_code,
_normalize_minimax_verification_uri,
_minimax_poll_token,
_refresh_minimax_oauth_state,
resolve_minimax_oauth_runtime_credentials,
Expand Down Expand Up @@ -133,6 +134,48 @@ def test_request_user_code_happy_path():
assert "x-request-id" in headers


def test_request_user_code_rewrites_stale_minimax_authorize_host():
state = "test-state-abc"
mock_response = _make_httpx_response(200, {
"user_code": "ABC-123",
"verification_uri": (
"https://www.minimax.io/oauth-authorize"
"?user_code=ABC-123&client=OpenClaw"
),
"expired_in": int(time.time() * 1000) + 300_000,
"state": state,
})

client = MagicMock()
client.post.return_value = mock_response

result = _minimax_request_user_code(
client,
portal_base_url=MINIMAX_OAUTH_GLOBAL_BASE,
client_id=MINIMAX_OAUTH_CLIENT_ID,
code_challenge="test-challenge",
state=state,
)

assert result["verification_uri"] == (
"https://platform.minimax.io/oauth-authorize"
"?user_code=ABC-123&client=OpenClaw"
)


def test_normalize_minimax_verification_uri_preserves_unrelated_urls():
assert (
_normalize_minimax_verification_uri("https://minimax.io/verify")
== "https://minimax.io/verify"
)
assert (
_normalize_minimax_verification_uri(
"https://www.minimax.io/account?user_code=ABC-123"
)
== "https://www.minimax.io/account?user_code=ABC-123"
)


# ---------------------------------------------------------------------------
# 3. test_request_user_code_state_mismatch_raises
# ---------------------------------------------------------------------------
Expand Down
Loading