Use OAuth to let users sign in with Whop on your website
Use OAuth 2.1 + PKCE with OpenID Connect (OIDC) to let users sign in with Whop on your website or app.
You can use the returned access tokens to access data and perform actions on behalf of whop users.
OAuth endpoints live at https://api.whop.com/oauth/
await startWhopOAuth( "app_xxxxxxxxx", "https://yourapp.com/oauth/callback", "openid profile email", // optionally specify more custom scopes here "biz_xxxxx", // optionally specify a scoped company id here);
If the user is not logged in, Whop will prompt for login, then show the consent screen.
If the user has already approved your application for the requested scopes,
they will be automatically redirected back without needing to confirm twice.
If you provide companyId, tokens are company-scoped for a specific user, meaning you
will only have access to resources that that particular user can control on the specified Whop company.
Whop redirects back to your redirect_uri with code and state. Use this function to verify the state, exchange the code for tokens, and retrieve credentials:
Report incorrect code
Copy
Ask AI
const STORAGE_KEY = "whop_oauth_pkce";interface WhopTokens { access_token: string; refresh_token: string; id_token?: string; // only present if "openid" scope was requested token_type: string; expires_in: number; obtained_at: number; // we add this client-side for refresh logic}async function handleWhopCallback(clientId: string, redirectUri: string): Promise<WhopTokens> { const params = new URLSearchParams(window.location.search); const [code, returnedState, error] = [params.get("code"), params.get("state"), params.get("error")]; if (error) throw new Error(`OAuth error: ${error} - ${params.get("error_description") || ""}`); const stored = JSON.parse(sessionStorage.getItem(STORAGE_KEY) || "null"); sessionStorage.removeItem(STORAGE_KEY); if (!stored || returnedState !== stored.state) throw new Error("Invalid state - possible CSRF"); const res = await fetch("https://api.whop.com/oauth/token", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ grant_type: "authorization_code", code, redirect_uri: redirectUri, client_id: clientId, code_verifier: stored.codeVerifier, }), }); if (!res.ok) { const err = await res.json().catch(() => ({})); throw new Error(`Token exchange failed: ${err.error_description || res.status}`); } const tokens = await res.json(); return { ...tokens, obtained_at: Date.now() };}function storeTokens(tokens: WhopTokens) { document.cookie = `whop_tokens=${encodeURIComponent(JSON.stringify(tokens))}; path=/; max-age=${60 * 60 * 24 * 30}; secure; samesite=strict`;}function getTokens(): WhopTokens | null { const match = document.cookie.match(/whop_tokens=([^;]+)/); return match ? JSON.parse(decodeURIComponent(match[1])) : null;}function clearTokens() { document.cookie = "whop_tokens=; path=/; max-age=0";}
Refresh tokens rotate on each use. Always store the new tokens returned from the refresh endpoint.
If you provided company_id during authorization, you must provide the same company_id when refreshing.
Always revoke tokens when users log out. This invalidates the refresh token immediately, preventing unauthorized access even if the token was compromised.