Farther ShoreDocs
Go to Farther Shore
Frontend SDK
Root & data components
Auth & sessions
Access-aware UI
Compose authentication and permission presentationHandle the actual callDo not duplicate the contractAvoid content flashes
Permission gates
Custom components
Variables
@farthershore/farthershore-js
Share with another member
Shared and private in one portal
@farthershore/farthershore-js exports
@farthershore/farthershore-js/react exports
@farthershore/farthershore-js/test-utils exports
@farthershore/farthershore-js/errors exports
@farthershore/farthershore-js/components exports
@farthershore/farthershore-js/components/docs-chrome exports
frontend-sdk HTTP contracts
Status
Docs/Build the customer UI/Access-aware UI

Access-aware UI

Present auth, role, plan, and limit state without duplicating gateway authorization.

A subscriber can be unable to use a control for different reasons. Keep those reasons separate so the UI offers the right remedy:

AxisQuestionUI responseSecurity boundary
AuthenticationIs there a current subscriber session?sign insession validation
Member permissionMay this member perform the action?hide, disable, or request accessgateway route permission
Plan/subscriptionDoes the subscriber's pinned plan grant the route and is access active?choose/upgrade/repair subscriptiongateway plan and subscription check
Dynamic limitIs this request within quota, rate, capacity, concurrency, spend, or adaptive limits?retry, reduce, queue, top up, or upgradegateway admission decision

The frontend can present observed state, but the route call is authoritative because plan, role, subscription, and usage can change after render.

Compose authentication and permission presentation

tsx
import {
  AccessDenied,
  PermissionGate,
  RequireAuth,
} from "@farthershore/farthershore-js/components";

export function CreateReportButton() {
  return (
    <RequireAuth fallback={<p>Sign in to create reports.</p>}>
      <PermissionGate
        permission="reports:create"
        mode="denied"
        fallback={<AccessDenied requiredPermission="reports:create" />}
      >
        <button>Create report</button>
      </PermissionGate>
    </RequireAuth>
  );
}

PermissionGate uses the server-resolved current member claim. It is a presentation primitive, not a client-side grant database.

Handle the actual call

tsx
import {
  FartherShoreApiError,
  LimitExceededError,
  retryWhileThrottled,
} from "@farthershore/farthershore-js";

async function createReport(input: unknown) {
  try {
    return await retryWhileThrottled(() => fs.route.post("/v1/reports", input));
  } catch (error) {
    if (error instanceof LimitExceededError) {
      showLimitNotice(error);
      return;
    }
    if (error instanceof FartherShoreApiError) {
      showStableDeny(error.code);
      return;
    }
    throw error;
  }
}

Only retry when the denial envelope says the request is retry-safe. Preserve an application idempotency key for writes and bound retry attempts. Quota/spend upgrade reactions and capacity reductions are not fixed by blind backoff.

LimitNotice maps the current limit class to the correct explanation. FsLimitBoundary can show a global prompt for a caught LimitExceededError. Use UpgradePrompt only for plan or funding remedies; a permission denial should offer access-request or administrator guidance instead.

Do not duplicate the contract

Plan grants are route/group refs in business/. Permission constraints and subject requirements also live on the route contract. Do not create a second hard-coded client feature map and treat it as authority.

It is reasonable to use bootstrap, entitlement, resource-limit, usage, and permission hooks to reduce dead-end interactions. Every mutation must still handle a deny from the gateway, and backend record queries must still use the verified organization.

Avoid content flashes

  • Hold protected content while auth is loading.
  • Hold permission-gated content until authzLoaded or usePermissionGate() resolves.
  • Do not optimistically reveal plan-restricted functionality from a stale local cache.
  • After plan, role, or organization changes, let the SDK invalidate/refetch its resources instead of manually mutating several copies of access state.

These rules keep the UI responsive without moving authorization into the browser.

PreviousAuth & sessionsNextPermission gates

On this page

Compose authentication and permission presentationHandle the actual callDo not duplicate the contractAvoid content flashes