Skip to main content
Use Ave Business when your app wants organization workspaces without building its own membership system. Ave owns the organization, member identities, roles, SSO requirement, signing authority, and encryption policy. Your app stores product data under the Ave organization ID. This is different from making every app maintain a separate workspace membership table. The app still owns app-specific data, but the question “is this identity an active member of this workspace?” comes from an Ave-issued organization-context token.

The model

Store Ave organization IDs as workspace IDs in your app:
type Workspace = {
  id: string;
  aveOrgId: string;
  name: string;
  plan: "free" | "pro" | "enterprise";
};

type Project = {
  id: string;
  workspaceId: string;
  name: string;
};
Ave manages:
ConcernSource of truth
Organization membershipAve Business
Member role and org scopesAve Business
Domain verification and SSO policyAve Business
App billing, projects, settings, feature flagsYour app
App-specific roles narrower than Ave accessYour app
If you need extra app permissions, layer them after Ave membership. Do not let an app-local role grant access when the Ave token no longer has organization context.

Choosing a workspace

Your app needs an Ave organization ID before it can request organization context. Common patterns:
  • After a normal Ave sign-in, call Ave’s organization lookup API with the returned access_token.
  • Let the user create a new Ave workspace from your app with the workspace creation API.
  • The organization admin links an Ave Business organization during app onboarding.
  • An invite or workspace URL contains the app workspace slug, and your app maps that slug to aveOrgId.
  • After a successful organization-context login, your app remembers the org_id as a recent workspace choice.
The remembered list is only navigation state. Do not authorize from it. Authorization still comes from a verified token that contains the same org_id.
import { listAveWorkspaceOrganizations } from "@ave-id/sdk";

const organizations = await listAveWorkspaceOrganizations(
  { clientId: "YOUR_CLIENT_ID" },
  tokens.access_token
);

const selected = organizations[0];
Use the returned name, slug, and logoUrl for the picker UI. Use the returned id as organizationId when starting the organization-context login.

Create a workspace from your app

Apps can create an Ave Business workspace for the signed-in identity. Before calling the endpoint, tell the user that this creates an Ave-managed workspace and that membership, SSO, and organization access will be managed through Ave.
import { createAveWorkspaceOrganization } from "@ave-id/sdk";

const organization = await createAveWorkspaceOrganization(
  { clientId: "YOUR_CLIENT_ID" },
  tokens.access_token,
  {
    name: "Example Co",
    userConfirmedAveWorkspaceCreation: true,
  }
);
The created identity becomes the owner of the new Ave workspace. Your app should store organization.id as the workspace’s aveOrgId, then start an organization-context login with that ID before allowing workspace actions.

Sign in to a workspace

When the user chooses a workspace, start the normal PKCE flow with organizationId.
import { startPkceLogin } from "@ave-id/sdk/client";

await startPkceLogin({
  clientId: "YOUR_CLIENT_ID",
  redirectUri: "https://app.example.com/callback",
  scope: "openid profile email offline_access",
  organizationId: workspace.aveOrgId,
});
Ave verifies that the selected identity is an active member of that organization before issuing tokens. If the organization requires enterprise SSO, Ave only issues organization-context tokens from a valid SSO session for that organization.
If you use signIn() and pass organizationId, the SDK uses the PKCE redirect flow. FedCM does not claim organization-context support.

Read workspace context

After the callback, verify the token on your server and extract the workspace context.
import {
  getAveWorkspaceContext,
  hasAveWorkspaceScope,
  verifyAveIdTokenFromAuthHeader,
} from "@ave-id/sdk/server";

