usememos/memos · error · Error

Failed to initialize OAuth flow

Error message

Failed to initialize OAuth flow

What it means

Thrown by initializeOAuthFlow when sessionStorage.setItem(STATE_STORAGE_KEY, ...) fails while persisting the CSRF state object before redirecting to the identity provider. sessionStorage can throw QuotaExceededError, SecurityError (cookies blocked in some iframe/Safari contexts), or be unavailable in private modes. Without stored state the OAuth callback cannot validate the redirect, so the flow aborts before leaving the page.

Source

Thrown at web/src/utils/oauth.ts:94

    codeVerifier = undefined;
    codeChallenge = undefined;
  }

  const stateData: OAuthState = {
    state,
    identityProviderName,
    flowMode,
    timestamp: Date.now(),
    returnUrl,
    linkingUserName,
    codeVerifier, // Store for later retrieval in callback (undefined if PKCE not available)
  };

  try {
    sessionStorage.setItem(STATE_STORAGE_KEY, JSON.stringify(stateData));
  } catch (error) {
    console.error("Failed to store OAuth state:", error);
    throw new Error("Failed to initialize OAuth flow");
  }

  return { state, codeChallenge };
}

// Validate and retrieve OAuth state from storage (CSRF protection)
// Returns identityProviderName, flowMode, returnUrl, linkingUserName, and codeVerifier for PKCE
export function validateOAuthState(
  stateParam: string,
): { identityProviderName: string; flowMode: OAuthFlowMode; returnUrl?: string; linkingUserName?: string; codeVerifier?: string } | null {
  try {
    const storedData = sessionStorage.getItem(STATE_STORAGE_KEY);
    if (!storedData) {
      console.error("No OAuth state found in storage");
      return null;
    }

    const stateData: OAuthState = JSON.parse(storedData);

View on GitHub (pinned to 14d757ce1f)

Solutions

  1. Check browser settings: allow cookies/site data for the Memos origin, and retry the sign-in.
  2. If embedding Memos in an iframe, add allow-same-origin to the sandbox attributes or open in a top-level tab.
  3. Clear the origin's sessionStorage (DevTools > Application > Session Storage) to free quota and retry.
  4. As a code hardening step, prune stale STATE_STORAGE_KEY entries before writing and surface the underlying caught error (currently swallowed into console.error) for diagnosability.

Example fix

// before
try {
  sessionStorage.setItem(STATE_STORAGE_KEY, JSON.stringify(stateData));
} catch (error) {
  console.error("Failed to store OAuth state:", error);
  throw new Error("Failed to initialize OAuth flow");
}

// after (surface cause + prune stale state)
try {
  sessionStorage.removeItem(STATE_STORAGE_KEY);
  sessionStorage.setItem(STATE_STORAGE_KEY, JSON.stringify(stateData));
} catch (error) {
  throw new Error(`Failed to initialize OAuth flow: ${error instanceof Error ? error.message : String(error)}`);
}
Defensive patterns

Strategy: try-catch

Validate before calling

function sessionStorageAvailable(): boolean {
  try {
    const probe = "__oauth_probe__";
    sessionStorage.setItem(probe, "1");
    sessionStorage.removeItem(probe);
    return true;
  } catch {
    return false;
  }
}

// before starting the flow:
if (!sessionStorageAvailable()) {
  showError("Enable site data/cookies to sign in with SSO.");
  return;
}

Type guard

function isStorageError(e: unknown): e is DOMException {
  return e instanceof DOMException && (e.name === "QuotaExceededError" || e.name === "SecurityError");
}

Try / catch

try {
  sessionStorage.setItem(STATE_STORAGE_KEY, JSON.stringify(stateData));
} catch (error) {
  if (error instanceof DOMException && error.name === "QuotaExceededError") {
    sessionStorage.clear(); // or remove stale keys and retry once
  }
  throw new Error(`Failed to initialize OAuth flow: ${error instanceof Error ? error.message : String(error)}`);
}

Prevention

When it happens

Trigger: Calling the OAuth init (signInWithIdp / link flow in web/src/utils/oauth.ts) when sessionStorage is full, blocked (Safari ITP / all-cookies-blocked), or accessed in a sandboxed iframe without allow-same-origin; also JSON.stringify of the state object itself cannot fail here, so the setItem call is the sole thrower.

Common situations: Browser set to block all cookies; Safari private mode; embedding Memos in an iframe; repeated large writes filling the 5MB origin quota; iOS WebView with storage partitioning.

Related errors


AI-assisted analysis of usememos/memos@14d757ce1f (2026-08-15). Data as JSON: /api/errors/f1e9833ea5dc030b. Report an issue: GitHub.