Skip to content

Commit b7e29bf

Browse files
committed
feat: apply McpMux branding to OAuth authorization pages
Replace generic cyan/teal colors and emoji icon on the browser-facing authorization request page with the McpMux brand logo (inline SVG) and orange-rust gradient (#DA7756 → #B8553A). Redesign the authorization complete page from a plain light-theme stub to a fully branded dark page matching the authorization request page, with the McpMux logo and a green checkmark indicator. Signed-off-by: Claude <noreply@anthropic.com> https://claude.ai/code/session_01Du7pRJtNqgW8SU2yitXEDM Signed-off-by: Claude <noreply@anthropic.com>
1 parent 96795b0 commit b7e29bf

2 files changed

Lines changed: 100 additions & 44 deletions

File tree

crates/mcpmux-gateway/src/pool/oauth.rs

Lines changed: 75 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -533,33 +533,89 @@ impl OutboundOAuthManager {
533533
}
534534
}
535535

536-
// Return a nice HTML page that auto-closes
537-
Html(
538-
r#"
539-
<!DOCTYPE html>
540-
<html>
536+
// Return a branded HTML page that auto-closes
537+
let app_name = branding::DISPLAY_NAME;
538+
Html(format!(
539+
r##"<!DOCTYPE html>
540+
<html lang="en">
541541
<head>
542-
<title>Authorization Complete</title>
542+
<meta charset="utf-8">
543+
<meta name="viewport" content="width=device-width, initial-scale=1">
544+
<title>{app_name} - Authorization Complete</title>
543545
<style>
544-
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
545-
display: flex; justify-content: center; align-items: center;
546-
height: 100vh; margin: 0; background: #f5f5f5; }
547-
.container { text-align: center; padding: 40px; background: white;
548-
border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); }
549-
h1 { color: #333; margin-bottom: 16px; }
550-
p { color: #666; }
546+
* {{ margin: 0; padding: 0; box-sizing: border-box; }}
547+
body {{
548+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
549+
min-height: 100vh;
550+
display: flex;
551+
align-items: center;
552+
justify-content: center;
553+
background: linear-gradient(135deg, #1a1210 0%, #2a1c17 50%, #1e1412 100%);
554+
color: #e6e6e6;
555+
padding: 1rem;
556+
}}
557+
.container {{
558+
text-align: center;
559+
max-width: 400px;
560+
}}
561+
.logo {{
562+
width: 64px;
563+
height: 64px;
564+
margin: 0 auto 1.5rem;
565+
}}
566+
.check {{
567+
width: 48px;
568+
height: 48px;
569+
margin: 0 auto 1rem;
570+
background: rgba(74, 222, 128, 0.12);
571+
border-radius: 50%;
572+
display: flex;
573+
align-items: center;
574+
justify-content: center;
575+
}}
576+
.check svg {{
577+
width: 24px;
578+
height: 24px;
579+
}}
580+
h1 {{
581+
font-size: 1.5rem;
582+
font-weight: 600;
583+
margin-bottom: 0.75rem;
584+
color: #fff;
585+
}}
586+
.subtitle {{
587+
color: #a0917e;
588+
line-height: 1.5;
589+
margin-bottom: 1.5rem;
590+
}}
591+
.note {{
592+
font-size: 0.875rem;
593+
color: #7a6e62;
594+
}}
551595
</style>
552596
</head>
553597
<body>
554598
<div class="container">
555-
<h1>✓ Authorization Complete</h1>
556-
<p>You can close this window and return to McpMux.</p>
557-
<script>setTimeout(() => window.close(), 2000);</script>
599+
<svg class="logo" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
600+
<defs><linearGradient id="bg" x1="0" y1="0" x2="32" y2="32" gradientUnits="userSpaceOnUse"><stop offset="0%" stop-color="#DA7756"/><stop offset="100%" stop-color="#B8553A"/></linearGradient></defs>
601+
<rect width="32" height="32" rx="7" fill="url(#bg)"/>
602+
<circle cx="6" cy="9" r="2.8" fill="white" opacity="0.85"/><circle cx="6" cy="16" r="3" fill="white"/><circle cx="6" cy="23" r="2.8" fill="white" opacity="0.85"/>
603+
<path d="M 10 21 V 11 L 13 18 L 16 10 L 19 18 L 22 11 V 21" stroke="white" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round" fill="none"/>
604+
<circle cx="26" cy="9" r="2.8" fill="white" opacity="0.85"/><circle cx="26" cy="16" r="3" fill="white"/><circle cx="26" cy="23" r="2.8" fill="white" opacity="0.85"/>
605+
</svg>
606+
<div class="check">
607+
<svg viewBox="0 0 24 24" fill="none" stroke="#4ade80" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
608+
</div>
609+
<h1>Authorization Complete</h1>
610+
<p class="subtitle">
611+
You can close this window and return to {app_name}.
612+
</p>
613+
<p class="note">This window will close automatically.</p>
558614
</div>
615+
<script>setTimeout(function(){{ window.close(); }}, 2000);</script>
559616
</body>
560-
</html>
561-
"#,
562-
)
617+
</html>"##
618+
))
563619
},
564620
),
565621
)

crates/mcpmux-gateway/src/server/handlers.rs

