magic-js icon indicating copy to clipboard operation
magic-js copied to clipboard

Double encoding of redirect_uri with the from query param

Open CambodianCoder opened this issue 2 months ago • 0 comments

Auth redirect urls from Magic appear to be double encoding the query params, leading users to encounter issues in logging in to their magic accounts.

The login with redirect began failing as the redirect no longer matched the redirect uri allow list configured in the magic settings, due to the double encoding of query params.

Versions: "magic-sdk": "^31.0.0", and "@magic-sdk/admin": "2.4.1",

The workaround listed below was sufficient to resolve the issue, but we're creating this ticket to track any internal upstream changes in the Magic code that might have impacted this.

/**
 * Detects and corrects double-encoded URLs where query parameters have been
 * incorrectly encoded in the pathname (e.g., /login%3Ffrom%3D...).
 *
 * @param pathname - The pathname that may contain encoded query params
 * @param search - The actual query string from the URL
 * @returns The corrected URL string, or null if no correction is needed
 */
function fixDoubleEncodedUrl(pathname: string, search: string): string | null {
  // Check if the URL has been double-encoded (e.g., /login%3Ffrom%3D...)
  if (!pathname.includes('%3F') && !pathname.includes('%3f')) {
    return null;
  }

  try {
    // Decode the pathname to extract the real path and query params
    const decodedPath = decodeURIComponent(pathname);

    // Split on the first ? to separate path from embedded query string
    const questionMarkIndex = decodedPath.indexOf('?');
    if (questionMarkIndex === -1) {
      return null;
    }

    const realPath = decodedPath.substring(0, questionMarkIndex);
    const embeddedQueryString = decodedPath.substring(questionMarkIndex + 1);

    // Parse embedded query params
    const embeddedParams = new URLSearchParams(embeddedQueryString);

    // Parse existing query params from the URL
    const existingParams = new URLSearchParams(search);

    // Merge both sets of params (existing params take precedence)
    const mergedParams = new URLSearchParams();
    embeddedParams.forEach((value, key) => {
      mergedParams.set(key, value);
    });
    existingParams.forEach((value, key) => {
      mergedParams.set(key, value);
    });

    // Construct the corrected URL
    const correctedSearch = mergedParams.toString();
    return correctedSearch ? `${realPath}?${correctedSearch}` : realPath;
  } catch (error) {
    console.error('Error decoding malformed URL:', error);
    return null;
  }
}

CambodianCoder avatar Oct 11 '25 15:10 CambodianCoder