export async function handleWorkspaceRequest(request: Request) {
  const principal = await verifyAveIdTokenFromAuthHeader(
    request.headers.get("Authorization"),
    { clientId: process.env.AVE_CLIENT_ID! }
  );

  if (!principal) return new Response("Unauthorized", { status: 401 });

  const workspace = getAveWorkspaceContext(principal.claims);
  if (!workspace) return new Response("Workspace context required", { status: 403 });

  if (!hasAveWorkspaceScope(workspace, "read")) {
    return new Response("Workspace access denied", { status: 403 });
  }

  return Response.json({
    identityId: principal.subject,
    aveOrgId: workspace.id,
    workspaceName: workspace.name,
    role: workspace.role,
    scopes: workspace.scopes,
  });
}
You can also call /userinfo with the opaque access_token. When the token has organization context, the response includes organization, and the SDK can convert it:
import { fetchUserInfo, getAveWorkspaceContextFromUserInfo } from "@ave-id/sdk";

const userInfo = await fetchUserInfo(config, tokens.access_token);
const workspace = getAveWorkspaceContextFromUserInfo(userInfo);

Route authorization

On every workspace request:
  1. Verify the id_token for your clientId, or verify your own app session created from it.
  2. Require auth_context: "organization".
  3. Require org_id to match the workspace being accessed.
  4. Check Ave org scopes for the action.
  5. Apply any app-local restrictions after the Ave checks pass.
Do not accept access_token_jwt as a generic app route token. Its audience is Ave’s resource audience, so it is for Ave APIs and Connector token exchange.
import { hasAveWorkspaceScope, requireAveWorkspaceContext } from "@ave-id/sdk/server";

const workspace = requireAveWorkspaceContext(principal.claims);

if (workspace.id !== routeWorkspace.aveOrgId) {
  return new Response("Wrong workspace", { status: 403 });
}

if (!hasAveWorkspaceScope(workspace, "manage_org")) {
  return new Response("Admin access required", { status: 403 });
}
Use the Ave sub claim as the acting identity ID. Use org_member_id for audit records when you want to show which organization membership authorized the action.

App-local roles

If your app has its own project roles, treat them as narrower permissions inside an Ave workspace:
const aveAllowed = hasAveWorkspaceScope(workspace, "read");
const appAllowed = await canReadProject(workspace.id, principal.subject, projectId);

if (!aveAllowed || !appAllowed) {
  return new Response("Forbidden", { status: 403 });
}
This lets an app support extra concepts like project owner, billing admin, or environment deployer without becoming the source of truth for workspace membership.

Revocation

When a member is removed from an Ave organization, refresh stops issuing organization-context tokens for that member. Existing short-lived tokens may remain valid until they expire. For sensitive actions, require a fresh token or an Ave signing flow. Do not cache workspace membership indefinitely in your app. Cache app metadata keyed by org_id, but make access decisions from verified current tokens.

Server-side sessions

For dashboards that keep tokens out of the browser, use a backend session with an HttpOnly cookie and store Ave refresh tokens server-side. On each protected workspace request:
  1. Load your app session.
  2. Refresh the Ave token if it is near expiry.
  3. If refresh returns access_denied, invalid_grant, or enterprise_sso_required, clear the app session and send the user back through Ave.
  4. Authorize from the freshly verified organization-context token.
Do not let the app cookie outlive Ave organization access by itself. Either cap the app session to the current Ave token expiry, or refresh with Ave before extending the app session. This keeps removed members from retaining long dashboard access through a cached app session.

Encryption and SSO

SSO proves the business identity is allowed to enter the organization. It is not an encrypted key delivery path. For standard business encryption, apps can use the organization context normally. For E2EE org mode, encrypted resources require identity-wrapped org key grants. If an SSO-created identity does not have an Ave identity encryption key and grant, it can still authenticate to the organization, but it cannot decrypt E2EE org material.

Personal and business side by side

Apps can keep personal accounts and Ave Business workspaces at the same time:
  • No organizationId: issue a normal personal identity session.
  • With organizationId: issue an organization-context session for that Ave workspace.
  • Store personal resources by sub.
  • Store business resources by org_id, with audit entries including sub and org_member_id.
This keeps membership centralized in Ave without blocking app-specific product structure.
Last modified on May 23, 2026