Lines changed: 25 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -304,7 +304,7 @@ pub async fn oauth_authorize(
304304
// The page shows a brief message while the app opens
305305
// Industry standard: Don't auto-close, let user close after approval
306306
let html = format!(
307-
r#"<!DOCTYPE html>
307+
r##"<!DOCTYPE html>
308308
<html lang="en">
309309
<head>
310310
<meta charset="utf-8">
@@ -318,24 +318,18 @@ pub async fn oauth_authorize(
318318
display: flex;
319319
align-items: center;
320320
justify-content: center;
321-
background: linear-gradient(135deg, #0f0f23 0%, #1a1a2e 50%, #16213e 100%);
321+
background: linear-gradient(135deg, #1a1210 0%, #2a1c17 50%, #1e1412 100%);
322322
color: #e6e6e6;
323323
padding: 1rem;
324324
}}
325325
.container {{
326326
text-align: center;
327327
max-width: 400px;
328328
}}
329-
.icon {{
329+
.logo {{
330330
width: 64px;
331331
height: 64px;
332332
margin: 0 auto 1.5rem;
333-
background: linear-gradient(135deg, #64ffda 0%, #00bcd4 100%);
334-
border-radius: 16px;
335-
display: flex;
336-
align-items: center;
337-
justify-content: center;
338-
font-size: 2rem;
339333
}}
340334
h1 {{
341335
font-size: 1.5rem;
@@ -344,34 +338,34 @@ pub async fn oauth_authorize(
344338
color: #fff;
345339
}}
346340
.subtitle {{
347-
color: #8892b0;
341+
color: #a0917e;
348342
margin-bottom: 2rem;
349343
line-height: 1.5;
350344
}}
351345
.client-info {{
352-
background: rgba(255,255,255,0.05);
353-
border: 1px solid rgba(255,255,255,0.1);
346+
background: rgba(218,119,86,0.06);
347+
border: 1px solid rgba(218,119,86,0.15);
354348
border-radius: 12px;
355349
padding: 1rem;
356350
margin-bottom: 1.5rem;
357351
}}
358352
.client-name {{
359353
font-weight: 500;
360-
color: #64ffda;
354+
color: #DA7756;
361355
margin-bottom: 0.25rem;
362356
}}
363357
.client-id {{
364358
font-size: 0.75rem;
365-
color: #6a7394;
359+
color: #7a6e62;
366360
word-break: break-all;
367361
}}
368362
.action {{
369363
margin-top: 1.5rem;
370364
}}
371365
.btn {{
372366
display: inline-block;
373-
background: linear-gradient(135deg, #64ffda 0%, #00bcd4 100%);
374-
color: #0f0f23;
367+
background: linear-gradient(135deg, #DA7756 0%, #B8553A 100%);
368+
color: #fff;
375369
padding: 0.75rem 2rem;
376370
border-radius: 8px;
377371
text-decoration: none;
@@ -383,12 +377,12 @@ pub async fn oauth_authorize(
383377
}}
384378
.btn:hover {{
385379
transform: translateY(-2px);
386-
box-shadow: 0 4px 20px rgba(100, 255, 218, 0.3);
380+
box-shadow: 0 4px 20px rgba(218, 119, 86, 0.35);
387381
}}
388382
.btn-secondary {{
389383
background: transparent;
390384
border: 1px solid rgba(255,255,255,0.2);
391-
color: #8892b0;
385+
color: #a0917e;
392386
margin-top: 1rem;
393387
}}
394388
.btn-secondary:hover {{
@@ -400,28 +394,34 @@ pub async fn oauth_authorize(
400394
.note {{
401395
margin-top: 2rem;
402396
font-size: 0.875rem;
403-
color: #6a7394;
397+
color: #7a6e62;
404398
}}
405399
</style>
406400
</head>
407401
<body>
408402
<div class="container">
409-
<div class="icon">🔐</div>
403+
<svg class="logo" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
404+
<defs><linearGradient id="bg" x1="0" y1="0" x2="32" y2="32" gradientUnits="userSpaceOnUse"><stop offset="0%" stop-color="#DA7756"/><stop offset="100%" stop-color="#B8553A"/></linearGradient></defs>
405+
<rect width="32" height="32" rx="7" fill="url(#bg)"/>
406+
<circle cx="6" cy="9" r="2.8" fill="white" opacity="0.85"/><circle cx="6" cy="16" r="3" fill="white"/><circle cx="6" cy="23" r="2.8" fill="white" opacity="0.85"/>
407+
<path d="M 10 21 V 11 L 13 18 L 16 10 L 19 18 L 22 11 V 21" stroke="white" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round" fill="none"/>
408+
<circle cx="26" cy="9" r="2.8" fill="white" opacity="0.85"/><circle cx="26" cy="16" r="3" fill="white"/><circle cx="26" cy="23" r="2.8" fill="white" opacity="0.85"/>
409+
</svg>
410410
<h1>Authorization Request</h1>
411411
<p class="subtitle">
412412
Complete authorization in {app_name}
413413
</p>
414-
414+
415415
<div class="client-info">
416416
<div class="client-name">{display_name}</div>
417417
<div class="client-id">wants to connect</div>
418418
</div>
419-
419+
420420
<div class="action">
421421
<a href="{deep_link_url}" class="btn">Open {app_name}</a>
422422
<button class="btn btn-secondary" onclick="window.close()">Close this tab</button>
423423
</div>
424-
424+
425425
<p class="note">
426426
If prompted by your browser, click "Open" to allow.
427427
</p>
@@ -434,7 +434,7 @@ pub async fn oauth_authorize(
434434
iframe.style.display = 'none';
435435
iframe.src = "{deep_link_url}";
436436
document.body.appendChild(iframe);
437-
437+
438438
// Fallback: remove iframe after a short delay
439439
// The protocol handler should have fired by then
440440
setTimeout(function() {{
@@ -445,7 +445,7 @@ pub async fn oauth_authorize(
445445
}})();
446446
</script>
447447
</body>
448-
</html>"#
448+
</html>"##
449449
);
450450

451451
axum::response::Html(html).into_response()

0 commit comments

Comments
 (0)