@smarthivelabs-devs/auth-react
v1.6.0
Published
SmartHive Auth React provider, hooks, and components for web applications
Downloads
678
Readme
@smarthivelabs-devs/auth-react
React provider, hooks, and components for SmartHive Auth. Works with any React 18+ app including Next.js (App Router and Pages Router), Vite, and Create React App.
Installation
npm install @smarthivelabs-devs/auth-react @smarthivelabs-devs/auth-sdk
# or
pnpm add @smarthivelabs-devs/auth-react @smarthivelabs-devs/auth-sdk
# or
yarn add @smarthivelabs-devs/auth-react @smarthivelabs-devs/auth-sdk
@smarthivelabs-devs/auth-sdkis a required peer dependency.
Prerequisites
From your SmartHive dashboard, get:
projectIdpublishableKeybaseUrl— the URL of your SmartHive Auth service
Setup
Wrap your app with SmartHiveAuthProvider. This provides auth state to every component in the tree.
Next.js App Router
Create a client-side provider wrapper (the SDK uses React context, which requires "use client"):
// app/providers.tsx
"use client";
import { SmartHiveAuthProvider } from "@smarthivelabs-devs/auth-react";
export function Providers({ children }: { children: React.ReactNode }) {
return (
<SmartHiveAuthProvider
projectId={process.env.NEXT_PUBLIC_SMARTHIVE_AUTH_PROJECT_ID!}
publishableKey={process.env.NEXT_PUBLIC_SMARTHIVE_AUTH_PUBLISHABLE_KEY!}
baseUrl={process.env.NEXT_PUBLIC_SMARTHIVE_AUTH_BASE_URL!}
redirectUri={`${process.env.NEXT_PUBLIC_APP_URL}/auth/callback`}
>
{children}
</SmartHiveAuthProvider>
);
}// app/layout.tsx
import { Providers } from "./providers";
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html>
<body>
<Providers>{children}</Providers>
</body>
</html>
);
}Vite / Create React App
// src/main.tsx
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import { SmartHiveAuthProvider } from "@smarthivelabs-devs/auth-react";
import App from "./App";
createRoot(document.getElementById("root")!).render(
<StrictMode>
<SmartHiveAuthProvider
projectId={import.meta.env.VITE_SMARTHIVE_AUTH_PROJECT_ID}
publishableKey={import.meta.env.VITE_SMARTHIVE_AUTH_PUBLISHABLE_KEY}
baseUrl={import.meta.env.VITE_SMARTHIVE_AUTH_BASE_URL}
redirectUri={`${window.location.origin}/auth/callback`}
>
<App />
</SmartHiveAuthProvider>
</StrictMode>
);Headless Sign-in (Custom Login Form)
No browser redirect. Call the method, get tokens. Full control of your UI.
Email + Password
import { useAuth } from "@smarthivelabs-devs/auth-react";
import { useState } from "react";
export default function LoginPage() {
const { signIn } = useAuth();
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [error, setError] = useState("");
async function handleSubmit(e: React.FormEvent) {
e.preventDefault();
try {
await signIn.email({ email, password });
// Session saved automatically — user is now signed in
} catch (e: any) {
setError(e.message);
}
}
return (
<form onSubmit={handleSubmit}>
<input value={email} onChange={(e) => setEmail(e.target.value)} placeholder="Email" type="email" />
<input value={password} onChange={(e) => setPassword(e.target.value)} placeholder="Password" type="password" />
{error && <p style={{ color: "red" }}>{error}</p>}
<button type="submit">Sign in</button>
</form>
);
}Phone OTP
const { signIn } = useAuth();
// Step 1 — send the code
await signIn.phone.sendOtp({ phoneNumber: "+1234567890" });
// Step 2 — verify (returns session, user is now signed in)
await signIn.phone.verify({ phoneNumber: "+1234567890", code: "123456" });Email OTP
const { signIn } = useAuth();
// Step 1 — send the code
await signIn.emailOtp.send({ email: "[email protected]" });
// Step 2 — verify (returns session, user is now signed in)
await signIn.emailOtp.verify({ email: "[email protected]", code: "123456" });Magic Link
const { signIn } = useAuth();
// Sends an email — user clicks the link to sign in (no token returned here)
await signIn.magicLink.send({ email: "[email protected]" });Headless Sign-up
const { signUp } = useAuth();
const result = await signUp.email({
email: "[email protected]",
password: "secret123",
name: "Jane Doe", // optional
});
if (result.requiresVerification) {
// Email verification required — show "check your inbox" screen
} else {
// Account created and signed in immediately
}Returning to your app after email verification
By default, the verification link in the email lands on SmartHive's own confirmation page. To send the user back to your app afterward, pass redirect (and optionally fallback) to signUp.email:
const result = await signUp.email({
email: "[email protected]",
password: "secret123",
redirect: "https://myapp.com/welcome",
fallback: "https://myapp.com/download", // optional, shown if redirect doesn't seem to work
});SmartHive's confirmation page shows a brief "Email verified" state, then auto-redirects to redirect (with a visible countdown and a "Continue now" button). Pass callbackUrl instead if you want full control — it bypasses SmartHive's confirmation page entirely and redirects straight to your own URL (with ?error=<code> appended on failure). Omit all three to keep today's default.
Resend verification email
const { signUp } = useAuth();
// Called from a "Didn't receive the email? Resend" button
await signUp.resendVerificationEmail({ email: "[email protected]" });
// Accepts the same redirect/fallback/callbackUrl options as signUp.email
await signUp.resendVerificationEmail({
email: "[email protected]",
redirect: "https://myapp.com/welcome",
});Throws SmartHiveAuthError with code resend_failed if the server rejects the request.
Social OAuth Sign-in (Google, Apple, GitHub, etc.)
Each provider uses your project's own credentials — the consent screen shows your app name. Configure credentials in your SmartHive dashboard under Project → OAuth Providers, then use loginSocial:
import { useAuth } from "@smarthivelabs-devs/auth-react";
export function SocialLoginButtons() {
const { loginSocial } = useAuth();
return (
<div>
<button onClick={() => loginSocial("google")}>Continue with Google</button>
<button onClick={() => loginSocial("apple")}>Continue with Apple</button>
<button onClick={() => loginSocial("github")}>Continue with GitHub</button>
</div>
);
}Set up a callback page that your redirectUri points to. It reads access_token + refresh_token from the URL and saves the session:
// app/auth/social/callback/page.tsx
"use client";
import { useRouter } from "next/navigation";
import { SocialAuthCallback } from "@smarthivelabs-devs/auth-react";
export default function SocialCallback() {
const router = useRouter();
return (
<SocialAuthCallback
onSuccess={() => router.replace("/dashboard")}
onError={(err) => router.replace(`/login?error=${err.message}`)}
fallback={<p>Signing you in...</p>}
/>
);
}Pass the callback URL when initiating:
loginSocial("google", { redirectUri: "https://myapp.com/auth/social/callback" });Supported providers:
google · apple · github · facebook · twitter · linkedin · microsoft · discord · spotify · twitch · reddit · gitlab · slack · notion · zoom · figma
OAuth Redirect Sign-in (SmartHive hosted page)
The original PKCE flow — redirects to the SmartHive hosted login page and back.
import { useAuth } from "@smarthivelabs-devs/auth-react";
export function LoginButton() {
const { login } = useAuth();
return <button onClick={() => login()}>Sign in with SmartHive</button>;
}Set up a callback page to handle the redirect back:
// app/auth/callback/page.tsx
"use client";
import { useRouter } from "next/navigation";
import { AuthCallback } from "@smarthivelabs-devs/auth-react";
export default function Callback() {
const router = useRouter();
return (
<AuthCallback
onSuccess={() => router.replace("/dashboard")}
onError={() => router.replace("/login")}
fallback={<div>Signing you in...</div>}
/>
);
}Provider Props
SmartHiveAuthProvider accepts all SmartHiveAuthConfig fields as props:
| Prop | Type | Required | Description |
|---|---|---|---|
| projectId | string | Yes | Your SmartHive project ID |
| publishableKey | string | Yes | Your publishable (public) key |
| baseUrl | string | Yes | URL of your SmartHive Auth service |
| authDomain | string | No | Custom branded auth domain for login flows |
| redirectUri | string | No | Default OAuth callback URL |
| children | ReactNode | Yes | Your app tree |
Hooks
useAuth()
Returns the full auth context. Use this when you need multiple values at once.
import { useAuth } from "@smarthivelabs-devs/auth-react";
function MyComponent() {
const {
client, // SmartHiveAuthClient — the raw SDK client
session, // AuthSession | null
isLoaded, // true once the initial session check is done
isSignedIn, // boolean
login, // PKCE redirect sign-in (SmartHive hosted page)
loginSocial, // social OAuth redirect (Google, Apple, GitHub, etc.)
logout, // () => Promise<void>
refreshSession, // () => Promise<void>
getAuthorizationHeader, // () => Promise<Record<string, string>>
authFetch, // fetch wrapper that adds the Bearer token
signIn, // headless sign-in methods (no redirect)
signUp, // headless sign-up methods (no redirect)
} = useAuth();
if (!isLoaded) return <p>Loading...</p>;
return isSignedIn ? (
<button onClick={logout}>Sign out</button>
) : (
<button onClick={() => login()}>Sign in</button>
);
}useSession()
Returns the current AuthSession or null.
import { useSession } from "@smarthivelabs-devs/auth-react";
function TokenDisplay() {
const session = useSession();
if (!session) return null;
return <pre>{session.accessToken}</pre>;
}useUser()
Returns the current user object from the session, or null.
import { useUser } from "@smarthivelabs-devs/auth-react";
function UserGreeting() {
const user = useUser();
if (!user) return null;
return <p>Welcome, {(user as any).email}</p>;
}useIsLoaded()
Returns true once the initial session check has finished. Use this to avoid flash of unauthenticated content.
import { useIsLoaded } from "@smarthivelabs-devs/auth-react";
function App() {
const isLoaded = useIsLoaded();
if (!isLoaded) return <FullPageSpinner />;
return <Router />;
}useIsSignedIn()
Returns true if signed in, false if signed out, or null while still loading.
import { useIsSignedIn } from "@smarthivelabs-devs/auth-react";
function NavBar() {
const isSignedIn = useIsSignedIn();
if (isSignedIn === null) return <NavSkeleton />;
return isSignedIn ? <UserMenu /> : <LoginButton />;
}useAuthFetch()
Returns an authenticated fetch function that automatically includes the Bearer token on every request.
import { useAuthFetch } from "@smarthivelabs-devs/auth-react";
function DataLoader() {
const authFetch = useAuthFetch();
async function loadData() {
const res = await authFetch("/api/protected-data");
const data = await res.json();
console.log(data);
}
return <button onClick={loadData}>Load data</button>;
}useAuthorizationHeader()
Returns a function that resolves to { authorization: "Bearer <token>" }. Useful for manually constructing requests or passing to third-party clients.
import { useAuthorizationHeader } from "@smarthivelabs-devs/auth-react";
function UploadButton() {
const getAuthorizationHeader = useAuthorizationHeader();
async function upload(file: File) {
const headers = await getAuthorizationHeader();
const formData = new FormData();
formData.append("file", file);
await fetch("/api/upload", { method: "POST", headers, body: formData });
}
return <input type="file" onChange={(e) => e.target.files && upload(e.target.files[0])} />;
}Render Helpers
Conditional rendering components based on auth state. These are ergonomic wrappers that also wait for isLoaded.
<SignedIn>
Renders children only when auth has loaded and a session exists.
import { SignedIn } from "@smarthivelabs-devs/auth-react";
<SignedIn>
<UserDashboard />
</SignedIn><SignedOut>
Renders children only when auth has loaded and there is no session.
import { SignedOut } from "@smarthivelabs-devs/auth-react";
<SignedOut>
<LandingPage />
</SignedOut><AuthLoading>
Renders children while the initial session check is in progress.
import { AuthLoading } from "@smarthivelabs-devs/auth-react";
<AuthLoading>
<FullPageSpinner />
</AuthLoading>Combined Example
import { SignedIn, SignedOut, AuthLoading } from "@smarthivelabs-devs/auth-react";
export default function HomePage() {
return (
<>
<AuthLoading>
<p>Checking authentication...</p>
</AuthLoading>
<SignedIn>
<Dashboard />
</SignedIn>
<SignedOut>
<LandingPage />
</SignedOut>
</>
);
}<AuthCallback> Component
Handles the OAuth redirect on your callback page. Automatically exchanges the code for tokens and calls onSuccess when done.
// app/auth/callback/page.tsx (Next.js App Router)
"use client";
import { useRouter } from "next/navigation";
import { AuthCallback } from "@smarthivelabs-devs/auth-react";
export default function CallbackPage() {
const router = useRouter();
return (
<AuthCallback
onSuccess={() => router.replace("/dashboard")}
onError={(err) => {
console.error("Auth error:", err.message);
router.replace("/login?error=auth_failed");
}}
fallback={<p>Completing sign in...</p>}
/>
);
}AuthCallback Props
| Prop | Type | Required | Description |
|---|---|---|---|
| onSuccess | (session: AuthSession) => void | Yes | Called after successful token exchange |
| onError | (error: Error) => void | No | Called if the exchange fails |
| fallback | ReactNode | No | Shown while the exchange is in progress |
<SocialAuthCallback> Component
Handles the social OAuth redirect on your callback page. Reads access_token + refresh_token from the URL, saves the session, and calls onSuccess. Use this on whatever page your social redirectUri points to.
// app/auth/social/callback/page.tsx (Next.js App Router)
"use client";
import { useRouter } from "next/navigation";
import { SocialAuthCallback } from "@smarthivelabs-devs/auth-react";
export default function SocialCallbackPage() {
const router = useRouter();
return (
<SocialAuthCallback
onSuccess={() => router.replace("/dashboard")}
onError={(err) => {
console.error("Social auth error:", err.message);
router.replace("/login?error=social_auth_failed");
}}
fallback={<p>Completing sign in...</p>}
/>
);
}SocialAuthCallback Props
| Prop | Type | Required | Description |
|---|---|---|---|
| onSuccess | (session: AuthSession) => void | Yes | Called after tokens are parsed and session saved |
| onError | (error: Error) => void | No | Called if the provider returned an error or token is missing |
| fallback | ReactNode | No | Shown while parsing the callback URL |
Route Protection
Next.js Middleware (App Router)
Protect routes server-side using @smarthivelabs-devs/auth-server:
// middleware.ts
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
import { createNextAuthMiddleware } from "@smarthivelabs-devs/auth-server";
const verifyAuth = createNextAuthMiddleware({
issuer: process.env.SMARTHIVE_AUTH_ISSUER!,
projectId: process.env.SMARTHIVE_AUTH_PROJECT_ID,
});
export async function middleware(request: NextRequest) {
const { auth, error } = await verifyAuth(request);
if (error) {
return NextResponse.redirect(new URL("/login", request.url));
}
// auth.userId is available
}
export const config = {
matcher: ["/dashboard/:path*", "/api/protected/:path*"],
};Client-side Guard (React)
"use client";
import { useIsSignedIn, useIsLoaded } from "@smarthivelabs-devs/auth-react";
import { useRouter } from "next/navigation";
import { useEffect } from "react";
export function ProtectedPage({ children }: { children: React.ReactNode }) {
const isLoaded = useIsLoaded();
const isSignedIn = useIsSignedIn();
const router = useRouter();
useEffect(() => {
if (isLoaded && !isSignedIn) {
router.replace("/login");
}
}, [isLoaded, isSignedIn, router]);
if (!isLoaded) return <FullPageSpinner />;
if (!isSignedIn) return null;
return <>{children}</>;
}Sign-out
const { logout } = useAuth();
// Clears localStorage + invalidates session on the server
await logout();Full Next.js App Router Example
app/
├── layout.tsx ← SmartHiveAuthProvider here
├── page.tsx ← uses SignedIn / SignedOut
├── login/
│ └── page.tsx ← custom login form using signIn.*
├── auth/
│ └── callback/
│ └── page.tsx ← AuthCallback (OAuth redirect only)
└── dashboard/
└── page.tsx ← protected page// app/login/page.tsx — custom form, no redirect
"use client";
import { useAuth } from "@smarthivelabs-devs/auth-react";
import { useState } from "react";
export default function LoginPage() {
const { signIn } = useAuth();
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [error, setError] = useState("");
async function handleSubmit(e: React.FormEvent) {
e.preventDefault();
try {
await signIn.email({ email, password });
} catch (e: any) {
setError(e.message ?? "Sign in failed.");
}
}
return (
<form onSubmit={handleSubmit}>
<input value={email} onChange={(e) => setEmail(e.target.value)} type="email" placeholder="Email" />
<input value={password} onChange={(e) => setPassword(e.target.value)} type="password" placeholder="Password" />
{error && <p style={{ color: "red" }}>{error}</p>}
<button type="submit">Sign in</button>
</form>
);
}// app/dashboard/page.tsx
"use client";
import { useAuth, SignedIn } from "@smarthivelabs-devs/auth-react";
export default function Dashboard() {
const { logout, authFetch } = useAuth();
async function fetchProfile() {
const res = await authFetch("/api/profile");
console.log(await res.json());
}
return (
<SignedIn>
<h1>Dashboard</h1>
<button onClick={fetchProfile}>Load profile</button>
<button onClick={logout}>Sign out</button>
</SignedIn>
);
}Environment Variables
# .env.local (Next.js)
NEXT_PUBLIC_SMARTHIVE_AUTH_PROJECT_ID=proj_abc123
NEXT_PUBLIC_SMARTHIVE_AUTH_PUBLISHABLE_KEY=pk_prod_abc123
NEXT_PUBLIC_SMARTHIVE_AUTH_BASE_URL=https://auth.myapp.com
NEXT_PUBLIC_APP_URL=https://myapp.com# .env (Vite)
VITE_SMARTHIVE_AUTH_PROJECT_ID=proj_abc123
VITE_SMARTHIVE_AUTH_PUBLISHABLE_KEY=pk_prod_abc123
VITE_SMARTHIVE_AUTH_BASE_URL=https://auth.myapp.comTypeScript Types
import type {
SmartHiveAuthProviderProps,
SocialProvider, // "google" | "apple" | "github" | ...
} from "@smarthivelabs-devs/auth-react";
import type {
AuthSession,
HeadlessClient,
HeadlessSignInResult,
HeadlessSignUpResult,
SmartHiveAuthClient,
SmartHiveAuthConfig,
} from "@smarthivelabs-devs/auth-sdk";Related Packages
| Package | Use case |
|---|---|
| @smarthivelabs-devs/auth-sdk | Core SDK — framework-agnostic |
| @smarthivelabs-devs/auth-expo | React Native / Expo apps |
| @smarthivelabs-devs/auth-server | Express / Next.js server-side JWT verification |
License
MIT © SmartHive Labs
