Access
Access decides whether a request may read a Visible entity’s Metadata (its RO-Crate metadata document) and Content (a
file’s bytes). arocapi attaches the result to every entity and file it returns, and enforces it on
/entity/:id/metadata and /file/:id before calling your storage backend. See
Record, Metadata, Content for the levels and
Visibility vs Access for how a request is decided.
Access strategies compares common ways to decide access, and
Licences & authorisation URLs explains the licence model.
| Option | Required | Type | Default | Purpose |
|---|---|---|---|---|
access |
yes | AccessFunction |
none | Decide Metadata and Content access for an entity. |
isAuthenticated |
no | IsAuthenticated |
none | Tell anonymous requests (401) from authenticated (403). |
access
Section titled “access”Called with the entity as a BaseEntity and the request context. It
returns an AccessInfo:
| Field | Type | Purpose |
|---|---|---|
metadata |
boolean |
Whether the request may read the entity’s Metadata. |
content |
boolean |
Whether the request may read the file’s Content. |
metadataAuthorizationUrl |
string? |
Where to request Metadata access. Kept only when truthy. |
contentAuthorizationUrl |
string? |
Where to request Content access. Kept only when truthy. |
Anything else it returns is dropped.
BaseEntityhas the Record fields, withmemberOfandrootCollectionas ids. arocapi doesn’t look up parents.- It runs once per entity in a listing and once per Metadata or Content request. Cache remote lookups per request.
- A file’s access is its file entity’s access. On collections and objects,
contentis only a signal to clients. - When access is false,
/entity/:id/metadataand/file/:idrefuse the request and never call your storage backend.
For a public repository, use AllPublicAccess, which grants everything, with the filters that make every entity
visible:
import { AllPublicAccess, AllVisibleEntityFilter, AllVisibleSearchFilter, type Options } from 'arocapi';
export const guards = { entityVisibilityFilter: AllVisibleEntityFilter, searchVisibilityFilter: AllVisibleSearchFilter, access: AllPublicAccess,} satisfies Partial<Options>;When access follows licences, use LicenceAccess. Metadata needs the entity’s metadataLicenseId and Content its
contentLicenseId. Each licence is checked at most once per request. See
licence checks against an external authority
for caching a user’s entitlements across licences.
import { LicenceAccess } from 'arocapi';import type { FastifyRequest } from 'fastify';
// Your app's lookup of the licences a request's user holdsdeclare function userLicences(request: FastifyRequest): Promise<string[]>;
const OPEN_LICENCES = ['https://creativecommons.org/licenses/by/4.0/'];
export const access = LicenceAccess({ hasLicence: async (licenceId, { request }) => OPEN_LICENCES.includes(licenceId) || (await userLicences(request)).includes(licenceId), authorisationUrl: (licenceId) => `https://rems.example.com/apply?resource=${encodeURIComponent(licenceId)}`,});LicenceAccess option |
Required | Type | Purpose |
|---|---|---|---|
hasLicence |
yes | (licenceId, context) => boolean | Promise<boolean> |
Whether the request holds a licence. |
authorisationUrl |
no | (licenceId) => string |
Authorisation URL to attach when a licence isn’t held. |
For any other rule, write your own function:
import type { AccessFunction } from 'arocapi';import type { FastifyRequest } from 'fastify';
// Your app's session lookupdeclare function currentUser(request: FastifyRequest): { isStaff: boolean } | undefined;
export const access: AccessFunction = (_entity, { request }) => { const user = currentUser(request);
return { metadata: true, content: Boolean(user?.isStaff), contentAuthorizationUrl: user ? undefined : 'https://auth.example.com/login', };};isAuthenticated
Section titled “isAuthenticated”Returns whether the request carries valid credentials. arocapi uses it to pick the status of a refusal: 401 for an anonymous request, so the client can prompt a login, and 403 when logging in won’t help; see Denied is 401 or 403, never 404.
import type { IsAuthenticated } from 'arocapi';import type { FastifyRequest } from 'fastify';
// Your app's session lookupdeclare function currentUser(request: FastifyRequest): object | undefined;
export const isAuthenticated: IsAuthenticated = ({ request }) => currentUser(request) !== undefined;Without it, the repository has no logins: no request is Anonymous, so a Denied request always gets 403. It is required
by promptLoginForHidden. See
Error responses for every outcome.