Skip to content
HaneDOCS日本語

Build apps

Authentication

API keys, service accounts, access tokens, token exchange with the user's id_token, and scopes and roles.

This page explains the credentials an app uses to call Hane’s public API, and the token exchange that lets it call on behalf of a user. Every credential is sent as Authorization: Bearer. The organization, project, and environment are not part of the URL; they are determined by the credential.

Credential Format Lifetime Used for
API key hane_<env>_… Set when issued (no expiry if omitted) Calling as a service account: batch jobs, CI, the BFF’s token exchange
Access token Issued by Hane through token exchange 15 minutes Calling as a user: the BFF, the console, hanectl
IdP id_token Issued by your IdP Depends on the IdP Input to token exchange, and APIs used before entering an organization (such as GET /v1/auth/me)

A service account is a caller that is not a person. It is bound to one project and environment, and its scopes are set when you create it. Scopes cannot be changed later; if they are not enough, create a new service account.

const sa = await unwrap(
admin.POST("/v1/platform/service-accounts", {
body: { name: "fleet-app", project: "fleet", environment: "dev", scopes: ["data:records.read", "agents:run"] },
}),
);
const key = await unwrap(
admin.POST("/v1/platform/service-accounts/{id}/keys", {
params: { path: { id: sa.sa_id } },
body: { expires_at: "2027-03-31T00:00:00Z" },
}),
);
// key.secret is returned only in this response
  • Issuing and revoking API keys requires platform:keys.manage. You can also do this in the console under Access → Service accounts and API keys.
  • Revoke a key with DELETE /v1/platform/service-accounts/{id}/keys/{keyId}. It is refused from the next request.
  • If you did not receive the response when issuing a key, revoke it and issue a new one.

POST /v1/auth/token exchanges an IdP id_token for an access token bound to an organization, project, and environment. It follows the OAuth 2.0 Token Exchange format.

const t = await unwrap(
anon.POST("/v1/auth/token", {
body: {
grant_type: "urn:ietf:params:oauth:grant-type:token-exchange",
subject_token: idToken,
subject_token_type: "urn:ietf:params:oauth:token-type:id_token",
actor_token: process.env.HANE_API_KEY!,
actor_token_type: "urn:hane:params:token-type:api_key",
organization: "acme",
project: "fleet",
environment: "dev",
},
}),
);
// t.access_token, t.expires_in, t.context.scopes
  • When you pass your app’s API key as actor_token, the user becomes the principal and the app’s service account becomes the actor. The scopes are the intersection of the user’s and the app’s scopes.
  • The API key must belong to the same organization, project, and environment as the exchange, or the request fails with FORBIDDEN. If the API key has an expiry, the access token cannot be used past it.
  • If the user is not a member of the organization, the request fails with FORBIDDEN. If you pass project, environment is also required.
  • The response’s context contains the organization, project, environment, user ID, roles, groups, and scopes. You can use it to decide what to show on screens.

Scopes have the form <service>:<resource>.<action>. You can also grant a whole service at once, such as data:*.

Service Main scopes
Data data:schemas.read, data:records.read, data:records.write, data:values.confirm, data:import
Definitions definitions:read, definitions:apply, definitions:release
Workflows workflows:read, workflows:operate, workflows:event-subscriptions.manage
Approvals approvals:tasks.read, approvals:tasks.decide
Documents documents:read, documents:write, documents:download, documents:delete
Agents agents:read, agents:run
Communication communication:read, communication:contacts.write
Organization platform:read, platform:members.manage, platform:projects.manage, platform:keys.manage
Audit and usage audit:read, usage:read

The reference app’s service account holds data read and write, value confirmation, import, Workflow read, Task read and decide, document read and write, and agent read and run.

Organization members get scopes through roles.

Role What it can do
owner Everything
admin All scopes
developer Read, plus writing data, applying and activating definitions, running agents, and more
operator Read, plus writing data and confirming values, deciding Tasks, writing documents, and more
viewer Read only

Roles are set when a member is invited, and can also be added to people, groups, and service accounts with a RoleBinding manifest. Whenever roles, groups, or API keys change, the policy epoch goes up and tokens from the old epoch are refused (POLICY_EPOCH_STALE). Exchange the token again when that happens.