-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Expand file tree
/
Copy pathOAuthCallback.tsx
More file actions
94 lines (79 loc) · 2.83 KB
/
OAuthCallback.tsx
File metadata and controls
94 lines (79 loc) · 2.83 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
import { useEffect, useRef } from "react";
import { InspectorOAuthClientProvider } from "../lib/auth";
import { SESSION_KEYS } from "../lib/constants";
import { auth } from "@modelcontextprotocol/sdk/client/auth.js";
import { useToast } from "@/lib/hooks/useToast";
import {
generateOAuthErrorDescription,
parseOAuthCallbackParams,
} from "@/utils/oauthUtils.ts";
interface OAuthCallbackProps {
onConnect: (serverUrl: string) => void;
}
const OAUTH_STATE_SESSION_KEY = "oauth_state";
const OAuthCallback = ({ onConnect }: OAuthCallbackProps) => {
const { toast } = useToast();
const hasProcessedRef = useRef(false);
useEffect(() => {
const handleCallback = async () => {
// Skip if we've already processed this callback
if (hasProcessedRef.current) {
return;
}
hasProcessedRef.current = true;
const notifyError = (description: string) =>
void toast({
title: "OAuth Authorization Error",
description,
variant: "destructive",
});
const params = parseOAuthCallbackParams(window.location.search);
if (!params.successful) {
return notifyError(generateOAuthErrorDescription(params));
}
const callbackState = new URLSearchParams(window.location.search).get("state");
const storedState = sessionStorage.getItem(OAUTH_STATE_SESSION_KEY);
if (!callbackState || !storedState || callbackState !== storedState) {
return notifyError("Invalid OAuth state");
}
sessionStorage.removeItem(OAUTH_STATE_SESSION_KEY);
const serverUrl = sessionStorage.getItem(SESSION_KEYS.SERVER_URL);
if (!serverUrl) {
return notifyError("Missing Server URL");
}
let result;
try {
// Create an auth provider with the current server URL
const serverAuthProvider = new InspectorOAuthClientProvider(serverUrl);
result = await auth(serverAuthProvider, {
serverUrl,
authorizationCode: params.code,
});
} catch (error) {
console.error("OAuth callback error:", error);
return notifyError(`Unexpected error occurred: ${error}`);
}
if (result !== "AUTHORIZED") {
return notifyError(
`Expected to be authorized after providing auth code, got: ${result}`,
);
}
// Finally, trigger auto-connect
toast({
title: "Success",
description: "Successfully authenticated with OAuth",
variant: "default",
});
onConnect(serverUrl);
};
handleCallback().finally(() => {
window.history.replaceState({}, document.title, "/");
});
}, [toast, onConnect]);
return (
<div className="flex items-center justify-center h-screen">
<p className="text-lg text-gray-500">Processing OAuth callback...</p>
</div>
);
};
export default OAuthCallback;