TypeScript
The base-idp package — browser client, server verifier, and adapters for React, Next.js, SvelteKit, Vite, Node, NestJS, and Express.
The base-idp npm package is the largest of the SDKs because TypeScript has
the most surfaces to integrate with. It ships browser code, server code, and
framework adapters for the runtimes Square products run on. The CLI you have
been reading about — npx base-idp create — is part of this same package.
Install
npm install base-idpThe package has no required peer dependencies. React is optional and only needed if you use the React adapter.
# With bun
bun add base-idp
# With pnpm
pnpm add base-idp
# With yarn
yarn add base-idpPicking an entry point
The package exports several entry points. You import the one that matches your runtime and role.
| Import path | When to use it |
|---|---|
base-idp/browser | Browser code, SPAs, public clients. |
base-idp/server | Server code that verifies tokens or runs login. |
base-idp/next | Next.js route handlers. |
base-idp/node | Plain Node servers (Express, Fastify, raw http). |
base-idp/react | React hooks and providers. |
base-idp/sveltekit | SvelteKit endpoints and hooks. |
base-idp/vite | Vite plugins for build-time config. |
Always import from the most specific entry point that fits. If you are
writing Next.js route handlers, use base-idp/next instead of
base-idp/server directly.
Browser usage (public client)
The browser client runs the PKCE login in a system browser tab.
import { createBrowserBaseIdpAuth } from "base-idp/browser";
export const auth = createBrowserBaseIdpAuth({
clientId: process.env.NEXT_PUBLIC_BASE_IDP_CLIENT_ID!,
});Login
await auth.loginWithRedirect({
returnTo: "/dashboard",
});This builds the authorize URL, stores the PKCE verifier and state in session storage, and redirects to Base IdP.
Handle the callback
On your callback page:
"use client";
import { useEffect } from "react";
import { auth } from "@/lib/auth";
export default function CallbackPage() {
useEffect(() => {
auth.handleCallback().then((result) => {
window.location.href = result.returnTo ?? "/";
});
}, []);
return <p>Signing you in...</p>;
}The handleCallback() call exchanges the code, verifies the token, and
returns the tokens plus the verified principal.
Logout
await auth.logout();Revokes the refresh token on Base IdP and clears local storage.
Server usage (verify-only)
A verify-only server only needs to check incoming tokens. No secret required.
import type { Request, Response, NextFunction } from "express";
import { BaseIdPServerClient } from "base-idp/server";
const client = new BaseIdPServerClient({
clientId: process.env.BASE_IDP_CLIENT_ID, // optional, pins the audience
});
export async function requireAuth(req: Request, res: Response, next: NextFunction) {
const header = req.headers.authorization ?? "";
const token = header.startsWith("Bearer ") ? header.slice(7) : "";
if (!token) return res.status(401).json({ error: "missing token" });
try {
(req as any).principal = await client.verifyAccessToken(token);
next();
} catch (err) {
res.status(401).json({ error: "invalid token" });
}
}What verifyAccessToken returns
type VerifiedPrincipal = {
id: string; // gid
subject: string; // sub
email?: string;
name?: string;
scopes: string[];
role?: string;
raw: AccessClaims; // the full claims object
};Server usage (confidential)
A confidential server also runs the login. Pass the secret in the config.
import { BaseIdPServerClient } from "base-idp/server";
export const auth = new BaseIdPServerClient({
clientId: process.env.BASE_IDP_CLIENT_ID!,
secret: process.env.BASE_IDP_CLIENT_SECRET!,
});Authorize URL
const url = auth.authorizeUrl({
state: "return-to=/dashboard",
});
// Redirect the user to `url`.Exchange the code
const tokens = await auth.exchangeCode({
code: req.query.code,
codeVerifier: storedVerifier,
});
const principal = await auth.verifyAccessToken(tokens.access_token);Next.js adapter
For Next.js the adapter wraps these steps in two route handlers.
import { createNextBaseIdpAuth } from "base-idp/next";
const auth = createNextBaseIdpAuth({
clientId: process.env.BASE_IDP_CLIENT_ID!,
secret: process.env.BASE_IDP_CLIENT_SECRET!,
});
export const GET = (req: Request) => auth.login(req);import { createNextBaseIdpAuth } from "base-idp/next";
const auth = createNextBaseIdpAuth(
{
clientId: process.env.BASE_IDP_CLIENT_ID!,
secret: process.env.BASE_IDP_CLIENT_SECRET!,
},
{
async onCallback({ principal, tokens, state }) {
// Persist your own session cookie here.
const response = Response.redirect(state ?? "/");
response.headers.append(
"Set-Cookie",
`session=${await mint(tokens, principal)}; HttpOnly; Path=/; Secure`,
);
return response;
},
},
);
export const GET = (req: Request) => auth.callback(req);React hooks
The React adapter exposes hooks for components that need the current principal.
"use client";
import { BaseIdpProvider, useBaseIdpSession } from "base-idp/react";
export function App({ children }) {
return (
<BaseIdpProvider clientId={process.env.NEXT_PUBLIC_BASE_IDP_CLIENT_ID!}>
{children}
</BaseIdpProvider>
);
}
function Header() {
const { principal, isLoading, signIn, signOut } = useBaseIdpSession();
if (isLoading) return <Spinner />;
if (!principal) return <button onClick={signIn}>Sign in</button>;
return (
<>
<span>{principal.email}</span>
<button onClick={signOut}>Sign out</button>
</>
);
}Config
The base config type is the same across entry points. Each entry point may add a few fields of its own.
type BaseIdPConfig = {
clientId?: string;
secret?: string;
issuer?: string;
key?: string; // legacy alias for clientId
fetch?: FetchLike; // override the fetch implementation (Cloudflare Workers, etc.)
};clientId
Required for any operation that needs to talk to Base IdP (authorize URL, token exchange, client-config). Optional for pure offline verification, where it pins the audience.
secret
Server-side only. Required for the token exchange step on confidential clients.
issuer
Defaults to the production issuer. Override only for local development.
fetch
Useful for environments without a global fetch (older Node, Workers,
Deno). Pass any function that matches the fetch signature.
CLI
base-idp create, init, test, and token are part of this package. See
the CLI overview for details.