diff --git a/src/handlers.ts b/src/handlers.ts index 1e7fc06..63a6d6e 100644 --- a/src/handlers.ts +++ b/src/handlers.ts @@ -53,6 +53,18 @@ app.post("/authorize", async (c) => { // redirect URL as query params = new URL("/callback", c.req.url).href to // send the user back to callback endpoint. // The callback endpoint will get the encrypted token and decrypt it to get the user's access token. + + // const targetURLAuthorize = new URL("callosum/v1/v2/auth/token/authorize", instanceUrl); + // targetURLAuthorize.searchParams.append('validity_time_in_sec', "86400"); + // const targetURLCallbackPath = new URL("/callback", c.req.url); + // targetURLCallbackPath.searchParams.append('instanceUrl', instanceUrl); + // targetURLAuthorize.searchParams.append('redirect_url', btoa(targetURLCallbackPath.toString())); + // const encodedState = btoa(JSON.stringify(state.oauthReqInfo)); + // targetURLAuthorize.searchParams.append('state', encodedState); + // targetURLAuthorize.searchParams.append('token_encryption_key', "1234567812345678"); + // targetURLAuthorize.searchParams.append('encryption_algorithm', 'AES'); + // redirectUrl.searchParams.append('targetURLPath', targetURLAuthorize.href); + const targetURLPath = new URL("/callback", c.req.url); targetURLPath.searchParams.append('instanceUrl', instanceUrl); const encodedState = btoa(JSON.stringify(state.oauthReqInfo)); @@ -64,6 +76,13 @@ app.post("/authorize", async (c) => { }) app.get("/callback", async (c) => { + + // TODO(shikhar.bhargava): remove this once we have a proper callback URL + // With the proper callback URL, we will get the encrypted token in the query params + // along with it we will get the instanceUrl and the state (oauthReqInfo). + // and we will decrypt the token to get the user's access token and complete the authorization. + // const encodedOauthReqInfo = c.req.query('state'); + const instanceUrl = c.req.query('instanceUrl'); const encodedOauthReqInfo = c.req.query('oauthReqInfo'); if (!instanceUrl) { diff --git a/src/oauth-manager/oauth-utils.ts b/src/oauth-manager/oauth-utils.ts index b1200a5..b4af248 100644 --- a/src/oauth-manager/oauth-utils.ts +++ b/src/oauth-manager/oauth-utils.ts @@ -451,7 +451,7 @@ export function renderApprovalDialog(request: Request, options: ApprovalDialogOp
-
@@ -498,6 +498,36 @@ export interface ParsedApprovalResult { } +/** + * Validates and sanitizes a URL to ensure it's a valid ThoughtSpot instance URL + * @param url - The URL to validate and sanitize + * @returns The sanitized URL + * @throws Error if the URL is invalid + */ +function validateAndSanitizeUrl(url: string): string { + try { + // Remove any whitespace + const trimmedUrl = url.trim(); + + // Add https:// if no protocol is specified + const urlWithProtocol = trimmedUrl.startsWith('http://') || trimmedUrl.startsWith('https://') + ? trimmedUrl + : `https://${trimmedUrl}`; + + const parsedUrl = new URL(urlWithProtocol); + + // Remove trailing slashes and normalize the URL + const sanitizedUrl = parsedUrl.origin; + + return sanitizedUrl; + } catch (e) { + if (e instanceof Error) { + throw new Error(`Invalid URL: ${e.message}`); + } + throw new Error('Invalid URL format'); + } +} + /** * Parses the form submission from the approval dialog, extracts the state, * and generates Set-Cookie headers to mark the client as approved. @@ -517,7 +547,7 @@ export async function parseRedirectApproval(request: Request): Promise