shreyask commited on
Commit
dbd24f9
·
verified ·
1 Parent(s): 8a269b1

Update src/components/OAuthCallback.tsx

Browse files
Files changed (1) hide show
  1. src/components/OAuthCallback.tsx +57 -12
src/components/OAuthCallback.tsx CHANGED
@@ -1,4 +1,5 @@
1
  import { useEffect, useState } from "react";
 
2
  import { exchangeCodeForToken } from "../services/oauth";
3
  import { secureStorage } from "../utils/storage";
4
  import type { MCPServerConfig } from "../types/mcp";
@@ -24,29 +25,57 @@ const OAuthCallback: React.FC<OAuthCallbackProps> = ({
24
  onError,
25
  }) => {
26
  const [status, setStatus] = useState<string>("Authorizing...");
 
27
 
28
  useEffect(() => {
29
- const params = new URLSearchParams(window.location.search);
 
 
 
 
 
 
 
30
  const code = params.get("code");
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
31
  // Always persist MCP server URL for robustness
32
  localStorage.setItem(STORAGE_KEYS.OAUTH_MCP_SERVER_URL, serverUrl);
 
33
  if (code) {
34
  exchangeCodeForToken({
35
  serverUrl,
36
  code,
37
- redirectUri: window.location.origin + DEFAULTS.OAUTH_REDIRECT_PATH,
38
  })
39
  .then(async (tokens) => {
40
  await secureStorage.setItem(STORAGE_KEYS.OAUTH_ACCESS_TOKEN, tokens.access_token);
 
41
  // Add MCP server to MCPClientService for UI
42
  const mcpServerUrl = localStorage.getItem(STORAGE_KEYS.OAUTH_MCP_SERVER_URL);
43
  if (mcpServerUrl) {
44
- // Use persisted name and transport from initial add
45
  const serverName =
46
  localStorage.getItem(STORAGE_KEYS.MCP_SERVER_NAME) || mcpServerUrl;
47
  const serverTransport =
48
  (localStorage.getItem(STORAGE_KEYS.MCP_SERVER_TRANSPORT) as MCPServerConfig['transport']) || DEFAULTS.MCP_TRANSPORT;
49
- // Build config and add to mcp-servers
50
  const serverConfig = {
51
  id: `server_${Date.now()}`,
52
  name: serverName,
@@ -58,40 +87,56 @@ const OAuthCallback: React.FC<OAuthCallbackProps> = ({
58
  token: tokens.access_token,
59
  },
60
  };
61
- // Load existing servers
62
  let servers: MCPServerConfig[] = [];
63
  try {
64
  const stored = localStorage.getItem(STORAGE_KEYS.MCP_SERVERS);
65
  if (stored) servers = JSON.parse(stored);
66
  } catch {}
67
- // Add or update
68
  const exists = servers.some((s: MCPServerConfig) => s.url === mcpServerUrl);
69
  if (!exists) {
70
  servers.push(serverConfig);
71
  localStorage.setItem(STORAGE_KEYS.MCP_SERVERS, JSON.stringify(servers));
72
  }
73
- // Clear temp values from localStorage for clean slate
 
74
  localStorage.removeItem(STORAGE_KEYS.MCP_SERVER_NAME);
75
  localStorage.removeItem(STORAGE_KEYS.MCP_SERVER_TRANSPORT);
76
  localStorage.removeItem(STORAGE_KEYS.OAUTH_MCP_SERVER_URL);
77
  }
 
 
 
 
78
  setStatus("Authorization successful! Redirecting...");
79
  if (onSuccess) onSuccess(tokens);
80
- // Redirect to main app page after short delay
 
81
  setTimeout(() => {
82
- window.location.replace("/");
83
  }, 1000);
84
  })
85
  .catch((err) => {
86
  setStatus("OAuth token exchange failed: " + err.message);
87
  if (onError) onError(err);
 
 
88
  });
89
  } else {
90
  setStatus("Missing authorization code in callback URL.");
 
91
  }
92
- }, [serverUrl, onSuccess, onError]);
93
 
94
- return <div>{status}</div>;
 
 
 
 
 
 
 
95
  };
96
 
97
- export default OAuthCallback;
 
1
  import { useEffect, useState } from "react";
2
+ import { useNavigate } from "react-router-dom";
3
  import { exchangeCodeForToken } from "../services/oauth";
4
  import { secureStorage } from "../utils/storage";
5
  import type { MCPServerConfig } from "../types/mcp";
 
25
  onError,
26
  }) => {
27
  const [status, setStatus] = useState<string>("Authorizing...");
28
+ const navigate = useNavigate(); // Add this hook
29
 
30
  useEffect(() => {
31
+ // Parse parameters from hash instead of search
32
+ const parseHashParams = () => {
33
+ const hash = window.location.hash.slice(1); // Remove #
34
+ const [, queryString] = hash.split('?');
35
+ return new URLSearchParams(queryString || '');
36
+ };
37
+
38
+ const params = parseHashParams();
39
  const code = params.get("code");
40
+ const state = params.get("state");
41
+ const error = params.get("error");
42
+
43
+ // Verify state parameter for CSRF protection
44
+ const savedState = localStorage.getItem('oauth_state');
45
+ if (state !== savedState) {
46
+ setStatus("Invalid state parameter. Possible CSRF attack.");
47
+ if (onError) onError(new Error("Invalid state parameter"));
48
+ return;
49
+ }
50
+
51
+ // Check for OAuth errors
52
+ if (error) {
53
+ const errorDescription = params.get("error_description") || error;
54
+ setStatus(`OAuth error: ${errorDescription}`);
55
+ if (onError) onError(new Error(errorDescription));
56
+ return;
57
+ }
58
+
59
  // Always persist MCP server URL for robustness
60
  localStorage.setItem(STORAGE_KEYS.OAUTH_MCP_SERVER_URL, serverUrl);
61
+
62
  if (code) {
63
  exchangeCodeForToken({
64
  serverUrl,
65
  code,
66
+ redirectUri: window.location.origin + "/#" + DEFAULTS.OAUTH_REDIRECT_PATH, // Add hash
67
  })
68
  .then(async (tokens) => {
69
  await secureStorage.setItem(STORAGE_KEYS.OAUTH_ACCESS_TOKEN, tokens.access_token);
70
+
71
  // Add MCP server to MCPClientService for UI
72
  const mcpServerUrl = localStorage.getItem(STORAGE_KEYS.OAUTH_MCP_SERVER_URL);
73
  if (mcpServerUrl) {
 
74
  const serverName =
75
  localStorage.getItem(STORAGE_KEYS.MCP_SERVER_NAME) || mcpServerUrl;
76
  const serverTransport =
77
  (localStorage.getItem(STORAGE_KEYS.MCP_SERVER_TRANSPORT) as MCPServerConfig['transport']) || DEFAULTS.MCP_TRANSPORT;
78
+
79
  const serverConfig = {
80
  id: `server_${Date.now()}`,
81
  name: serverName,
 
87
  token: tokens.access_token,
88
  },
89
  };
90
+
91
  let servers: MCPServerConfig[] = [];
92
  try {
93
  const stored = localStorage.getItem(STORAGE_KEYS.MCP_SERVERS);
94
  if (stored) servers = JSON.parse(stored);
95
  } catch {}
96
+
97
  const exists = servers.some((s: MCPServerConfig) => s.url === mcpServerUrl);
98
  if (!exists) {
99
  servers.push(serverConfig);
100
  localStorage.setItem(STORAGE_KEYS.MCP_SERVERS, JSON.stringify(servers));
101
  }
102
+
103
+ // Clear temp values
104
  localStorage.removeItem(STORAGE_KEYS.MCP_SERVER_NAME);
105
  localStorage.removeItem(STORAGE_KEYS.MCP_SERVER_TRANSPORT);
106
  localStorage.removeItem(STORAGE_KEYS.OAUTH_MCP_SERVER_URL);
107
  }
108
+
109
+ // Clear OAuth state
110
+ localStorage.removeItem('oauth_state');
111
+
112
  setStatus("Authorization successful! Redirecting...");
113
  if (onSuccess) onSuccess(tokens);
114
+
115
+ // Use React Router navigation instead of window.location.replace
116
  setTimeout(() => {
117
+ navigate("/", { replace: true });
118
  }, 1000);
119
  })
120
  .catch((err) => {
121
  setStatus("OAuth token exchange failed: " + err.message);
122
  if (onError) onError(err);
123
+ // Clear OAuth state on error
124
+ localStorage.removeItem('oauth_state');
125
  });
126
  } else {
127
  setStatus("Missing authorization code in callback URL.");
128
+ if (onError) onError(new Error("Missing authorization code"));
129
  }
130
+ }, [serverUrl, onSuccess, onError, navigate]);
131
 
132
+ return (
133
+ <div className="flex items-center justify-center min-h-screen">
134
+ <div className="text-center">
135
+ <div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600 mx-auto mb-4"></div>
136
+ <p>{status}</p>
137
+ </div>
138
+ </div>
139
+ );
140
  };
141
 
142
+ export default OAuthCallback;