XKOVA Docs

Member Browser and BFF Integration

The browser talks only to your same-origin backend. Your BFF owns provider callbacks, the XKOVA session, refresh rotation, and the tenant API credential. The browser receives a host-only application cookie and safe member projections. XKOVA member-session and identity-provider credentials remain server-side.

Package boundaries

SurfaceRuns inResponsibility
@xkova/sdkCustomer serverCustomer business APIs, including money movement, using bearer authorization and production mutation signatures.
@xkova/sdk/member-bffCustomer BFFProvider-proof transactions, Hosted Auth start and completion, XKOVA session exchange and rotation, exact logout, typed context, cookies, CSRF, and store contracts.
@xkova/memberBrowserOptional same-origin Consumer Xperience client for member self-service.

@xkova/member is not the foundational abstraction for Embedded Solution vendor adapters. Lumin, Q2, and Banno integrations may share internal client primitives while preserving the hosting, launch, bridge, and lifecycle rules of each platform.

The security boundary

browser
  -> first-party application cookie
  -> customer BFF
  -> provider-specific proof exchange
  -> server-held XKOVA member session
  -> XKOVA member APIs

The browser never receives the workspace API key, XKOVA member-session ID, XKOVA refresh token, provider proof, provider access token, or provider refresh token. Resource endpoints do not accept raw provider tokens, X-OIDC-ID-Token, X-End-User-Id, or arbitrary account-holder selectors.

The durable application cookie must be named with the __Host- prefix and use Secure, HttpOnly, Path=/, and SameSite=Strict. A separate short-lived SameSite=Lax flow cookie is permitted only where a cross-site OAuth or OIDC callback needs it. Use exact Origin checks and a session-bound CSRF token on same-origin mutations.

Start and complete Hosted Auth

Enable Hosted Auth in Console, select an existing same-workspace API principal, choose invite-only or open enrollment, and register your exact origin and callback. The BFF helper creates state and S256 PKCE material, stores the transaction, and asks XKOVA for the controlled customer-branded ceremony URL. Your application never handles the email code, TOTP code, or provider token.

import {
  XKOVAMemberBffClient,
  serializeXKOVAMemberSessionCookie,
} from '@xkova/sdk/member-bff';

const auth = new XKOVAMemberBffClient({
  baseUrl: process.env.XKOVA_API_URL,
  apiKey: process.env.XKOVA_API_KEY,
  publishableKey: process.env.XKOVA_PUBLISHABLE_KEY,
  requestSigner,
});

const start = await auth.beginHostedAuthorization({
  browserOrigin: 'https://app.example.com',
  redirectUri: 'https://app.example.com/api/auth/callback',
  transactionStore,
});

// At the registered callback:
const established = await auth.completeHostedAuthorization({
  callbackUrl: request.url,
  expectedState: flowCookieState,
  transactionStore,
  sessionStore,
  applicationData: { memberAuthPublishableKey: process.env.XKOVA_PUBLISHABLE_KEY },
});

Redirect only to start.authorizationUrl. At callback, the helper validates exact state, callback, and PKCE binding, consumes the one-time result, and stores the provider-neutral session. Keep established.session and established.reference server-side.

Platform-enabled sandbox demos

Quick Demo is a narrow sandbox admission capability, currently available only to the explicitly platform-provisioned Plover application. It is not ordinary-member impersonation and cannot be enabled by supplying a holder, email, wallet, or provider subject. Ordinary customer applications use the Hosted or FI sign-in flows above and below.

const admitted = await auth.completeSandboxDemoAuthorization({
  state: start.state,
  transactionStore,
});
// Redirect to admitted.callbackUrl with the original flow cookie.
// Complete it using completeHostedAuthorization as above.

The SDK reads the existing Hosted transaction from its confidential store and validates the exact callback, state, result code, and expiry. The API verifies the registered principal and application, current policy versions, sandbox workspace, and login-only transaction purpose before exclusively leasing a platform test persona. Production workspaces and Account Center transactions are denied. Preserve the same reservation and state after an ambiguous response; repeating the operation uses the same idempotency key and persona lease. The SDK does not automatically retry admission.

Start and complete FI OIDC

The BFF helper creates state, nonce, and PKCE material and stores the transaction by a one-way state hash. Callback completion serializes one winner, stages the confidential provider proof with a stable XKOVA exchange attempt and application-session reference, and retains a bounded completed record. Retrying the identical callback can therefore recover after an ambiguous response or store acknowledgement without reusing the provider authorization code. Your code performs the FI-specific authorization-code exchange and returns the proof to the helper. XKOVA verifies it against the registered application's pinned issuer, audience, provider kind, and namespace.

