Skip to content
These docs describe arocapi v5, which is in beta. Install it with pnpm add arocapi@beta.

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).

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.

  • BaseEntity has the Record fields, with memberOf and rootCollection as 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, content is only a signal to clients.
  • When access is false, /entity/:id/metadata and /file/:id refuse 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 holds
declare 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 lookup
declare 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',
};
};

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 lookup
declare 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.