@uninspired/auth-client
v1.0.16
Published
Auth client for Uninspired Studio Products
Downloads
384
Readme
@uninspired/auth-client
Client SDK for integrating Uninspired Studio authentication, purchases, and checkout into product apps (e.g. Momentum, Unpinned).
Works in any JavaScript environment. Includes a React provider and hook for apps that use React.
Installation
bun add @uninspired/auth-client
# or
npm install @uninspired/auth-clientPeer dependencies
For React apps, also install:
bun add react react-dom@posthog/react is an optional peer dependency — the AuthProvider automatically identifies users when a PostHog instance is available in the React tree.
What it provides
| Feature | Description |
|---|---|
| Session management | Fetch the current user session (including anonymous sessions) |
| Anonymous sign-in | Automatically sign in visitors as anonymous users |
| Sign out | End the current session |
| Purchase data | Fetch Paddle transactions and Stripe/Paddle subscriptions |
| Access checks | isPurchased() and isSubscribed() helpers for gating features |
| Login URL builder | Generate a link to the accounts page with a post-login redirect back to your app |
| Checkout URL builder | Generate a link to the hosted checkout flow |
| Newsletter | Subscribe/unsubscribe from mailing lists |
| PostHog integration | Auto-identify users in PostHog when authenticated |
Authentication methods (magic link, email OTP, passkey) are handled on the accounts page — this client manages sessions and purchase data, not the login UI itself.
Quick start (React)
import { AuthProvider, useAuth } from "@uninspired/auth-client";
function App() {
return (
<AuthProvider
baseUrl="https://auth.uninspired.app"
frontendUrl="https://uninspired.app"
mailingListPublicKey="your-public-key"
>
<MyApp />
</AuthProvider>
);
}
function MyApp() {
const { isLoggedIn, session, isPurchased, getLoginUrl, signOut } = useAuth();
if (!isLoggedIn) {
return (
<a href={getLoginUrl({ redirectUrl: window.location.href, appName: "My App" })}>
Log in
</a>
);
}
const hasPro = isPurchased(["pri_01234567890"]);
return (
<div>
<p>Hello, {session?.user.email ?? "guest"}</p>
{hasPro ? <ProFeature /> : <UpgradePrompt />}
<button onClick={() => signOut()}>Sign out</button>
</div>
);
}AuthClient (vanilla / SSR)
Use AuthClient directly when you are not in a React tree, or in server-side rendering.
import { AuthClient } from "@uninspired/auth-client";
const client = new AuthClient(
"https://auth.uninspired.app", // API URL
"https://uninspired.app", // Accounts frontend URL
request.headers.get("cookie"), // optional: forward cookies for SSR
"your-mailing-list-public-key", // optional
);
const session = await client.getSession();
const purchases = await client.getPurchases();Constructor
new AuthClient(apiUrl, frontendUrl, cookie?, mailingListPublicKey?)| Parameter | Description |
|---|---|
| apiUrl | Auth API base URL (e.g. https://auth.uninspired.app) |
| frontendUrl | Accounts frontend URL (e.g. https://uninspired.app) |
| cookie | Optional cookie header string for server-side session lookup |
| mailingListPublicKey | Optional public key for mailing list endpoints |
All API requests use credentials: "include" in the browser, so session cookies are sent automatically on the same root domain.
Methods
Session
const session = await client.getSession();
// Returns Session | null
await client.signInAnonymously();
// Creates an anonymous session (done automatically by AuthProvider)
await client.signOut(onSuccess?);
// Signs out the current userPurchases
const purchases = await client.getPurchases();
// Returns UserPurchases | nullUserPurchases contains:
{
paddle: {
customerId?: string;
subscriptions: Array<{ id, status, items: [{ priceId, productId }] }>;
transactions: Array<{ id, items: string[], adjustments: [...] }>;
};
stripe: Array<{ id, status, items: string[] }>;
mailingList: { subscribed: boolean };
}URL builders
// Link to the accounts login page, redirect back after auth
const loginUrl = client.getLoginUrl({
redirectUrl: "https://myapp.uninspired.app/dashboard",
appName: "My App", // optional, shown on the login page
});
// Link to the hosted checkout flow
const checkoutUrl = client.getCheckoutUrl({
priceId: "pri_01234567890",
productId: "pro_01234567890",
discountCode: "SAVE20", // optional
successUrl: "https://myapp.uninspired.app/welcome", // optional
quantity: 1, // optional, defaults to 1
});Newsletter
await client.newsletterSubscribe("[email protected]", "my-app-group", "https://callback.url");
await client.newsletterUnsubscribe("[email protected]");AuthProvider and useAuth (React)
AuthProvider props
| Prop | Type | Description |
|---|---|---|
| baseUrl | string | Auth API URL |
| frontendUrl | string | Accounts frontend URL |
| mailingListPublicKey | string? | Public key for mailing list API |
| initialSession | Session \| null? | Pre-fetched session for SSR |
| initialPurchases | UserPurchases \| null? | Pre-fetched purchases for SSR |
| authClient | AuthClient? | Custom client instance |
| queryClient | QueryClient? | Custom TanStack Query client |
The provider automatically:
- Revalidates the session on mount via
get-session(with 1-minute stale time; SSRinitialSessionis shown immediately while revalidating) - Signs in anonymously only after revalidation confirms no session exists
- Fetches purchases when a session is available
- Identifies the user in PostHog when authenticated
useAuth() return value
| Property | Type | Description |
|---|---|---|
| session | Session \| null | Current session |
| isLoggedIn | boolean | true if user is authenticated (not anonymous) |
| isSessionFetching | boolean | Session query loading state |
| refetchSession | () => Promise<void> | Re-fetch session |
| purchases | UserPurchases \| null | User's purchase data |
| isPurchasesFetching | boolean | Purchases query loading state |
| refetchPurchases | () => Promise<void> | Re-fetch purchases |
| signOut | (onSuccess?) => Promise<void> | Sign out (resets PostHog) |
| getLoginUrl | AuthClient["getLoginUrl"] | Build login URL |
| getCheckoutUrl | AuthClient["getCheckoutUrl"] | Build checkout URL |
| isPurchased | (priceIds: string[]) => boolean | Check one-time purchase |
| isSubscribed | (priceIds: string[], productId: string) => boolean | Check active subscription |
| newsletterSubscribe | AuthClient["newsletterSubscribe"] | Subscribe to newsletter |
| newsletterUnsubscribe | AuthClient["newsletterUnsubscribe"] | Unsubscribe from newsletter |
Access checks
const { isPurchased, isSubscribed } = useAuth();
// One-time purchase: true if user bought any of these price IDs (and hasn't been refunded)
isPurchased(["pri_lifetime", "pri_bundle"]);
// Subscription: true if user has an active Paddle or Stripe subscription
isSubscribed(["pri_monthly", "pri_yearly"], "pro_product_id");Server-side rendering
For SSR frameworks, fetch session and purchases on the server and pass them as initial data:
// server
import { AuthClient } from "@uninspired/auth-client";
const client = new AuthClient(
process.env.AUTH_API_URL,
process.env.ACCOUNTS_FRONTEND_URL,
request.headers.get("cookie"),
);
const [session, purchases] = await Promise.all([
client.getSession(),
client.getPurchases(),
]);
// render
<AuthProvider
baseUrl={process.env.AUTH_API_URL}
frontendUrl={process.env.ACCOUNTS_FRONTEND_URL}
initialSession={session}
initialPurchases={purchases}
>
{children}
</AuthProvider>Forward the request's Cookie header to the constructor so the API can resolve the session server-side.
Cross-subdomain cookies
Sessions use cross-subdomain cookies scoped to the Uninspired Studio root domain (e.g. .uninspired.app). Your product app must be hosted on a subdomain of the same root domain for cookies to be shared automatically.
Make sure your app's origin is listed in the auth API's FRONTEND_URLS configuration.
Types
The package re-exports shared types:
import type { Session, UserPurchases } from "@uninspired/auth-client";Building from source
This package is part of the US Auth monorepo. To build locally:
cd packages/auth-client
bun run buildOutput goes to dist/.
Environment URLs
| Environment | API | Accounts frontend |
|---|---|---|
| Production | https://auth.uninspired.app | https://uninspired.app |
| Development | https://auth.dev.uninspired.app | https://dev.uninspired.app |
| Local | http://localhost:3000 | http://localhost:5173 |