const start = await auth.beginAuthorization({
  proofKind: 'oidc',
  browserOrigin: 'https://app.example.com',
  redirectUri: 'https://app.example.com/api/auth/callback',
  transactionStore,
});

Preserve the flow cookie and durable transaction when a retriable infrastructure failure escapes callback completion. Only XKOVAMemberAuthorizationTerminalError marks that exact flow unusable and permits the BFF to clear the cookie and start again.

The publishable key is safe to include in public application configuration because it grants no authority. The BFF still presents the confidential workspace bearer credential, and production mutations still require asymmetric request signing.

Read security setup before starting an action

After resolving the confidential session, read resolved.resources.self.actionReadiness(). This uses the existing session endpoint and performs no enrollment, provider request, action creation, or grant consumption. Hosted members receive distinct states for first-passkey setup, recovery-code acknowledgement, lost-credential recovery, and ready setup. FI-managed members are reported as externally managed; do not send them to Hosted enrollment.

The browser companion exposes getActionReadiness() through your same-origin BFF. Keep dependent action controls disabled while readiness is unknown and show the next setup step. Refresh after Account Center return, credential changes, and session rotation. A ready projection is not an authorization grant: every sensitive action still needs fresh proof, current permission, wallet readiness, policy checks, and its actual settlement result. Provider health is explicitly not_checked, not assumed healthy.

Session storage and rotation

Implement XKOVAMemberSessionStore and the authorization transaction store with your existing durable database or shared session service. Its per-reference exclusivity method must serialize refresh and logout across every BFF instance. A process-local lock is suitable only for one-process development. Encrypt and access-control refresh material and authorization transactions because a pending transaction can briefly contain a provider proof.

For an embedded or other confidential proof that does not use the browser callback helper, call establishSession with a stable customer-generated exchangeId and an encrypted XKOVAMemberProofExchangeTransactionStore. Persist that identifier before the first attempt and reuse it for recovery so the same XKOVA operation attempt and application-session reference are retained.

Refresh credentials are single-use. The API retains append-only lineage so replay or concurrent reuse revokes the refresh family. Session validation also enforces idle and absolute expiry, exact tenant, workspace, application, and account-holder binding, application configuration version, session policy version, membership status, and explicit revocation. Logout revokes the exact session unless the caller deliberately requests the broader server-side scope.

The browser client

import { createMemberClient } from '@xkova/member';

const member = createMemberClient({ basePath: '/api/member' });
const session = await member.getSession();
const wallets = session ? await member.listWallets() : [];
if (session && wallets.length === 0) {
  await member.ensureWalletProvisioning();
}

The initial browser package covers auth launch and status, enrollment, safe profile and avatar, balances, transaction history, consents, data exports, verification, wallet readiness and self-wallet operations, and Account Center handoff. It intentionally excludes contacts, payments, payment requests, escrows, bank transfers, ramps, treasury, tokenization, and compliance decisions.

Those excluded business operations remain available to a Consumer Xperience backend through the customer-server SDK when they are present in the reviewed customer operation manifest. Excluding them from browser JavaScript does not remove the product capability.

Self-wallet safety

Member self-wallet routes derive account-holder and provider identity from the active XKOVA session. The browser can list its wallets, read readiness, and send a bodyless request that wakes or resumes durable provisioning. It cannot name another account holder or provider account, submit an address or chain, mint a raw provider credential, or register an arbitrary wallet.

Member signing still occurs in the controlled signing UI after a separate exact human-action ceremony. XKOVA gives that page a public Thirdweb client selector and one fresh 60-second custom JWT for the canonical account-holder wallet. The provider session is memory-only and disposed immediately, but Thirdweb does not restrict it to the XKOVA business purpose. XKOVA freezes the payload and accepts or relays only the exact authorized signer, action, and signature. This bootstrap is never exposed as an ordinary member or customer API.

Hosted Auth and BYO OIDC

Keep the entry points visibly distinct. BYO OIDC is the primary enterprise model and redirects to the financial institution's IdP. Hosted Auth is an optional XKOVA-managed alternative. Its controlled page presents tenant branding, six-digit email-code sign-in, and an existing TOTP challenge when required. Customer applications must not recreate those ceremonies on their own origins. Hosted members use the tenant-branded Account Center for passkey lifecycle, Hosted session management, recovery-code readiness, and approved lost-all-factor recovery. Both identity models converge into the same provider-neutral XKOVA member-session model without exposing durable credentials to browser JavaScript.

Purpose-Bound Browser Flows

Payment signing, escrow signing, claims, approvals, and verification are narrow flows. For wallet signing, the BFF freezes the exact action first. Hosted members prove a fresh Account Center passkey. FI/BYO OIDC members prove the configured fresh external assurance profile. The one-use result becomes an opaque signing session, never URL bearer authority or a reusable member session.

  • Payment and escrow signing. Render the exact typed data returned by the signing session, establish one ephemeral provider ceremony for the canonical member wallet, and submit the resulting signature through the purpose-bound flow.
  • Claims. The path token selects one claim and the recipient completes the required recipient-bound verification before release.
  • Approvals and verification. Follow the controlled handoff and return contract. Do not reinterpret these capabilities as general API credentials.

