Authentication
IC Reactor provides seamless integration with Internet Identity for authentication. This guide covers everything you need to know about user authentication.
Authentication hooks are created by passing an AuthenticationManager to
createAuthHooks. Identity-attribute hooks come from a separate
createIdentityAttributeHooks factory:
import { AuthenticationManager, IdentityAttributesManager, createAuthHooks, createIdentityAttributeHooks,} from "@ic-reactor/react"import { clientManager } from "./index"
export const authentication = new AuthenticationManager({ clientManager })export const identityAttributes = new IdentityAttributesManager(authentication)
export const { useAuth, useUserPrincipal, useAgentState } = createAuthHooks(authentication)
export const { useIdentityAttributes } = createIdentityAttributeHooks(identityAttributes)useAuth
Section titled “useAuth”The primary hook for managing authentication. Provides login, logout, and access to the current identity and principal:
import { useAuth } from "../reactor/hooks"
function AuthButton() { const { login, logout, isAuthenticated, isAuthenticating, principal } = useAuth()
if (isAuthenticating) { return <button disabled>Connecting...</button> }
return isAuthenticated ? ( <button onClick={logout}> Logout {principal?.toText().slice(0, 8)}... </button> ) : ( <button onClick={() => login()}>Login with Internet Identity</button> )}Login Options
Section titled “Login Options”Customize the login flow with options:
const { login } = useAuth()
login({ // Identity provider URL (auto-detected based on network) identityProvider: "https://id.ai/authorize",
// Session duration (default: 8 hours) maxTimeToLive: BigInt(7 * 24 * 60 * 60 * 1_000_000_000), // 7 days
// Callbacks — onError receives an optional error string onSuccess: () => { console.log("Logged in!") navigate("/dashboard") }, onError: (error) => { console.error("Login failed:", error) toast.error("Authentication failed") },})Client Options
Section titled “Client Options”Every option below can be passed either once, on the AuthenticationManager
constructor, or per call to login() — the per-call value wins.
| Option | Type | Default | Description |
|---|---|---|---|
identityProvider |
string | URL |
auto-detected | Identity provider URL |
derivationOrigin |
string | URL |
– | Origin the principal is derived from — see the callout below |
idleOptions |
AuthClientIdleOptions |
10 min → sign out + reload | Idle detection and what happens on timeout |
storage |
AuthClientStorageLike |
IndexedDB | Persistent storage backend for the session key and delegation |
keyType |
"ECDSA" | "Ed25519" |
"ECDSA" |
Session key algorithm; use "Ed25519" when the storage backend cannot hold a CryptoKey |
windowOpenerFeatures |
string |
– | window.open features string for the identity provider popup |
openIdProvider |
"google" | "apple" | "microsoft" |
– | One-click sign-in through that provider |
transport |
"window" | "redirect" |
"window" |
How the client talks to the provider; "redirect" needs @icp-sdk/auth v8 |
identity |
SignIdentity | PartialIdentity |
– | An existing identity to authenticate via delegation |
login() additionally accepts maxTimeToLive, targets, onSuccess, and
onError. The AuthenticationManager constructor additionally accepts
clientManager (required), authClient (bring your own client instance), and
internetIdentityId (canister ID of a locally deployed Internet Identity).
Auto-Detected Identity Provider
Section titled “Auto-Detected Identity Provider”The AuthenticationManager automatically selects the correct Internet Identity provider based on your network:
| Network | Identity Provider |
|---|---|
| IC (mainnet) | https://id.ai/authorize |
| Local | http://rdmx6-jaaaa-aaaaa-aaadq-cai.localhost:4943/authorize |
The local URL uses the replica port from the agent host and the
internet_identity canister ID from the ic_env cookie when one is present.
A provider named by that cookie is honoured only where the cookie is trusted —
a local replica, or a host you opted in with allowEnvConfig — since any
sibling subdomain can write it; the mainnet provider is fixed regardless. You
can override the whole thing by passing identityProvider.
Identity Attributes
Section titled “Identity Attributes”IC Reactor can request signed identity attributes from @icp-sdk/auth v8
for OpenID email and profile flows. Use useIdentityAttributes() when a React
component needs to request attributes and send the signed payload to a backend or
canister for verification.
import { useEffect } from "react"import { authentication, useIdentityAttributes } from "../reactor/hooks"
function RegisterWithOpenIdProvider() { const { requestOpenIdAttributes, attributes, isRequestingAttributes, attributeError, } = useIdentityAttributes()
// Preload the auth module so the identity provider window can be opened // synchronously from the click handler below. useEffect(() => { authentication.prepareClient() }, [])
async function handleRegister() { const result = await requestOpenIdAttributes({ // Pass the nonce as a callback — see the caution below nonce: () => backend.callMethod({ functionName: "register_begin" }), openIdProvider: "microsoft", keys: ["email", "name"], })
await backend.callMethod({ functionName: "register_finish", args: [ { data: result.signedAttributes.data, signature: result.signedAttributes.signature, }, ], }) }
return ( <button disabled={isRequestingAttributes} onClick={handleRegister}> {attributes?.decodedAttributes.email ?? attributeError?.message ?? "Continue with provider"} </button> )}Pass a documented auth provider alias ("google", "apple", or
"microsoft") or an issuer URL such as "https://issuer.example.com" for
custom OpenID providers. Only the three aliases change how the user signs in; a
raw issuer URL just scopes the requested keys to openid:<issuer>:<key>.
For non-React code, use identityAttributes.requestOpenId() — or
identityAttributes.request() for non-OpenID keys — on the
IdentityAttributesManager directly.
useUserPrincipal
Section titled “useUserPrincipal”A convenience hook to get the current user’s principal:
import { useUserPrincipal } from "../reactor/hooks"
function UserBadge() { const principal = useUserPrincipal()
if (!principal || principal.isAnonymous()) { return <span>Anonymous User</span> }
return ( <span className="principal-badge">{principal.toText().slice(0, 8)}...</span> )}useAgentState
Section titled “useAgentState”Access the IC agent’s initialization state. This is useful for showing a global loading screen while the agent (and potential session) is warming up:
import { useAgentState } from "../reactor/hooks"
function AppRoot({ children }) { const { isInitializing, error } = useAgentState()
if (isInitializing) { return <LoadingScreen message="Connecting to IC..." /> }
if (error) { return <ErrorScreen error={error} /> }
return children}Protected Routes
Section titled “Protected Routes”Create a wrapper component for routes that require authentication:
import { useAuth } from "../reactor/hooks"import { Navigate, useLocation } from "react-router-dom"
function ProtectedRoute({ children }: { children: React.ReactNode }) { const { isAuthenticated, isAuthenticating } = useAuth() const location = useLocation()
// Show loading while checking auth state if (isAuthenticating) { return <LoadingSpinner /> }
// Redirect to login if not authenticated if (!isAuthenticated) { return <Navigate to="/login" state={{ from: location }} replace /> }
return <>{children}</>}Session Persistence
Section titled “Session Persistence”Sessions are automatically persisted in IndexedDB. When users return to your app, the useAuth hook automatically triggers session restoration. clientManager.initialize() only initializes the agent — outside React, call authentication.authenticate() to restore a session.
Session Duration
Section titled “Session Duration”By default, Internet Identity sessions last 8 hours. You can customize this:
login({ maxTimeToLive: BigInt(7 * 24 * 60 * 60 * 1_000_000_000), // 7 days in nanoseconds})Automatic Query Invalidation
Section titled “Automatic Query Invalidation”When the user’s identity changes — on login, logout, and session restore — IC Reactor sweeps the cache of every connected canister: in-flight queries are cancelled, inactive cached entries are removed, and active queries are invalidated so they refetch as the new identity.
Removal matters because query keys carry no principal. An entry that was only invalidated would still be readable through getQueryData or fetchQuery under the next identity — so the previous principal’s inactive data (a balance, a profile, a deposit address) is dropped outright, and only the queries a mounted component is actively watching live on through a refetch.
The sweep is scoped by canister ID (every reactor query key starts with one), so sharing a QueryClient with the rest of your app does not cause your unrelated REST or GraphQL queries to refetch on every sign-in.
Without Authentication
Section titled “Without Authentication”If your app doesn’t need authentication, you can skip the @icp-sdk/auth package entirely. The ClientManager will work with an anonymous identity. Queries will work, but update calls requiring authentication will fail.
Further Reading
Section titled “Further Reading”- useAuth — Main auth hook reference
- useAgentState — Agent state reference
- React Setup — Complete React configuration
- Error Handling — Handle auth and query errors
- Query Caching — Understand query invalidation