@humanity-org/react-sdk
v0.0.2
Published
React components and hooks for Humanity Protocol OAuth and verification
Downloads
179
Readme
Table of Contents
- Installation
- Quick Start
- Components
- Hooks
- TypeScript Types
- Error Handling
- Testing
- Theming
- Production Checklist
- License
Installation
npm install @humanity-org/react-sdk
# or
yarn add @humanity-org/react-sdk
# or
pnpm add @humanity-org/react-sdkQuick Start
1. Wrap your app with HumanityProvider
import { HumanityProvider } from '@humanity-org/react-sdk'
function App() {
return (
<HumanityProvider
clientId="hp_xxx"
redirectUri="https://yourapp.com/callback"
environment="production"
>
<YourApp />
</HumanityProvider>
)
}2. Add a login button
import { HumanityConnect } from '@humanity-org/react-sdk'
function LoginPage() {
return (
<HumanityConnect
scopes={['openid', 'profile', 'identity:read']}
onSuccess={(result) => console.log('Logged in!', result)}
/>
)
}3. Access user data and gate content
import { useAuth, HumanityGate } from '@humanity-org/react-sdk'
function Dashboard() {
const { user, isAuthenticated, logout } = useAuth()
if (!isAuthenticated) return <div>Please log in</div>
return (
<div>
<h1>Welcome, {user?.name}!</h1>
<HumanityGate
preset="ageOver21"
fallback={<p>Age verification required</p>}
>
<PremiumContent />
</HumanityGate>
<button onClick={logout}>Sign Out</button>
</div>
)
}Components
All components must be rendered inside a <HumanityProvider>.
HumanityProvider
The root context provider. Wraps your entire application.
<HumanityProvider
clientId="hp_xxx"
redirectUri="https://app.com/callback"
environment="production"
storage="memory"
theme="system"
onError={(err) => console.error(err)}
>
<App />
</HumanityProvider>Props
| Prop | Type | Default | Description |
| ------------- | ------------------------------------------------ | -------------- | ---------------------------------------------------------------------------- |
| clientId | string | required | Your Humanity application client ID. |
| redirectUri | string | required | OAuth callback URI registered in the developer portal. |
| environment | 'production' \| 'sandbox' | 'production' | Which Humanity API environment to use. |
| storage | 'memory' \| 'localStorage' \| 'sessionStorage' | 'memory' | Token storage strategy. memory is most secure (tokens cleared on refresh). |
| theme | 'light' \| 'dark' \| 'system' | 'system' | Controls built-in component theming. |
| baseUrl | string | — | Advanced override for custom API base URL. |
| onError | (error: HumanityReactError) => void | — | Global error handler called for auth/verification errors. |
HumanityConnect
OAuth login button that handles the full authentication flow.
<HumanityConnect
scopes={['openid', 'profile', 'identity:read']}
mode="popup"
variant="primary"
size="md"
label="Sign in with Humanity"
onSuccess={(result) => handleLogin(result)}
onError={(error) => console.error(error)}
/>Props
| Prop | Type | Default | Description |
| ----------- | --------------------------------------- | ------------------------- | -------------------------------------------------- |
| scopes | string[] | ['openid', 'profile'] | OAuth scopes to request. |
| mode | 'popup' \| 'redirect' | 'popup' | OAuth flow type. |
| onSuccess | (result: AuthResult) => void | — | Called after successful authentication. |
| onError | (error: HumanityReactError) => void | — | Called on authentication error. |
| onLoading | (loading: boolean) => void | — | Called when loading state changes. |
| variant | 'primary' \| 'secondary' \| 'outline' | 'primary' | Button visual variant. |
| size | 'sm' \| 'md' \| 'lg' | 'md' | Button size. |
| label | string | 'Sign in with Humanity' | Button label text. |
| disabled | boolean | false | Disable the button. |
| className | string | — | Additional CSS class. |
| style | React.CSSProperties | — | Inline styles. |
| children | React.ReactNode | — | Custom children (replaces default button content). |
HumanityVerify
One-click verification against a single preset.
<HumanityVerify
preset="ageOver21"
onVerified={(result) => {
if (result.verified) grantAccess()
}}
/>Props
| Prop | Type | Default | Description |
| ------------ | --------------------------------------- | ------------ | -------------------------------------------- |
| preset | string | required | Preset key to verify (e.g. 'ageOver21'). |
| onVerified | (result: VerificationResult) => void | — | Called after verification completes. |
| onError | (error: HumanityReactError) => void | — | Called on verification error. |
| label | string | — | Custom button label. |
| variant | 'primary' \| 'secondary' \| 'outline' | 'primary' | Button visual variant. |
| size | 'sm' \| 'md' \| 'lg' | 'md' | Button size. |
| autoVerify | boolean | false | Trigger verification automatically on mount. |
HumanityGate
Conditionally renders children when a preset is verified; shows a fallback otherwise.
<HumanityGate
preset="kycPassed"
fallback={<CompleteKYC />}
loadingFallback={<Spinner />}
>
<AgeRestrictedContent />
</HumanityGate>Multiple presets (require all by default):
<HumanityGate
preset={['kyc_passed', 'accredited_investor']}
match="all"
fallback={<CompleteVerification />}
>
<InvestorDashboard />
</HumanityGate>Props
| Prop | Type | Default | Description |
| ----------------- | ----------------- | ------------ | -------------------------------------------------------------------- |
| preset | string | required | Single preset key; gate passes when it's verified. |
| presets | string[] | — | Multiple presets. Use with requireAll for AND/OR logic. |
| requireAll | boolean | true | When true, all presets must pass. When false, any one is enough. |
| fallback | React.ReactNode | — | Rendered when the gate condition is not met. |
| loadingFallback | React.ReactNode | — | Rendered while verification is in progress. |
HumanityProfile
Displays the authenticated user's profile with optional verification badges.
<HumanityProfile
variant="card"
showAvatar={true}
showBadges={true}
showFields={['email', 'humanity_score']}
/>Props
| Prop | Type | Default | Description |
| ------------ | --------------------------------- | -------- | -------------------------- |
| variant | 'card' \| 'inline' \| 'minimal' | 'card' | Display variant. |
| showAvatar | boolean | true | Show user avatar. |
| showBadges | boolean | true | Show verification badges. |
| showFields | string[] | — | Profile fields to display. |
| className | string | — | Additional CSS class. |
HumanityErrorBoundary
React error boundary for catching and displaying errors in the Humanity component tree.
<HumanityErrorBoundary
fallback={(error) => <p>Something went wrong: {error.message}</p>}
>
<HumanityConnect />
</HumanityErrorBoundary>Props
| Prop | Type | Description |
| ---------- | ----------------------------------------------- | ------------------------------------- |
| fallback | (error: Error) => React.ReactNode | Render function for the error state. |
| onError | (error: Error, info: React.ErrorInfo) => void | Called when a child component throws. |
| children | React.ReactNode | The component tree to protect. |
Hooks
useHumanity()
Full-access hook to the entire context. Throws if called outside a <HumanityProvider>.
const {
user,
accessToken,
authStatus,
isAuthenticated,
isLoading,
error,
login,
logout,
refreshToken,
verify,
verifyMultiple,
} = useHumanity()useAuth()
Auth-focused hook that selects only authentication state and operations. Thin wrapper around useHumanity().
import { useAuth } from '@humanity-org/react-sdk'
function LoginButton() {
const { isAuthenticated, login, logout, isLoading } = useAuth()
if (isLoading) return <Spinner />
if (isAuthenticated) return <button onClick={logout}>Sign Out</button>
return <button onClick={() => login()}>Sign In</button>
}Returns
| Field | Type | Description |
| ----------------- | --------------------------------------------------- | -------------------------------------------- |
| authStatus | 'loading' \| 'authenticated' \| 'unauthenticated' | Tri-state auth status. |
| isAuthenticated | boolean | Shorthand: authStatus === 'authenticated'. |
| isLoading | boolean | Shorthand: authStatus === 'loading'. |
| user | UserProfile \| null | Current user profile, or null. |
| accessToken | string \| null | Current access token, or null. |
| error | HumanityReactError \| null | Most recent auth error, or null. |
| login | (options?: LoginOptions) => Promise<AuthResult> | Initiate OAuth login flow. |
| logout | () => Promise<void> | Log out and revoke tokens. |
| refreshToken | () => Promise<AuthResult> | Manually refresh the access token. |
| getAccessToken | () => string \| null | Get the current access token synchronously. |
useVerification()
Runs preset verifications against the Humanity Protocol API. Returns a discriminated union status for type-safe state narrowing.
import { useVerification } from '@humanity-org/react-sdk'
function AgeCheck() {
const { verify, status, result, isLoading } = useVerification()
return (
<div>
<button onClick={() => verify('ageOver21')} disabled={isLoading}>
Check Age
</button>
{status === 'success' && (
<p>{result.verified ? '✅ Verified' : '❌ Not verified'}</p>
)}
{status === 'error' && <p>Verification failed</p>}
</div>
)
}Returns
| Field | Type | Description |
| ---------------- | ------------------------------------------------------ | ---------------------------------------------------------------- |
| status | 'idle' \| 'loading' \| 'success' \| 'error' | Discriminated union status. Narrow with switch/if. |
| result | VerificationResult \| null | Verification result when status === 'success'; null otherwise. |
| error | HumanityReactError \| null | Error when status === 'error'; null otherwise. |
| isLoading | boolean | True while a request is in-flight. |
| verify | (preset: string) => Promise<VerificationResult> | Verify a single preset. |
| verifyMultiple | (presets: string[]) => Promise<VerificationResult[]> | Verify multiple presets at once. |
| reset | () => void | Reset to idle state. |
usePresets()
Manages multiple preset verifications. Stores results in an internal map for easy per-preset access.
import { usePresets } from '@humanity-org/react-sdk'
import { useEffect } from 'react'
function Dashboard() {
const { verifyAll, isVerified, getResult, isLoading } = usePresets()
useEffect(() => {
verifyAll(['ageOver18', 'kycPassed'])
}, [verifyAll])
if (isLoading) return <Spinner />
return (
<div>
<p>Age 18+: {isVerified('ageOver18') ? '✅' : '❌'}</p>
<p>KYC: {isVerified('kycPassed') ? '✅' : '❌'}</p>
</div>
)
}Returns
| Field | Type | Description |
| ------------ | ------------------------------------------------------ | ------------------------------------------------ |
| status | 'idle' \| 'loading' \| 'success' \| 'error' | Discriminated union status. |
| results | Map<string, VerificationResult> | Map of preset key → verification result. |
| error | HumanityReactError \| null | Error when status === 'error'; null otherwise. |
| isLoading | boolean | True while a verification batch is in-flight. |
| verifyAll | (presets: string[]) => Promise<VerificationResult[]> | Verify a batch of presets; stores all results. |
| getResult | (preset: string) => VerificationResult \| undefined | Get the cached result for a specific preset. |
| isVerified | (preset: string) => boolean | Quick check: is the given preset verified? |
| reset | () => void | Clear all stored results. |
useCredentialUpdates()
Polls the Humanity Protocol API for credential updates. Useful for keeping verification status in sync.
import { useCredentialUpdates } from '@humanity-org/react-sdk'
function CredentialDashboard() {
const { data, isLoading, error, refetch } = useCredentialUpdates({
pollInterval: 30_000,
enabled: true,
})
if (isLoading && !data) return <Spinner />
if (error) return <p>Failed to load credentials</p>
return (
<div>
<p>Credentials: {data?.credentials.length ?? 0}</p>
<button onClick={refetch}>Refresh</button>
</div>
)
}Options
| Option | Type | Default | Description |
| -------------- | --------- | ------- | ------------------------------------------------------- |
| pollInterval | number | 0 | Polling interval in milliseconds. 0 disables polling. |
| enabled | boolean | true | Whether polling is active. |
Returns
| Field | Type | Description |
| ----------- | ---------------------------- | ----------------------------------- |
| data | CredentialUpdates \| null | Latest credential updates, or null. |
| isLoading | boolean | True while a request is in-flight. |
| error | HumanityReactError \| null | Most recent error, or null. |
| refetch | () => Promise<void> | Manually trigger a fetch. |
TypeScript Types
All types are exported from @humanity-org/react-sdk.
Key types
// Auth state
type AuthStatus = 'loading' | 'authenticated' | 'unauthenticated'
type Environment = 'production' | 'sandbox'
type StorageStrategy = 'memory' | 'localStorage' | 'sessionStorage'
type OAuthMode = 'popup' | 'redirect'
// User profile (returned after login)
interface UserProfile {
sub: string // Unique user ID
email?: string
name?: string
humanity_score?: number
// ...additional fields from granted scopes
}
// Auth operation result
interface AuthResult {
user: UserProfile
accessToken: string
// ...token metadata
}
// Verification result
interface VerificationResult {
preset: string
verified: boolean
status: 'valid' | 'expired' | 'pending' | 'unavailable'
expiresAt?: string
}
// Preset status
type PresetStatus = 'valid' | 'expired' | 'pending' | 'unavailable'Error Handling
The SDK exports typed error classes for precise error handling:
import {
isHumanityError,
type HumanityReactError,
} from '@humanity-org/react-sdk'
function handleError(err: unknown) {
if (isHumanityError(err)) {
console.error('Humanity error:', {
message: err.message,
code: err.code,
status: err.status,
})
}
}Using the global error handler:
<HumanityProvider
clientId="hp_xxx"
redirectUri="https://app.com/callback"
onError={(error) => {
Sentry.captureException(error)
}}
>
<App />
</HumanityProvider>Wrapping with an error boundary:
<HumanityErrorBoundary
fallback={(error) => <ErrorPage message={error.message} />}
>
<HumanityConnect onError={handleError} />
</HumanityErrorBoundary>Error utilities
| Export | Description |
| ----------------- | ---------------------------------------------------------------- |
| isHumanityError | Type guard: returns true if the value is a HumanityReactError. |
| createError | Factory for typed SDK errors. |
| normalizeError | Converts any thrown value into a HumanityReactError. |
| parseOAuthError | Parses OAuth error parameters from a callback URL. |
Testing
Use MockHumanityProvider for unit tests without real API calls:
import { MockHumanityProvider } from '@humanity-org/react-sdk/testing'
test('shows dashboard when authenticated', () => {
render(
<MockHumanityProvider
user={{ sub: 'test-123', email: '[email protected]', name: 'Test User' }}
isAuthenticated={true}
>
<Dashboard />
</MockHumanityProvider>,
)
expect(screen.getByText('Welcome, Test User!')).toBeInTheDocument()
})
test('shows login when unauthenticated', () => {
render(
<MockHumanityProvider isAuthenticated={false}>
<Dashboard />
</MockHumanityProvider>,
)
expect(screen.getByText('Please log in')).toBeInTheDocument()
})Theming
Customize component appearance with CSS variables:
:root {
--humanity-primary: #000000;
--humanity-primary-hover: #222222;
--humanity-success: #10b981;
--humanity-error: #ef4444;
--humanity-border-radius: 8px;
--humanity-font-family: inherit;
}Set theme="dark" or theme="light" on <HumanityProvider> to control the built-in theme; theme="system" (default) follows the OS preference.
Production Checklist
Before going live:
- [ ] OAuth popup flow works end-to-end across all supported browsers
- [ ] Redirect URI is registered for both sandbox and production environments
- [ ] Required scopes match what the user is prompted to grant
- [ ]
storage="memory"is used (or you understand the XSS risk oflocalStorage) - [ ] Error cases handled: expired tokens, revoked access, popup blocked
- [ ]
<HumanityErrorBoundary>wraps authentication-critical UI - [ ]
onErrorglobal handler is configured for error monitoring - [ ]
baseUrlandenvironmentpoint to production, not sandbox - [ ] Client ID is correct for the production application
License
MIT
