mirror of
https://github.com/samanhappy/mcphub.git
synced 2025-12-24 02:39:19 -05:00
Add OAuth 2.0 authorization server to enable ChatGPT Web integration (#413)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: samanhappy <2755122+samanhappy@users.noreply.github.com> Co-authored-by: samanhappy <samanhappy@gmail.com>
This commit is contained in:
@@ -1,11 +1,34 @@
|
||||
import React, { useState } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import React, { useState, useMemo, useCallback } from 'react';
|
||||
import { useLocation, useNavigate } from 'react-router-dom';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useAuth } from '../contexts/AuthContext';
|
||||
import { getToken } from '../services/authService';
|
||||
import ThemeSwitch from '@/components/ui/ThemeSwitch';
|
||||
import LanguageSwitch from '@/components/ui/LanguageSwitch';
|
||||
import DefaultPasswordWarningModal from '@/components/ui/DefaultPasswordWarningModal';
|
||||
|
||||
const sanitizeReturnUrl = (value: string | null): string | null => {
|
||||
if (!value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
// Support both relative paths and absolute URLs on the same origin
|
||||
const origin = typeof window !== 'undefined' ? window.location.origin : 'http://localhost';
|
||||
const url = new URL(value, origin);
|
||||
if (url.origin !== origin) {
|
||||
return null;
|
||||
}
|
||||
const relativePath = `${url.pathname}${url.search}${url.hash}`;
|
||||
return relativePath || '/';
|
||||
} catch {
|
||||
if (value.startsWith('/') && !value.startsWith('//')) {
|
||||
return value;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
const LoginPage: React.FC = () => {
|
||||
const { t } = useTranslation();
|
||||
const [username, setUsername] = useState('');
|
||||
@@ -14,7 +37,46 @@ const LoginPage: React.FC = () => {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [showDefaultPasswordWarning, setShowDefaultPasswordWarning] = useState(false);
|
||||
const { login } = useAuth();
|
||||
const location = useLocation();
|
||||
const navigate = useNavigate();
|
||||
const returnUrl = useMemo(() => {
|
||||
const params = new URLSearchParams(location.search);
|
||||
return sanitizeReturnUrl(params.get('returnUrl'));
|
||||
}, [location.search]);
|
||||
|
||||
const buildRedirectTarget = useCallback(() => {
|
||||
if (!returnUrl) {
|
||||
return '/';
|
||||
}
|
||||
|
||||
// Only attach JWT when returning to the OAuth authorize endpoint
|
||||
if (!returnUrl.startsWith('/oauth/authorize')) {
|
||||
return returnUrl;
|
||||
}
|
||||
|
||||
const token = getToken();
|
||||
if (!token) {
|
||||
return returnUrl;
|
||||
}
|
||||
|
||||
try {
|
||||
const origin = window.location.origin;
|
||||
const url = new URL(returnUrl, origin);
|
||||
url.searchParams.set('token', token);
|
||||
return `${url.pathname}${url.search}${url.hash}`;
|
||||
} catch {
|
||||
const separator = returnUrl.includes('?') ? '&' : '?';
|
||||
return `${returnUrl}${separator}token=${encodeURIComponent(token)}`;
|
||||
}
|
||||
}, [returnUrl]);
|
||||
|
||||
const redirectAfterLogin = useCallback(() => {
|
||||
if (returnUrl) {
|
||||
window.location.assign(buildRedirectTarget());
|
||||
} else {
|
||||
navigate('/');
|
||||
}
|
||||
}, [buildRedirectTarget, navigate, returnUrl]);
|
||||
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
@@ -35,7 +97,7 @@ const LoginPage: React.FC = () => {
|
||||
// Show warning modal instead of navigating immediately
|
||||
setShowDefaultPasswordWarning(true);
|
||||
} else {
|
||||
navigate('/');
|
||||
redirectAfterLogin();
|
||||
}
|
||||
} else {
|
||||
setError(t('auth.loginFailed'));
|
||||
@@ -49,7 +111,7 @@ const LoginPage: React.FC = () => {
|
||||
|
||||
const handleCloseWarning = () => {
|
||||
setShowDefaultPasswordWarning(false);
|
||||
navigate('/');
|
||||
redirectAfterLogin();
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -160,4 +222,4 @@ const LoginPage: React.FC = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export default LoginPage;
|
||||
export default LoginPage;
|
||||
|
||||
Reference in New Issue
Block a user