From a Pending Payment to Member Signing

Creating a payment does not grant its creator control of the payer's wallet. POST /v1/payments intentionally returns a pending resource, not a bearer signing_url. Changing channel cannot grant signing authority.

  1. In Console, open Settings, Member Auth (/settings/member-auth). Register a Hosted application on your workspace, bound to your confidential BFF API principal. Register the exact HTTPS application origin and separate login, Account Center and member-action callbacks. Grant only the permission families needed by the server operations, including member_auth and payment for this flow. A payment-only key cannot establish member sessions. Add wallet or funding permissions only when those operations are needed.
  2. Use beginHostedAuthorization and completeHostedAuthorization above. In open enrollment, the verified subject creates its exact workspace membership. Invite-only enrollment requires the configured invitation process first. Take established.session.accountHolderId from the server result. Do not create an unrelated bare holder and attach an email later.
  3. Provision and fund that member's wallet, accept required policies, and use that holder and wallet when creating the payment. Keep the payment ID for the same authenticated payer. A machine can initiate the payment, but cannot impersonate the payer to sign it.
  4. Hosted members complete passkey enrollment and save recovery codes in Account Center. A lost code display can be resumed with the existing passkey. member_security_setup_required means finish that setup, not repeatedly log in.
  5. The BFF starts the public member-action API through the helper:

Preserve the original business review before a setup detour. The browser companion's createMemberActionReference(), saveActionDraft() and getActionDraft() retain an opaque review reference. The BFF uses resolveActionIntents() with its encrypted, replica-serialized intent store. Its create() freezes reviewed input without a business mutation; explicit prepare() uses one stable idempotency key and persists the exact payment or escrow resource. Repeated initiation does not create another resource. Changed input is a conflict. Review expiry is one hour, capped by the application session; an expired or missing review never silently starts a replacement action.

const action = await auth.beginMemberAction({
  resourceType: 'payment',
  resourceId: paymentId,
  action: 'sign',
  browserOrigin: 'https://app.example.com',
  returnUri: 'https://app.example.com/api/member-actions/callback',
  applicationReturnUri: 'https://app.example.com/payments/complete',
  sessionReference: authenticatedApplicationSessionReference,
  sessionStore,
  transactionStore,
});
// Keep action.state in a short-lived Secure, HttpOnly, SameSite=Lax flow cookie.
// Hosted applications redirect to action.authorizationUrl when
// action.authorizationMethod === 'hosted_passkey'.

// At the registered member-action callback:
const completed = await auth.completeMemberAction({
  callbackUrl: request.url,
  expectedState: flowCookieState,
  sessionStore,
  transactionStore,
});
const exchange = new URL('/api/member-action/exchange', completed.signingUrl);
exchange.searchParams.set('code', completed.signingExchangeCode);
if (completed.applicationReturnUri) {
  exchange.searchParams.set('return_to', completed.applicationReturnUri);
}
// Clear the flow cookie and return an HTTP 303 to exchange with
// Cache-Control: no-store and Referrer-Policy: no-referrer.

The helper calls POST /v1/member-actions with the registered principal, application selector and exact server-held member session. The controlled signing surface exchanges the one-use result, obtains the frozen payload and submits the wallet signature. These are not guessed /members, /apps or per-payment /sign endpoints. Integrations must not substitute internal signing APIs for the controlled ceremony. FI OIDC uses the same member-action helper with its registered external proof and exchangeProviderCode callback instead of a Hosted passkey.

Subscribe the webhook endpoint in the same tenant and workspace as the payment. A payment in a separate demonstration tenant cannot prove delivery to your tenant's endpoint. Verify the signature on raw request bytes before parsing JSON, using verifyWebhookSignature from @xkova/sdk/webhook. Its inputs are rawBody, the XKOVA-Webhook-Signature header and the endpoint secret. Do not reserialize a captured JSON object or try alternate signing schemes. Check payment.confirmed only after this exact payment actually reaches confirmed.

Embedded Solution note

In Embedded Solution, the institution's digital channel already authenticated the member. Lumin and Q2 launch proofs remain rejected until an exact vendor contract is registered and implemented. Banno's standards-based OIDC proof can converge through the registered JWT proof exchange. The Integration Gateway, not the browser or identity adapter, retains core credentials, raw core mappings, and authoritative fiat posting responsibility.

Related

See Authentication, SDK Overview, Wallets and Balances, and the interactive customer reference.