Skip to main content

Protocol

Message envelope

Every message is a JSON-serializable object of the following shape:

interface PrivateKitMessage {
type: PRIVATE_KIT_MESSAGE_TYPES;
payload: {
connectionId: string;
// ...event-specific fields
};
}

See Message Contracts for the exact payload shape of each type.

connectionId

connectionId is a UUID generated by the iframe at mount time and sent to the host in PRIVATE_KIT_INIT. It is the canonical handle for this particular iframe instance:

  • The host must echo it in every outgoing message (PRIVATE_KIT_UPDATE_USERNAME).
  • The iframe silently drops any inbound message whose payload.connectionId does not match the one it generated.
  • Hosts embedding multiple iframes simultaneously must track and route by connectionId to avoid cross-talk.

If the host remounts the iframe, a new connectionId is generated. Do not reuse a stored one.

authToken

Unlike Web3Kit, PrivateKit does not read the user's token from its URL. Every action message that requires authentication (PRIVATE_KIT_UPDATE_USERNAME, PRIVATE_KIT_UPDATE_EMAIL, PRIVATE_KIT_CONFIRM_EMAIL, PRIVATE_KIT_RESEND_EMAIL_CODE, PRIVATE_KIT_UPDATE_PHONE, PRIVATE_KIT_CONFIRM_PHONE, PRIVATE_KIT_RESEND_PHONE_CODE, PRIVATE_KIT_UPDATE_PASSWORD) carries the private authToken in its payload. The iframe forwards it as Authorization: Bearer <token> to the auth-central API.

  • The host must use the user's private token (the JWT that grants access to /private/api/v1/users).
  • Each action message can use a fresh token. After receiving PRIVATE_KIT_AUTH_TOKEN_401, the host refreshes the token and re-sends the same action — there is no separate "set token" handshake.

Directions

DirectionMessage types
iframe → parentPRIVATE_KIT_INIT, PRIVATE_KIT_USERNAME_UPDATED, PRIVATE_KIT_USERNAME_VALIDATION_ERROR, PRIVATE_KIT_EMAIL_UPDATED, PRIVATE_KIT_EMAIL_VALIDATION_ERROR, PRIVATE_KIT_EMAIL_CONFIRMED, PRIVATE_KIT_EMAIL_CONFIRMATION_ERROR, PRIVATE_KIT_EMAIL_CODE_RESENT, PRIVATE_KIT_PHONE_UPDATED, PRIVATE_KIT_PHONE_VALIDATION_ERROR, PRIVATE_KIT_PHONE_CONFIRMED, PRIVATE_KIT_PHONE_CONFIRMATION_ERROR, PRIVATE_KIT_PHONE_CODE_RESENT, PRIVATE_KIT_PASSWORD_UPDATED, PRIVATE_KIT_PASSWORD_VALIDATION_ERROR, PRIVATE_KIT_AUTH_TOKEN_401
parent → iframePRIVATE_KIT_UPDATE_USERNAME, PRIVATE_KIT_UPDATE_EMAIL, PRIVATE_KIT_CONFIRM_EMAIL, PRIVATE_KIT_RESEND_EMAIL_CODE, PRIVATE_KIT_UPDATE_PHONE, PRIVATE_KIT_CONFIRM_PHONE, PRIVATE_KIT_RESEND_PHONE_CODE, PRIVATE_KIT_UPDATE_PASSWORD

Origin handling

  • The iframe currently posts with targetOrigin: '*'. This will be tightened to a whitelist once allowed host origins are fixed.
  • The host must filter inbound messages by event.origin === PRIVATEKIT_ORIGIN regardless of what the iframe sends.
  • When the host posts to the iframe via iframeRef.current.contentWindow.postMessage(...), set the second argument to PRIVATEKIT_ORIGIN in production. '*' is acceptable only during local development.

Ordering

Within a single iframe lifetime the iframe always emits PRIVATE_KIT_INIT first. After that, every host-initiated action message produces exactly one outbound message from the iframe. Possible exchanges:

Action messageIframe response
PRIVATE_KIT_UPDATE_USERNAMEone of USERNAME_UPDATED, USERNAME_VALIDATION_ERROR, AUTH_TOKEN_401
PRIVATE_KIT_UPDATE_EMAILone of EMAIL_UPDATED, EMAIL_VALIDATION_ERROR, AUTH_TOKEN_401
PRIVATE_KIT_CONFIRM_EMAILone of EMAIL_CONFIRMED, EMAIL_CONFIRMATION_ERROR, AUTH_TOKEN_401
PRIVATE_KIT_RESEND_EMAIL_CODEone of EMAIL_CODE_RESENT, EMAIL_VALIDATION_ERROR (limitReached / unknown), AUTH_TOKEN_401
PRIVATE_KIT_UPDATE_PHONEone of PHONE_UPDATED, PHONE_VALIDATION_ERROR, AUTH_TOKEN_401
PRIVATE_KIT_CONFIRM_PHONEone of PHONE_CONFIRMED, PHONE_CONFIRMATION_ERROR, AUTH_TOKEN_401
PRIVATE_KIT_RESEND_PHONE_CODEone of PHONE_CODE_RESENT, PHONE_VALIDATION_ERROR (limitReached / unknown), AUTH_TOKEN_401
PRIVATE_KIT_UPDATE_PASSWORDone of PASSWORD_UPDATED, PASSWORD_VALIDATION_ERROR, AUTH_TOKEN_401

The host may repeat any action within the same iframe instance (e.g. after a 401-and-refresh, or after the user types a different value). The email-change and phone-change flows follow the same two-step shape: UPDATE_**_UPDATED → user copies the verification code → CONFIRM_**_CONFIRMED, with RESEND_*_CODE available between the first and last step. Password change is single-step — UPDATE_PASSWORDPASSWORD_UPDATED directly, no out-of-band verification.

Logging

When the auth-central app is built with appConfig.demo.available = true, both sides emit verbose console.log traces:

  • Iframe side: prefix [private-kit], with direction tag in / out.
  • Reference host (DemoPrivateKitTestPage): prefix [private-kit-demo], also tagged in / out.

In production builds (demo.available = false) all logging is a no-op.