# Tuurio ID TYPO3 extension provisioning contract (Direct API 1.0.0)

This is the reviewed provisioning contract for the Tuurio ID TYPO3 extension. It provisions
Tuurio ID through the public direct provisioning API, performs the authorization-code flow with
PKCE for backend and frontend users, and never exposes provisioning credentials to browser code.

## Compatibility

Use `https://id.tuurio.com/cli/v1/provisioning`; the `/cli/v1` path is retained for compatibility
and does not require executing a CLI. Before starting, call `GET /capabilities`, require
`contractVersion: "20260815"` or a compatible later version, `directApiSupported: true`, and
`supportedFrameworks` containing `TYPO3`.

`manage-tuurio-id@1.7.0` supports TYPO3 through `init --framework typo3` and derives these exact
addresses from `--base-url`. The Direct API remains available for installations that cannot run Node.js.

## Exact TYPO3 addresses

Derive all values from TYPO3's canonical public installation base URL, preserving an installation
subdirectory and never inventing a host or wildcard:

- `deploymentBaseUrl`: `https://example.com/cms`
- `redirectUri`: `https://example.com/cms/tuurio-id/callback`
- `postLogoutRedirectUri`: `https://example.com/cms/`

The callback is served by the extension's installation-wide PSR-15 middleware. Use HTTPS in
production; cleartext is permitted only for explicit loopback development hosts. Tuurio requires
the three primary URLs together, requires them to use the same origin, and binds their exact values
to the browser handoff.

## Direct provisioning flow

1. An authorized TYPO3 administrator reviews the exact URLs and explicitly approves the action.
2. Start `POST /handoff/start` with `framework: "TYPO3"`, `campaign: "typo3_extension"`, a stable
   non-secret `flowId`, the three exact URLs, and `allowExistingClientReuse: true` only when the
   extension can retain an existing confidential-client secret securely.
3. Store `handoffId` and `deviceCode` only in short-lived protected server-side state. Open only
   the returned `verificationUriComplete`; the human completes sign-in, organization selection,
   and legal acceptance in that Tuurio-controlled browser flow.
4. Poll `POST /handoff/poll` serially and no more frequently than `pollIntervalSeconds`. An
   `APPROVED` result contains the short-lived provisioning session token.
5. If `selectedClientRecordId` is absent, call `POST /scaffold` with the bearer session token, a
   stable `Idempotency-Key`, one `TYPO3` framework, and the same browser-approved addresses. If it
   is present, do not scaffold: call `PUT /clients/{clientRecordId}/redirect-uris` instead.
6. Treat `registeredClients` from scaffold, or `client` from the existing-client update response,
   as the only authoritative proof of the persisted redirect URI registrations.
7. Import only the allowlisted server-side `TUURIO_*` settings, validate discovery, then revoke
   the provisioning session through `POST /session/logout` and erase the protected handoff state.

The existing-client update endpoint accepts no address body. It applies only the exact addresses
approved in the same browser handoff, only to `selectedClientRecordId`, and is idempotent.
`existingClients[].clientRecordId` is the only value allowed in the endpoint path; never use
`existingClients[].id` or `existingClients[].clientId`, which are protocol client identifiers. A
TYPO3 client secret never authorizes this administrative change.

## Security requirements

- Never expose device codes, provisioning sessions, client secrets, or refresh tokens in frontend
  JavaScript, TypoScript, logs, telemetry, exports, or AI prompts.
- Use bounded HTTP timeouts, CSRF protection, and serialized polling.
- Only an authorized TYPO3 backend user may start, inspect, retry, import, or disconnect
  provisioning; protect each extension action with TYPO3's backend permission and CSRF controls.
- The extension is a confidential web client: complete the authorization-code flow with PKCE S256,
  transaction-bound `state`, `nonce`, one-time callback processing, and a server-side code
  exchange in the PSR-15 middleware.
- Cryptographically validate every initial and renewed ID token against the issuer's JWKS. Require
  an exact issuer, the configured client ID in `aud`, valid time claims, and the transaction nonce
  before using claims; fail closed and clear local session state on validation failure.
- Keep the confidential client secret in protected extension configuration, outside public files.
- Require an exact registered post-logout URI and send the validated ID token only as
  `id_token_hint` to the issuer's discovery-provided `end_session_endpoint`.
