Skip to content
Merged
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
20 changes: 1 addition & 19 deletions ui/litellm-dashboard/src/hooks/useMcpOAuthFlow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
serverRootPath,
} from "@/components/networking";
import { extractErrorMessage } from "@/utils/errorUtils";
import { generateCodeChallenge, generateCodeVerifier } from "@/utils/pkce";
import { getSecureItem, setSecureItem } from "@/utils/secureStorage";

export type McpOAuthStatus = "idle" | "authorizing" | "exchanging" | "success" | "error";
Expand All @@ -34,25 +35,6 @@ interface UseMcpOAuthFlowResult {
tokenResponse: Record<string, any> | null;
}

const base64UrlEncode = (buffer: ArrayBuffer) => {
const bytes = new Uint8Array(buffer);
let binary = "";
bytes.forEach((b) => (binary += String.fromCharCode(b)));
return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
};

const generateCodeVerifier = () => {
const array = new Uint8Array(32);
window.crypto.getRandomValues(array);
return base64UrlEncode(array.buffer);
};

const generateCodeChallenge = async (verifier: string) => {
const data = new TextEncoder().encode(verifier);
const digest = await window.crypto.subtle.digest("SHA-256", data);
return base64UrlEncode(digest);
};

export const useMcpOAuthFlow = ({
accessToken,
getCredentials,
Expand Down
24 changes: 3 additions & 21 deletions ui/litellm-dashboard/src/hooks/useUserMcpOAuthFlow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
} from "@/components/networking";
import NotificationsManager from "@/components/molecules/notifications_manager";
import { extractErrorMessage } from "@/utils/errorUtils";
import { generateCodeChallenge, generateCodeVerifier } from "@/utils/pkce";
import { getSecureItem, setSecureItem } from "@/utils/secureStorage";

export type UserMcpOAuthStatus = "idle" | "authorizing" | "exchanging" | "success" | "error";
Expand Down Expand Up @@ -60,25 +61,6 @@ type StoredFlowState = {
scopes?: string[];
};

const b64url = (buf: ArrayBuffer) => {
const bytes = new Uint8Array(buf);
let s = "";
bytes.forEach((b) => (s += String.fromCharCode(b)));
return btoa(s).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
};

const genVerifier = () => {
const arr = new Uint8Array(32);
window.crypto.getRandomValues(arr);
return b64url(arr.buffer);
};

const genChallenge = async (verifier: string) => {
const data = new TextEncoder().encode(verifier);
const digest = await window.crypto.subtle.digest("SHA-256", data);
return b64url(digest);
};

const setStorage = (key: string, value: string) => {
setSecureItem(key, value);
};
Expand Down Expand Up @@ -144,8 +126,8 @@ export const useUserMcpOAuthFlow = ({
}
}

const verifier = genVerifier();
const challenge = await genChallenge(verifier);
const verifier = generateCodeVerifier();
const challenge = await generateCodeChallenge(verifier);
const state = crypto.randomUUID();
const redirectUri = buildCallbackUrl();
const scopeString = scopes?.filter((s) => s.trim()).join(" ");
Expand Down
18 changes: 18 additions & 0 deletions ui/litellm-dashboard/src/utils/pkce.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
const base64UrlEncode = (buffer: ArrayBuffer) => {
const bytes = new Uint8Array(buffer);
let binary = "";
bytes.forEach((b) => (binary += String.fromCharCode(b)));
return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
};

export const generateCodeVerifier = () => {
const array = new Uint8Array(32);
window.crypto.getRandomValues(array);
return base64UrlEncode(array.buffer);
};

export const generateCodeChallenge = async (verifier: string) => {
const data = new TextEncoder().encode(verifier);
const digest = await window.crypto.subtle.digest("SHA-256", data);
return base64UrlEncode(digest);
};
Loading