@poince/auth
Poince authentication, embedded wallets, and payments for React.
The building blocks for auth, wallets, and payments — from sign-in to CRM: authentication (passkeys, email/SMS, social/OAuth, SIWE, MFA), self-custodial embedded wallets across EVM and Solana with funding and payment flows, and user attributes and events for your own backend. The UI ships in seven built-in locales — English, Chinese (Simplified and Traditional), Japanese, Spanish, Portuguese, and French — all fully overridable.
Install
npm install @poince/auth viem
# or
pnpm add @poince/auth viem
react (^18 || ^19), react-dom and viem (^2.38.6) are required peer dependencies —
viem is a peer because the SDK's public API hands you viem objects (clients, chains, accounts), which
must come from your app's own viem. npm 7+ and pnpm install peers automatically; on Yarn v1 or with
auto-install-peers=false, install them yourself. The Solana packages install automatically
(regular dependencies).
pnpm: allow or skip the native build scripts
On pnpm 10.4+ a few transitive dependencies with install/build scripts — @reown/appkit,
bufferutil, keccak, utf-8-validate (and, in a Next.js app, sharp) — are blocked by
default: you'll see Ignored build scripts …, and on pnpm 11 pnpm install (or any command
that auto-installs first, such as pnpm dev) aborts with ERR_PNPM_IGNORED_BUILDS. None of them
are needed at runtime — bufferutil, utf-8-validate and keccak have pure-JS fallbacks and
@reown/appkit's postinstall is non-essential — so clear it once and move on:
# review and approve (or skip) each script interactively — the simplest fix, any pnpm version
pnpm approve-builds
Or declare the choice in pnpm-workspace.yaml with the allowBuilds map (pnpm 11; it replaced
ignoredBuiltDependencies): false skips a package's build script (the JS fallbacks run), true
compiles the native speed-up.
# pnpm-workspace.yaml (pnpm 11)
allowBuilds:
'@reown/appkit': false
bufferutil: false
keccak: false
utf-8-validate: false
sharp: false # only if your app pulls sharp (e.g. a Next.js app)
npm and Yarn run these scripts without prompting, so this step is pnpm-only.
Quick start
// app/layout.tsx — a Server Component is fine: the SDK's entries are marked "use client".
import { PoinceProvider } from "@poince/auth";
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
<PoinceProvider appId="YOUR_APP_ID_25_CHARS_LONG">{children}</PoinceProvider>
</body>
</html>
);
}
appId is required — create an app in your Poince developer portal
to obtain it (a 25-character id). Solana support loads automatically when enabled in the portal —
connectors are auto-loaded by the provider, no extra import.
Trigger sign-in from a Client Component with useLogin; login() opens the Poince sign-in UI, and
usePoince() exposes the auth state. Gate on ready so the UI waits for the provider to initialise:
"use client";
import { useLogin, usePoince } from "@poince/auth";
export function SignIn() {
const { ready, authenticated, user } = usePoince();
const { login } = useLogin();
if (!ready) return <button disabled>Loading…</button>;
return authenticated ? (
<p>Signed in as {user?.id}</p>
) : (
<button onClick={() => login()}>Sign in</button>
);
}
Other hooks (useLoginWithEmail, useLogout, the wallet hooks) follow the same shape. If the appId
is missing or wrong, <PoinceProvider> renders a diagnostic in place of your tree (customise it with
onProviderError / providerErrorFallback), and login() rejects rather than hanging.
Entry points
| Import path | What it provides |
|---|---|
@poince/auth | Core: PoinceProvider, auth hooks, wallet hooks, tokens, chains |
@poince/auth/solana | Solana signing & wallet hooks; connectors auto-load — low-level connector exports also live here (advanced) |
@poince/auth/skin-contract | The themeable token vocabulary for served-theme authors (data only) |
@poince/auth/internal | ⚠️ Internal / unstable — no semver guarantee; not intended for app use |
Framework & tooling notes
- Next.js App Router / RSC: the published entries are marked
"use client", so you can render<PoinceProvider>straight from a Server Component such asapp/layout.tsx— no wrapper needed. Components that call Poince hooks are Client Components, as with any React hook. One exception: props passed from a Server Component must be serializable, so if you use the function forms ofonProviderError/providerErrorFallback, render the provider from a small"use client"file. - ESM-only: there is no CommonJS build. Consume the package through a bundler (Next, Vite, Remix,
webpack, Rollup, esbuild). A bare
<script type="module">will not work on its own — the SDK has many bare-specifier imports that a bundler (or an import map) must resolve. - Jest / CommonJS: because the package is ESM,
require()throwsERR_REQUIRE_ESM. Under Jest, let it through your transform (e.g.transformIgnorePatterns) or use an ESM test setup. - TypeScript: use
moduleResolution"bundler","node16", or"nodenext". Types ship as.d.mtsbehind theexportsmap and will not resolve under the legacy"node"mode. - Content-Security-Policy: the SDK injects
<style>tags (its stylesheet andstyled-components). Under a strictstyle-src 'nonce-…'policy, pass the nonce —<PoinceProvider nonce={nonce}>— or expose it as<meta property="csp-nonce" nonce="…">(Vite'shtml.cspNonceemits exactly this) or__webpack_nonce__; then no'unsafe-inline'is needed for styles. With server-side rendering, give your own styled-components registry the same nonce. Inlinestyle=attributes in server-rendered markup are outside a nonce's reach. You also needconnect-srcfor the Poince API andscript-src/frame-srcfor the captcha and WalletConnect providers.
Configuration
appId (and optional clientId) identify your app. The optional config prop sets only
client-side mechanism keys — e.g. which login methods your UI offers. Server-authored settings
(theme / appearance, available chains, captcha, legal links, Solana RPC) are server-authoritative
and are ignored if passed through config; configure those in the developer portal.
Errors & debugging
Everything the SDK throws or rejects with is a PoinceError: a PoinceApiError when the Poince API
answered with an error (status holds the HTTP status, null for a network failure), a
PoinceClientError when the SDK rejected a call before sending it, or a PoinceConnectorError when a
wallet connector failed. Branch on error.code — a PoinceErrorCode value — never on the message text:
import { isPoinceApiError, isPoinceError, PoinceErrorCode, useLoginWithEmail } from "@poince/auth";
const { sendCode } = useLoginWithEmail();
try {
await sendCode({ email });
} catch (error) {
if (isPoinceApiError(error) && error.code === PoinceErrorCode.TOO_MANY_REQUESTS) {
// back off, then let the user retry
} else if (isPoinceError(error)) {
console.warn(error.code, error.message);
} else {
throw error;
}
}
If <PoinceProvider> fails while building its configuration, it renders a built-in diagnostic in place
of your tree; pass onProviderError to log it and providerErrorFallback to render your own UI.
Invalid props — e.g. an appId that is not exactly 25 characters — throw a PoinceClientError during
render, which your own error boundary catches. The package ships without source maps — the code is obfuscated by design — so
stack traces point into minified chunks: report the code, the message and the SDK version, and see
docs.poince.com for what each code means.
Changelog
See CHANGELOG.md for the release notes — it ships in this package.
License
Commercial, proprietary. See LICENSE. Use requires a valid Poince account and agreement via the Poince developer portal.