Configure
app: {
identity: {
scopes: ["user:basic", "accounts:read", "teams:read"],
},
}The scopes are frozen into the exact deployment. A repository cannot request a scope that is not in this v1 vocabulary.
Use
import { forge } from "/.forge/client.js";
const user = await forge.identity.currentUser();
if (!user) forge.identity.login();
const accounts = await forge.identity.listAccounts();
const teams = await forge.identity.listTeams();
await forge.identity.logout();Use the account control
<div id="account"></div>
<script type="module">
import { forge } from "/.forge/client.js";
const control = forge.identity.mountAccountControl("#account");
control.addEventListener("forge-account-select", ({ detail }) => {
console.log(detail.account);
});
</script>The framework-neutral control handles sign-in, session state, account and team display, and sign-out. Account selection is an app-local event; it never changes the resource owner or service tier.
Server-side
const user = env.forge.identity.currentUser();
const account = env.forge.identity.requireAccountRole(
"acct_org_example", ["owner", "admin"]
);The trusted hosting layer projects only the app-scoped principal. On a connected custom domain, Forge publishes a wrapper backed by a private release-scoped service binding. Repository code never sees the binding, its tenancy props, or the raw session cookie.
Security boundary
- Authorization uses a 60-second, single-use code with S256 PKCE and an exact callback origin.
- The app receives an app-scoped random user ID, not Forge's global user ID.
- The trusted Sites edge or connected-Worker wrapper owns a host-only, Secure, HttpOnly session cookie and strips it before application dispatch.
- Connected Identity requires the exact active custom domain, project, release, provider version and target generation; provider previews and path-only routes fail closed.
- Account, tier, suspension, and team membership generations invalidate stale sessions.
- Control-plane bearer tokens, platform secrets, D1, and Cloudflare credentials never reach repository code.
Visitors can review connected apps and end their active sessions from Forge Settings. Reauthorization preserves the same app-specific identity. Forge owns this provider-neutral session contract; Cloudflare Access or OIDC may authenticate upstream but is not exposed to the app as Forge Identity.
Current v1 limits
Sessions have a 12-hour hard lifetime and no refresh token. The shared Sites preview domain remains a controlled-preview surface. Product-specific authorization remains the application's responsibility.