> Markdown version of https://authpi.com/docs/sdks/idp-typescript/ — fetch the complete AuthPI docs index at https://authpi.com/llms.txt to discover all available pages.

# IdP SDK — TypeScript

Add AuthPI login to TypeScript backends with @authpi/idp — the OIDC authorization-code flow with PKCE, client_credentials for M2M, token refresh, and per-organization authorization checks.

[`@authpi/idp`](https://www.npmjs.com/package/@authpi/idp) is the official TypeScript SDK for authenticating against your AuthPI issuer: it drives the OIDC authorization-code flow with PKCE, supports `client_credentials` for machine-to-machine auth, exchanges and refreshes tokens, and gives you an authenticated *agent* object with the caller's identity and per-organization permissions.

It runs on Node.js 18+, Bun, Deno, and Cloudflare Workers.

## Install

```bash
npm install @authpi/idp
```

## Initialize

```ts
import { IdpClient } from '@authpi/idp';

const idp = new IdpClient({
  issuerUrl: 'https://idp.authpi.com/i_4r8w2k9m5x1p7q3e6t0y2u4i8',
  clientId: 'cli_xxx',
  clientSecret: process.env.AUTHPI_CLIENT_SECRET, // omit for public clients (SPAs)
  redirectUri: 'https://app.example.com/callback',
});
```

`issuerUrl` is your issuer's URL (or your custom domain). Confidential clients pass a `clientSecret`; public clients omit it and rely on PKCE.

## The login flow

**1. Send the user to AuthPI.** `createAuthorizationUrl` generates the PKCE verifier, `state`, and `nonce` — store them in the user's session, then redirect:

```ts
const { url, codeVerifier, state, nonce } = await idp.createAuthorizationUrl({
  scopes: ['openid', 'profile', 'email'],
});

session.set('oauth', { codeVerifier, state, nonce });
redirect(url);
```

To issue tokens restricted to one organization, include `org` in the authorization URL options:

```ts
const { url, codeVerifier, state, nonce } = await idp.createAuthorizationUrl({
  scopes: ['openid', 'profile', 'email'],
  org: 'org_0kfz3m8q1w5e9r2t6y4u7i3o5',
});
```

**2. Handle the callback.** After checking that the returned `state` matches the one you stored, exchange the code for an authenticated agent. The exchange goes directly to the token endpoint over TLS, and PKCE binds it to the verifier from step 1:

```ts
const agent = await idp.exchangeCode(code, codeVerifier);
await session.set('tokens', agent.tokens);
```

**3. Use the agent.** It carries the user's identity and organization memberships, with an authorization helper that checks scopes within an organization (driven by the [`organizations` claim](/docs/concepts/multi-org-tokens)):

```ts
if (agent.hasAccessIn('org_0kfz3m8q1w5e9r2t6y4u7i3o5', 'write', 'projects')) {
  // the user can write to projects in that organization
}
```

## Sessions and refresh

Rebuild an agent from stored tokens on subsequent requests — `createAgent` refreshes expired access tokens automatically and hands you the rotated tokens through `onRefresh`:

```ts
const agent = await idp.createAgent(await session.get('tokens'), {
  onRefresh: async (newTokens) => {
    await session.set('tokens', newTokens);
  },
  onRefreshError: async (error) => {
    await session.destroy(); // session expired — send the user back to login
  },
});
```

Refresh tokens rotate on use, so always persist what `onRefresh` gives you. Refresh timing is configurable (`autoRefresh`, `refreshBufferSeconds`), and the agent exposes `expiresAt` / `expiresIn` / `isExpired()` for your own checks. Organization claims in refreshed tokens are recomputed at every refresh — membership changes propagate within the access-token TTL ([the propagation contract](/docs/guides/org-lifecycle)).

## Machine-to-machine

For server-to-server auth, use the `client_credentials` flow with an M2M client (or an agent identity) — no `redirectUri` needed:

```ts
const idp = new IdpClient({
  issuerUrl: 'https://idp.authpi.com/i_4r8w2k9m5x1p7q3e6t0y2u4i8',
  clientId: 'cli_m2m_xxx',
  clientSecret: process.env.AUTHPI_CLIENT_SECRET,
});

const agent = await idp.clientCredentials({ scopes: ['users:read'] });
agent.hasAccess('read', 'users'); // token-level scopes — no organizations
```

See [machine-to-machine auth](/docs/guides/m2m-auth) for choosing between M2M clients, org API keys, and agent identities.

## Next steps

- [Server-side auth with the TypeScript SDK](/docs/quickstarts/typescript-backend) — the full flow as a step-by-step tutorial
- [Token claims reference](/docs/reference/token-claims) — what's inside `agent.tokens`
- [Validate tokens in your API](/docs/guides/validate-tokens) — verifying these tokens in downstream services
- [Package on npm](https://www.npmjs.com/package/@authpi/idp) — full README, framework notes, and advanced options