> ## Documentation Index
> Fetch the complete documentation index at: https://docs.tai42.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Author an identity provider

> Resolve an inbound token into an authenticated identity.

Author an identity provider plugin — the piece that answers one question for the
[access-control](/concepts/access-control) gate: *who is this token?*

An identity provider validates an opaque inbound credential and returns the
caller's identity, or `None` if the token is not its own. It depends on
`tai42-contract` only and never imports the skeleton: the contract's
`IdentityProvider` base and the module-level registry are the entire surface it
touches.

## Implement the contract

Subclass `IdentityProvider` and implement `validate_token`. Return an
`AuthIdentity` (a `user_id` plus a claims dict) for a token you recognise, and
`None` for one you do not — never raise on an unrecognised token. Raise only on a
backend failure: the gate treats a raised error as fail-closed, while `None`
falls through to the next provider in the chain.

```python theme={null}
from tai42_contract.access_control import AuthIdentity, IdentityProvider

class MyIdentityProvider(IdentityProvider):
    async def validate_token(self, token: str) -> AuthIdentity | None:
        claims = await self._verify(token)   # your own store / issuer
        if claims is None:
            return None                       # not mine -> fall through
        return AuthIdentity(user_id=claims["sub"], claims=claims)
```

Two optional overrides let core health-check your backing store without naming
it:

* `healthcheck` — probe your own storage and raise loudly on failure. The
  skeleton awaits it at startup for whatever provider is active, so a broken
  backend fails the boot rather than the first request. A provider that
  validates over HTTP against an external issuer has nothing to probe and
  inherits the no-op.
* `readiness_targets` — declare the backing store(s) core's `/ready` probe
  should ping on your behalf. Core dedupes a shared store against every other
  subsystem's connections, so a store you share is pinged once.

## Minting keys is a separate interface

A validate-only provider — an OIDC/JWT verifier, for instance — implements the
plain `IdentityProvider` above and mints nothing. A provider that also **issues**
API keys implements `ApiKeyIdentityProvider`, adding `provision` (and the key
lifecycle methods), which writes the key→identity record into the provider's
**own** storage.

Ownership is a mint-path guarantee the gate enforces centrally: `provision`
records the owning account via the `OWNER_USER_ID_CLAIM`, and the verifier
**strips** that claim from any provider that is not an `ApiKeyIdentityProvider`.
An external issuer can therefore never assert key ownership it did not mint — you
do not have to defend against it, the gate does.

## Register on import

Register the factory through the module-level registry at import — no app handle,
no decorator that needs the skeleton:

```python theme={null}
from tai42_contract.access_control import register_identity_provider

register_identity_provider("my-identity", lambda settings: MyIdentityProvider(settings))
```

Importing the module registers the provider; the application **activates** it
only when its name appears in the configured chain. Registration alone activates
nothing, and a duplicate name raises loudly.

## Load it

The host loads the provider by naming its module under `lifecycle_modules` and
listing its registration name in the ordered
[`ACCESS_CONTROL_AUTH_PROVIDERS`](/concepts/access-control) chain. The gate tries
each provider in order, first match wins. Document your provider's own settings
(issuer URLs, audiences, store connections) in your repository's README — that
impl-specific configuration lives in your repo, not on this site.

## Shipped implementations

Shipped identity providers appear in the [ecosystem
catalog](/reference/catalog), each linking to its own repository.

## See also

* [Access control](/concepts/access-control) — the gate, multi-provider
  resolution, and attenuation.
* [Author an accounts provider](/guides/authors/accounts-provider) — the human
  side, which is also an identity provider.
* [Python SDK reference](/reference/python-sdk/index) — the contract packages a
  plugin builds against.
