> **Can't find what you're looking for?** Use `search_docs` on the docs MCP server at `https://mpp.dev/api/mcp` to find what you need.

# Your own identity provider \[Reuse your login or protocol]

Use your own identity provider in one of two ways:

* **Connect your identity provider** when callers already have an API key, session, or SSO login from you. The server checks it before payment.
* **Build a protocol** when the client must prove something new. You write both sides: what the client sends and how the server checks it.

## Connect your identity provider

Wrap your existing check in [`Identity.from`](/sdk/typescript/core/Identity.from). Callers your identity provider accepts continue to payment; everyone else is rejected before they see a price.

```ts twoslash [server.ts]
import * as Identity from 'mppx/identity'
import { Mppx, tempo } from 'mppx/server'

declare const customers: ReadonlyMap<string, { customerId: string }>

// [!code hl:start]
const customer = Identity.from({
  challenge: { 'WWW-Authenticate': 'ApiKey' },
  verify(request) {
    const key = request.headers.get('X-Api-Key')
    if (!key) return undefined
    const identity = customers.get(key)
    if (!identity) throw new Identity.Rejection('Unknown API key.', { status: 'forbidden' })
    return identity
  },
})
// [!code hl:end]
const mppx = Mppx.create({ methods: [tempo.charge()] })
export const handler = mppx.charge({ amount: '0.05', identity: [customer] })
```

### Details

* **No credential:** return `undefined` or `null`. The caller gets a `401`.
* **Rejected credential:** throw [`Identity.Rejection`](/sdk/typescript/core/Identity.Rejection) with `status` `invalid` (`401`), `forbidden` (`403`), or `unavailable` (`503`). Other errors propagate.
* **Reading the identity:** use `result.identity?.get(customer)` in a successful handler.
* **Combining with email:** `identity: [Identity.anyOf(email, customer)]` accepts either; a plain list requires both.

## Build your own protocol

Third-party identity protocols implement the `Identity.Verifier` and `Identity.Prover` types from `mppx/identity`.

* **Verifier:** `verify(request)` returns `{ status: 'valid', value }` or a [rejection status](/extensions/identity#how-identity-checks-run) with a `reason` and optional `headers`.
* **Prover:** `prove(request, context)` returns the proved request for origins in `scope`. `context` has a fresh nonce, a timestamp, and on a paid retry the Challenge.
* **`prepare`** (optional): runs on a `402` before the paid retry. Its `requirements` list the 402's `Identity-Required` entries.
* **`retry`** (optional): returns headers to resend a rejected non-402 request once. Paid attempts are never resent.
* **`headers`** (optional): sensitive request headers, stripped on cross-origin redirects and never proxied upstream.

```ts twoslash [identity.ts]
import type * as Identity from 'mppx/identity'

declare function signRequest(request: Request, context: Identity.ProveContext): Promise<Request>

export const prover: Identity.Prover = {
  // [!code hl:start]
  prove: (request, context) => signRequest(request, context),
  scope: ['https://api.example.com'],
  // [!code hl:end]
}
```

Outside `Mppx.create`, wrap any Fetch implementation with `Identity.Client.wrapFetch`. Prefer `Mppx.create({ identity })`, which also passes the payment Challenge to provers.

```ts twoslash [client.ts]
import * as Identity from 'mppx/identity'

declare const prover: Identity.Prover

const fetch = Identity.Client.wrapFetch(
  globalThis.fetch,
  Identity.Client.from([prover]),
)

await fetch('https://api.example.com/resource')
```
