@agora-sdk/auth-react-js
v0.10.2
Published
Black-box OAuth callback handling and auth ergonomics for Agora SDK (Replyke fork) web apps: persistence-gated callback redirect, auth-ready status, reliable logout, stale-account self-heal.
Maintainers
Readme
@agora-sdk/auth-react-js
Black-box OAuth callback handling + auth ergonomics for Agora SDK (a Replyke fork) web apps.
It runs inside <ReplykeProvider> and observes the SDK's own auth state — you never touch
localStorage keys, count render cycles, or read useAccountSync.
Why
The SDK's handleOAuthCallback() stages tokens in Redux and starts an async user fetch, but the
session isn't written to localStorage until several render cycles later. In a multi-page app (Astro,
Next.js, Remix, SvelteKit), navigating away on its synchronous return tears down the React tree
before persistence runs — and the freshly minted session is lost. This package gates navigation on
actual persistence.
Install
pnpm add @agora-sdk/auth-react-js
# peers (you already have these): react, react-dom, @agora-sdk/react-jsOAuth callback page (the thing everyone tries to write — now correct)
import { useOAuthCallback } from "@agora-sdk/auth-react-js";
export default function Callback() {
const { status, error } = useOAuthCallback({ redirectTo: "/#comments" });
if (status === "error") return <p>Sign-in failed: {error}</p>;
return <Spinner />; // success full-page-navigates to redirectTo
}It gates navigation on a fresh session actually landing in localStorage (the state the next document
boots from), not on volatile in-store auth — so it stays correct even when a leftover stale account's
boot-refresh fails 401 mid-flow. Single-session apps can pass pruneStaleOnMount: true to clear any
pre-existing account first and silence that cosmetic 401.
Or the drop-in component:
import { OAuthCallbackHandler } from "@agora-sdk/auth-react-js";
<OAuthCallbackHandler redirectTo="/" onError={(m) => toast(m)} pending={<Spinner />} />Auth-ready status
import { useAuthStatus } from "@agora-sdk/auth-react-js";
const { status } = useAuthStatus(); // 'initializing' | 'authenticated' | 'unauthenticated'Reliable logout
import { useSignOutEverywhere } from "@agora-sdk/auth-react-js";
const { signOutEverywhere, isPending } = useSignOutEverywhere();
<button disabled={isPending} onClick={() => void signOutEverywhere().catch(() => {})}>Sign out</button>Prefer this over the SDK's
useAuth().signOut(), which is active-account-only (it switches accounts rather than ending the session).
Self-heal a stale-only session
import { useAuthSelfHeal } from "@agora-sdk/auth-react-js";
function App() { useAuthSelfHeal(); return <Thread />; } // prunes a dead active account onceEmail verification & password reset pages
The server emails links back to your app — /auth/verify-email and /auth/reset-password. Drop
these in on those routes so the links stop 404-ing:
import { EmailVerificationHandler, PasswordResetHandler } from "@agora-sdk/auth-react-js";
// route: /auth/verify-email
<EmailVerificationHandler redirectTo="/?verified=1" />
// route: /auth/reset-password (ships a minimal new-password form; pass renderForm for your own UI)
<PasswordResetHandler redirectTo="/signin?reset=1" />Both fail closed if the link's projectId doesn't match your app's (no request is sent), and strip
the one-time token from the URL after use. Each has a matching logic hook (useEmailVerification,
usePasswordReset) if you'd rather render your own page. For the "didn't get it?" flow:
import { ResendVerificationButton } from "@agora-sdk/auth-react-js";
<ResendVerificationButton email={email} onSent={() => toast("Sent!")} />Scope
Web only. The callback race is specific to cross-document navigation; React Native / Expo OAuth does not tear down the tree.
Full guide
The complete guide — every hook + component, the design guarantees, and the A8 stale-account race
analysis — ships with this package as AUTH.md and lives in the repo at
docs/AUTH.md.
