nocobase/nocobase · error

Invalid temporary file access token

Error message

Invalid temporary file access token

What it means

verifyTemporaryFileAccessToken() verifies a temporary-file-access JWT with HS256 against the plugin-derived secret and the expected audience (TEMPORARY_FILE_ACCESS_AUDIENCE). If jwt.verify passes (signature/expiry valid) but the decoded payload is a string or falsy — i.e. not the expected JSON object payload — it throws 'Invalid temporary file access token'. Note that jwt.verify itself throws separately for bad signatures and expired tokens; this error specifically means the token decoded but its shape is not a valid TemporaryFileAccessPayload object.

Source

Thrown at packages/plugins/@nocobase/plugin-file-manager/src/server/temporary-access.ts:91

      ...(resource.role ? { role: resource.role } : {}),
    },
    deriveTemporaryFileAccessSecret(plugin),
    {
      algorithm: 'HS256',
      audience: TEMPORARY_FILE_ACCESS_AUDIENCE,
      ...(currentUserId ? { subject: String(currentUserId) } : {}),
      expiresIn: options.expiresIn || getTemporaryFileAccessExpiresIn(),
    },
  );
}

export function verifyTemporaryFileAccessToken(plugin: PluginFileManagerServer, token: string) {
  const decoded = jwt.verify(token, deriveTemporaryFileAccessSecret(plugin), {
    algorithms: ['HS256'],
    audience: TEMPORARY_FILE_ACCESS_AUDIENCE,
  });
  if (!decoded || typeof decoded === 'string') {
    throw new Error('Invalid temporary file access token');
  }
  return decoded as TemporaryFileAccessPayload;
}

export async function createTemporaryURLAction(ctx: Context, next: Next) {
  const collectionName = ctx.action.resourceName;
  const collection = ctx.dataSource.collectionManager.getCollection(collectionName) as Collection | undefined;
  if (!isFileCollection(collection)) {
    return ctx.throw(404);
  }
  if (!hasStandardFileId(collection)) {
    ctx.logger.error('file collection is missing standard id field', {
      method: 'file-manager.createTemporaryURL',
      collection: collection.name,
    });
    return ctx.throw(500);
  }

View on GitHub (pinned to fa42722fef)

Solutions

  1. Re-request a fresh token via the temporary-URL action instead of reusing old/hand-made tokens
  2. Ensure the token was signed by the same app (deriveTemporaryFileAccessSecret uses plugin.app.name) with the expected audience and an object payload
  3. Check that the client is not mangling the token in transit (truncation, URL-encoding issues) — though typically that fails verification earlier
  4. If old tokens fail after an upgrade, invalidate them and re-issue; keep the token payload matching TemporaryFileAccessPayload

Example fix

// before: token created manually with a string payload
const token = jwt.sign('some-file-id', secret, { algorithm: 'HS256' });

// after: use the plugin's signing function / object payload
const token = signTemporaryFileAccessToken(plugin, { attachmentId: file.id });
Defensive patterns

Strategy: validation

Validate before calling

// Client-side sanity check before sending the token
function looksLikeTemporaryFileToken(token: string): boolean {
  const parts = token.split('.');
  if (parts.length !== 3) return false;
  try {
    const payload = JSON.parse(Buffer.from(parts[1], 'base64url').toString('utf8'));
    return payload && typeof payload === 'object' && !Array.isArray(payload);
  } catch {
    return false;
  }
}
if (!looksLikeTemporaryFileToken(token)) throw new Error('Refusing to send malformed temporary file token');

Type guard

function isTemporaryFileAccessPayload(
  decoded: string | object | null,
): decoded is TemporaryFileAccessPayload {
  return (
    !!decoded &&
    typeof decoded === 'object' &&
    !Array.isArray(decoded) &&
    typeof (decoded as TemporaryFileAccessPayload).attachmentId !== 'undefined'
  );
}

Try / catch

import { TokenExpiredError, JsonWebTokenError } from 'jsonwebtoken';
try {
  const payload = verifyTemporaryFileAccessToken(plugin, token);
} catch (e) {
  if (e instanceof TokenExpiredError) return respondTokenExpired();
  if (e instanceof JsonWebTokenError || e.message === 'Invalid temporary file access token') {
    return respondUnauthorized('Request a new temporary file URL');
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling the file access endpoint (getFile path) with a token that: was created with a different payload format (e.g. signed by another feature or an older plugin version using a string sub-only payload), was crafted as a string-payload JWT, or otherwise verifies cryptographically but does not decode to the expected object with the right audience.

Common situations: A token issued by a different NocoBase app name (secret derivation uses plugin.app.name, so verification would fail there rather than here); hand-rolled tokens in integrations/scripts that pass a plain string as the JWT payload; plugin upgrades changing the payload schema so old tokens no longer match the expected object shape.

Related errors


AI-assisted analysis of nocobase/nocobase@fa42722fef (2026-09-01). Data as JSON: /api/errors/2064c5cf419c346e. Report an issue: GitHub.