Contexts and tokens
Every File Service call acts in one context (a tenant and one of its applications) and carries a token for the Files API. This page explains both, and which combinations the service accepts. It is for backend developers and for anyone reviewing the security model.
The context is in the URL
Section titled “The context is in the URL”Every route about files names its context in the path: /tenants/:tenant_id/applications/:application_id/…. This site writes that prefix as P, so P/uploads is /tenants/:tenant_id/applications/:application_id/uploads under https://files.centralcodestation.com/api/v1.
Everything lives inside one context: folders, files, settings, quotas, share links and the audit log. An id from another context answers 404, as if it did not exist. Two applications of the same tenant share nothing but the tenant’s quota.
A few routes are about the caller or the tenant instead:
| Route | Answers |
|---|---|
GET /me |
The signed-in user, and whether the Identity Server reports them as a platform administrator |
GET /me/tenants |
The user’s active tenants |
GET /tenants/:tenant_id/applications |
The applications of a tenant the caller may use, each with files_enabled |
GET P/me/capabilities |
The caller’s roles and expanded permission keys in the context |
GET P/settings/public |
What an uploader needs: allowed types, sizes, the named upload policies |
GET /health |
Liveness; no token |
Two kinds of token
Section titled “Two kinds of token”| User token | Machine token | |
|---|---|---|
| Who | A signed-in user, through your backend | Your backend client itself |
| How you get it | Token exchange of the user’s token for your API (User tokens) | client_credentials with your client’s id and secret |
| Scope | files:access |
files:manage, files:read or files:config.manage |
| What it may do | What the user’s permissions allow, item by item (Permissions) | Everything its scope grants in its context, whatever the entries say |
| Lifetime | At most 10 minutes | Until expires_in; the Node client renews it |
The service tells them apart by the token itself: a token whose subject is the client (no user session) is a machine token. Both must have the audience files-api.
The machine scopes
Section titled “The machine scopes”| Scope | Grants |
|---|---|
files:manage |
Every action on every file and folder of its context, plus purging, owners and entries: uploads, metadata, versions, URLs, permissions, shares, trash, search |
files:read |
view and download: reads, search and URL minting only |
files:config.manage |
The application’s settings, taxonomies, usage and audit log; no content |
A machine client never gets the role matrix: changing what roles may do is for people on the console. Prefer one client per job: your API holds files:manage, a deployment script files:config.manage.
The resource identifier and the audience
Section titled “The resource identifier and the audience”Two values name the Files API, and they are easy to mix up.
| Value | Where it appears |
|---|---|
https://files.centralcodestation.com/api |
The resource identifier: what you send as resource= when you ask the Identity Server for a token |
files-api |
The audience: what the token’s aud claim says. Only the service reads it |
Sending files-api as resource= fails at the Identity Server (invalid_target). A token for any other audience fails at the service with invalid_token.
Which contexts a client may use
Section titled “Which contexts a client may use”- A client of a tenant application works only in that tenant and application. Any other context answers
client_context_mismatch. That is the normal case for your backend. - A client of a platform application (such as Chat’s) works in every tenant application that has Files switched on, and passes the context with every call. What Chat stores this way is on Media in Chat.
- A user must be an active member of the tenant (
not_tenant_member) with access to the application (no_application_access). - An application that switched Files off answers
files_access_not_grantedon every route. - A suspended tenant keeps reading but cannot upload: new tickets answer
tenant_suspended.
One client, many contexts
Section titled “One client, many contexts”A multi-tenant backend constructs one Node client without a default context and passes { tenantId, applicationId } as the last argument of every call. A per-call context wins over the client’s default; a call with neither rejects with FilesContextError before any request is made.
await files.folders.create({ parent_id: 'root', name: 'Media' }, { tenantId: 'fitness-co', applicationId: 'fitness-app' });await files.files.update(fileId, { title: 'Beach' }, { ifMatch: 4 }, { tenantId, applicationId }); // options first, context lastActing for a user
Section titled “Acting for a user”A machine call is your backend’s own. Two fields say which user it was for:
owner_idon a ticket or a folder: the user the item belongs to. It must be an active member of the context. Without it the file belongs toacting_user_id, or else to no user.acting_user_idon a write: the user recorded on the audit row. The service checks that the user is a member, nothing more: your backend’s own rule has already run.
To check what a user may do before you act for them, ask the service: see Permissions from code.