ic-use-internet-identity
Internet Identity is an authentication service running on the Internet Computer. It allows users to create an identity that can be used to authenticate with canisters (smart contracts) running on the Internet Computer.
ic-use-internet-identity
is a hook that makes it easy to integrate Internet Identity into your React application. It provides a simple interface for logging in and out with the Internet Identity service.

Features
- Cached Identity: The identity is cached in local storage and restored on page load. This allows the user to stay logged in even if the page is refreshed.
- Login progress: State variables are provided to indicate whether the user is logged in, logging in, or logged out.
- Reactive Identity Expiry: Automatically resets authentication state when the identity expires, keeping your app in sync without page reloads.
- Works with ic-use-actor: Plays nicely with ic-use-actor that provides easy access to canister methods.
- Router integration: Exposes
ensureInitialized()
and isAuthenticated()
for use outside React (examples use TanStack Router).
Table of Contents
Installation
pnpm install ic-use-internet-identity
The hook also requires the following @dfinity/x
packages to be installed with a version of at least 3.1.0
:
pnpm install @dfinity/agent @dfinity/auth-client @dfinity/identity @dfinity/candid
Usage
[!TIP]
For a complete example, see the ic-use-internet-identity-demo demo project.
To use ic-use-internet-identity
in your React application, follow these steps:
1. Setup the InternetIdentityProvider
component
Wrap your application's root component with InternetIdentityProvider
to provide all child components access to the identity context.
import { InternetIdentityProvider } from "ic-use-internet-identity";
import React from "react";
import ReactDOM from "react-dom/client";
ReactDOM.createRoot(document.getElementById("root")!).render(
<React.StrictMode>
<InternetIdentityProvider>
<App />
</InternetIdentityProvider>
</React.StrictMode>
);
[!TIP]
Identity Provider Configuration: The library defaults to using the main Internet Identity instance at https://identity.ic0.app
. You can override this by setting the identityProvider
in your loginOptions
.
- Default:
https://identity.ic0.app
(used automatically)
- Custom via loginOptions: Pass
identityProvider
in loginOptions
prop
- Local development:
http://${CANISTER_ID_INTERNET_IDENTITY}.localhost:4943
Configure via loginOptions
<InternetIdentityProvider
loginOptions={{
identityProvider: process.env.DFX_NETWORK === "local"
? `http://${process.env.CANISTER_ID_INTERNET_IDENTITY}.localhost:4943`
: "https://identity.ic0.app"
}}
>
<App />
</InternetIdentityProvider>
2. Connect the login()
function to a button
The login()
function initiates the Internet Identity authentication process. Here's what happens when you call it:
- Pre-flight Validation: The function first validates that all prerequisites are met (provider is present, auth client is initialized, user isn't already authenticated)
- Popup Window: If validation passes, it opens the Internet Identity service in a new popup window
- Status Updates: The
status
immediately changes to "logging-in"
and remains there until the process completes
- User Authentication: The user completes authentication in the popup window
- Result Handling:
- Success:
status
becomes "success"
, identity
is populated, and the popup closes
- Error:
status
becomes "error"
and error
contains the error details
- User Cancellation: Treated as an error with appropriate error message
[!WARNING]
User Interaction Required: The login()
function MUST be called in response to a user interaction (e.g., button click). Calling it in useEffect
or similar will fail because browsers block popup windows that aren't triggered by user actions.
[!IMPORTANT]
No Promise Handling Required: The login()
function returns void
and handles all results through the hook's state. Monitor the status
, error
, and identity
values returned by the hook instead of using try/catch blocks.
Monitoring the Login Process
The login process follows a predictable status flow:
"initializing"
→ Library is loading and checking for existing authentication
"idle"
→ Ready to login
"logging-in"
→ Login popup is open, user is authenticating
"success"
→ Login completed successfully, identity
is available
"error"
→ Login failed, check error
for details
Use the status
and error
state variables to track the login process and provide appropriate UI feedback:
import { useInternetIdentity } from "ic-use-internet-identity";
export function LoginButton() {
const { login, status, error, isError, identity } = useInternetIdentity();
const renderButton = () => {
switch (status) {
case "initializing":
return (
<button disabled>
⏳ Initializing...
</button>
);
case "idle":
return (
<button onClick={login}>
Login with Internet Identity
</button>
);
case "logging-in":
return (
<button disabled>
🔄 Logging in...
</button>
);
case "success":
return (
<button disabled>
✅ Logged in as {identity?.getPrincipal().toString().slice(0, 8)}...
</button>
);
case "error":
return (
<button onClick={login}>
🔄 Retry Login
</button>
);
default:
return null;
}
};
return (
<div>
{renderButton()}
{isError && (
<div style={{ color: "red", marginTop: "8px" }}>
❌ Login failed: {error?.message}
</div>
)}
</div>
);
}
Status Helper Properties
The hook also provides convenient boolean properties for common status checks:
const {
isInitializing,
isIdle,
isLoggingIn,
isLoginSuccess,
isError
} = useInternetIdentity();
if (isInitializing) {
}
if (isLoggingIn) {
}
if (isLoginSuccess && identity) {
}
3. Use the identity
context variable to access the identity
The identity
context variable contains the identity of the currently logged in user. The identity is available after successfully loading the identity from local storage or completing the login process.
The preferred way to use the identity is to connect it to the ic-use-actor hook. The hook provides a typed interface to the canister methods as well as interceptor functions for handling errors etc.
import { ReactNode } from "react";
import {
ActorProvider,
createActorContext,
createUseActorHook,
} from "ic-use-actor";
import {
canisterId,
idlFactory,
} from "path-to/your-service/index";
import { _SERVICE } from "path-to/your-service.did";
import { useInternetIdentity } from "ic-use-internet-identity";
const actorContext = createActorContext<_SERVICE>();
export const useActor = createUseActorHook<_SERVICE>(actorContext);
export default function Actors({ children }: { children: ReactNode }) {
const { identity } = useInternetIdentity();
return (
<ActorProvider<_SERVICE>
canisterId={canisterId}
context={actorContext}
identity={identity}
idlFactory={idlFactory}
>
{children}
</ActorProvider>
);
}
InternetIdentityProvider props
{
createOptions?: AuthClientCreateOptions;
loginOptions?: LoginOptions;
clearIdentityOnExpiry?: boolean;
children: ReactNode;
}
LoginOptions
The LoginOptions
interface extends AuthClientLoginOptions
from @dfinity/auth-client
with some modifications:
import type { AuthClientLoginOptions } from "@dfinity/auth-client";
export interface LoginOptions
extends Omit<
AuthClientLoginOptions,
"onSuccess" | "onError" | "maxTimeToLive"
> {
maxTimeToLive?: bigint;
}
This means you can use all properties from AuthClientLoginOptions
except onSuccess
, onError
. Available properties include:
identityProvider?: string | URL
- Identity provider URL (defaults to https://identity.ic0.app
)
maxTimeToLive?: bigint
- Session expiration (defaults to 1 hour)
allowPinAuthentication?: boolean
- Allow PIN/temporary key authentication
derivationOrigin?: string | URL
- Origin for delegated identity generation
windowOpenerFeatures?: string
- Popup window configuration
customValues?: Record<string, unknown>
- Extra values for login request
useInternetIdentity interface
export type Status =
| "initializing"
| "idle"
| "logging-in"
| "success"
| "error";
export type InternetIdentityContext = {
identity?: Identity;
login: () => void;
clear: () => void;
status: Status;
isInitializing: boolean;
isIdle: boolean;
isLoggingIn: boolean;
isLoginSuccess: boolean;
isError: boolean;
error?: Error;
};
Error Handling
The library handles all errors through its state management system. You don't need try/catch blocks - simply monitor the error
and isError
state:
import { useInternetIdentity } from "ic-use-internet-identity";
export function LoginComponent() {
const { login, error, isError, status } = useInternetIdentity();
return (
<div>
<button
onClick={login}
disabled={status === "logging-in"}
>
{status === "logging-in" ? "Logging in..." : "Login"}
</button>
{isError && (
<div style={{ color: "red" }}>
Login error: {error?.message}
</div>
)}
</div>
);
}
Router integration
When using ic-use-internet-identity
with routing libraries (for example, TanStack Router), it's recommended to handle the initialization phase before allowing navigation to protected routes. The library exports utility functions that work outside of React components and can be used with many routing libraries — TanStack Router is shown here as an example.
Available Functions
ensureInitialized(): Promise<Identity | undefined>
isAuthenticated(): boolean
getIdentity(): Identity | undefined
Basic Example
Here's how to protect routes (example: TanStack Router):
import { createRoute, redirect } from "@tanstack/react-router";
import { ensureInitialized, isAuthenticated } from "ic-use-internet-identity";
const dashboardRoute = createRoute({
getParentRoute: () => rootRoute,
path: "dashboard",
beforeLoad: async () => {
const identity = await ensureInitialized();
if (!identity) {
throw redirect({ to: "/login" });
}
},
component: DashboardComponent,
});
Client-side (reactive) Auth Guard
Note: beforeLoad
is executed during navigation and does not re-run when authentication state changes. If a user signs out after a route has already loaded, the beforeLoad
hook will not be invoked again. To handle dynamic changes in authentication (for example, user-initiated sign out), provide a React component that observes the hook and reacts to changes at runtime.
import { useRouter } from "@tanstack/react-router";
import { useInternetIdentity } from "ic-use-internet-identity";
import { useEffect } from "react";
export function AuthGuard() {
const router = useRouter();
const { identity } = useInternetIdentity();
useEffect(() => {
if (!identity) {
void router.invalidate()
}
}, [identity, router]);
return null;
}
Creating a Route Guard Helper
For multiple protected routes you can extract a small beforeLoad
helper and use file-route helpers (the example below uses TanStack Router). Below are two patterns: a simple authenticateRoute()
helper and an example showing how to create a protected file route with createFileRoute
.
import { isRedirect, redirect } from "@tanstack/react-router";
import { ensureInitialized } from "ic-use-internet-identity";
export async function authenticateRoute() {
try {
const identity = await ensureInitialized();
if (!identity) {
throw redirect({ to: "/login" });
}
} catch (err) {
if (isRedirect(e)) throw e
console.error("Identity initialization failed:", err);
throw redirect({ to: "/error" });
}
}
import { createFileRoute } from "@tanstack/react-router";
import { authenticateRoute } from "../lib/authenticate-route";
import About from "../components/About";
export const Route = createFileRoute("/about")({
beforeLoad: async () => authenticateRoute(),
component: About,
});
Important Notes
-
Always await initialization: The ensureInitialized()
function ensures the library has finished checking for cached identities before making routing decisions.
-
Double-check in components: beforeLoad
runs once during navigation and does not react to later authentication changes (for example, when a user signs out). Use the useInternetIdentity
hook in your components — or the AuthGuard
component above — to observe auth state changes and perform redirects or show fallback UI.
-
Handle loading states: During initialization (< 1 second), consider showing a loading spinner or splash screen.
-
Redirect patterns: You can either redirect to a login page or let components handle the unauthenticated state based on your UX preferences.
Security Considerations
- Delegation Expiry: By default, delegations expire after 1 hour and the identity state is automatically reset. Monitor
identity
for changes and handle re-authentication.
- Secure Storage: Identities are stored in browser local storage. Consider the security implications for your use case.
- Session Management: The library automatically clears the identity on expiry by default. To disable, set
clearIdentityOnExpiry={false}
on the InternetIdentityProvider
. Consider your app's security requirements.
Updates
See the CHANGELOG for details on updates.
Author
Contributing
Contributions are welcome. Please submit your pull requests or open issues to propose changes or report bugs.
License
This project is licensed under the MIT License. See the LICENSE file for more details.