Secure SSO Token Handling in Coco App: The `handle_sso_callback` Strategy
Coco App mitigates OAuth interception and replay attacks by validating a one-time request ID in the frontend before delegating the authorization code exchange to the Tauri backend, where tokens are stored in native storage never accessible to the renderer process.
The open-source Coco App (infinilabs/coco-app) implements a hardened OAuth 2.0 flow for single sign-on (SSO) that keeps access tokens out of the JavaScript runtime. By leveraging Tauri's multi-process architecture, the application ensures that securely handling SSO tokens after handle_sso_callback involves strict state validation, server-side token exchange, and filesystem-level isolation.
CSRF Protection via One-Time Request IDs
Generating the State Parameter in the UI
When a user initiates login, src/components/Cloud/ServiceAuth.tsx generates a cryptographically random UUID to serve as a transient state parameter. This value is stored in the global appStore (ssoRequestID) and embedded in the authentication URL sent to the identity provider (IdP).
// src/components/Cloud/ServiceAuth.tsx
const LoginClick = useCallback(() => {
if (loading) return;
const requestID = uuidv4(); // one-time UUID
setSSORequestID(requestID); // store in appStore
const url = `${cloudSelectService?.auth_provider?.sso?.url}
/?provider=${cloudSelectService?.id}
&product=coco
&request_id=${requestID}`; // embed in SSO URL
console.log("Open SSO link, requestID:", url);
OpenURLWithBrowser(url); // open in external browser
setLoading(true);
}, [loading, cloudSelectService]);
This pattern ensures that the authorization request is bound to a specific client state, preventing cross-site request forgery (CSRF) attacks where a malicious actor might attempt to inject a stolen authorization code.
Deep-Link Validation Before Backend Invocation
The useDeepLinkManager.ts hook intercepts the OAuth callback via the operating system's deep-link handler. Before calling the Tauri backend, it extracts both the request_id and code from the callback URL and performs a strict equality check against the value held in appStore.
// src/hooks/useDeepLinkManager.ts
const handleOAuthCallback = useCallback(async (url: URL) => {
const reqId = url.searchParams.get("request_id");
const code = url.searchParams.get("code");
const { ssoRequestID } = useAppStore.getState(); // retrieve stored state
if (reqId !== ssoRequestID) {
addError("Request ID not matched, skip");
return; // abort if mismatch
}
const serverId = cloudSelectService?.id;
if (!code || !serverId) return;
// delegate to backend (only code, no token)
await platformAdapter.commands("handle_sso_callback", {
serverId,
requestId: ssoRequestID,
code,
});
platformAdapter.emitEvent("oauth_success", { serverId });
}, []);
If the received request_id does not match the stored ssoRequestID, the flow aborts immediately, shielding the backend from processing potentially replayed or malicious authentication attempts.
Backend Token Isolation and Persistence
The handle_sso_callback Command Implementation
Located in src-tauri/src/server/auth.rs, the handle_sso_callback command receives only the server_id, request_id, and code. It never accepts a pre-fetched token, ensuring that the frontend cannot inject arbitrary bearer tokens. The function exchanges the authorization code for an access token via the Coco-Server API and immediatelymaterializes a ServerAccessToken instance.
// src-tauri/src/server/auth.rs
#[tauri::command]
pub async fn handle_sso_callback(
app_handle: AppHandle,
server_id: String,
request_id: String,
code: String,
) -> Result<(), String> {
// exchange code → access token (performed by the server)
let access_token = ServerAccessToken::new(server_id.clone(), code.clone(), 3600);
save_access_token(server_id.clone(), access_token).await; // store securely
persist_servers_token(&app_handle).await?; // persist to disk
// additional profile refresh omitted for brevity
Ok(())
}
According to the Coco App source code, the save_access_token function stores the token in a memory-safe structure, while persist_servers_token (defined in src-tauri/src/server/servers.rs) serializes the data to the Tauri data directory—outside the Chromium WebView sandbox.
Native Storage Outside the Renderer
The persist_servers_token utility writes the token store to the local filesystem using Tauri's native APIs. This architecture guarantees that:
- Token confidentiality: The access token is handled exclusively within the Rust backend; the string never traverses the IPC boundary to the TypeScript frontend.
- Secure persistence: Tokens reside in the operating system's user data directory, protected by OS-level file permissions, rather than
localStorageorIndexedDBwhere they could be accessed by XSS payloads.
Event-Driven UI Synchronization
Rather than returning the token to the frontend, the backend signals completion by emitting an oauth_success event. The ServiceAuth component subscribes to this event via platformAdapter.listenEvent and refreshes the server metadata without ever handling the raw bearer token.
// src/components/Cloud/ServiceAuth.tsx
const { run: debouncedAuthSuccess } = useDebounceFn((event) => {
const { serverId } = event.payload;
if (serverId) {
refreshClick(serverId, () => setLoading(false));
addError(t("cloud.connect.hints.loginSuccess"), "info");
}
});
useEffect(() => {
const unlistenOAuth = platformAdapter.listenEvent(
"oauth_success",
debouncedAuthSuccess
);
return () => { unlistenOAuth.then(fn => fn()); };
}, [refreshClick]);
This event-driven model enforces least-privilege UI design: the renderer process learns only that authentication succeeded, while the sensitive credential remains confined to the native backend.
Summary
- State validation: A one-time UUID generated in
ServiceAuth.tsxand verified inuseDeepLinkManager.tsprevents CSRF and replay attacks by rejecting callbacks with mismatchedrequest_idvalues. - Backend isolation: The
handle_sso_callbackcommand insrc-tauri/src/server/auth.rsperforms the OAuth token exchange, ensuring the access token is never exposed to the JavaScript context. - Native persistence: The
persist_servers_tokenfunction stores credentials in the Tauri data directory, isolated from the WebView sandbox and XSS vulnerabilities. - Event architecture: The UI receives only an
oauth_successsignal, maintaining strict separation between the authentication state and the token material.
Frequently Asked Questions
How does Coco App prevent CSRF attacks during SSO?
Coco App prevents CSRF attacks by generating a cryptographically random UUID (requestID) when the user clicks Login, storing it in the global appStore, and appending it to the SSO URL as a state parameter. When the IdP redirects back to the app, useDeepLinkManager.ts validates that the callback's request_id matches the stored value before invoking handle_sso_callback; any mismatch aborts the flow immediately.
Where are access tokens stored after handle_sso_callback?
Access tokens are stored exclusively in the Tauri backend's native filesystem via the persist_servers_token function in src-tauri/src/server/servers.rs. This writes the ServerAccessToken data to the Tauri data directory (e.g., $APP_DATA on Linux/macOS or %APPDATA% on Windows), ensuring the token persists across sessions without ever entering the browser's storage mechanisms.
Why doesn't the frontend receive the raw access token?
The frontend does not receive the raw access token to enforce the principle of least privilege and mitigate XSS risks. The handle_sso_callback command in src-tauri/src/server/auth.rs performs the code exchange and persists the token internally, then emits an oauth_success event to notify the UI of completion. This design ensures that even if the renderer process is compromised, the attacker cannot extract the bearer token from memory or storage.
What happens if the request_id does not match during callback?
If the request_id parameter in the OAuth callback URL does not match the ssoRequestID stored in appStore, the handleOAuthCallback function in src/hooks/useDeepLinkManager.ts logs an error ("Request ID not matched, skip") and returns early without calling the Tauri backend. This prevents the application from exchanging a potentially stolen authorization code, effectively blocking replay attacks and unauthorized authentications.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →