hcengineering/platform · error · TokenError

nbf and exp are required when sub is not provided

Error message

nbf and exp are required when sub is not provided

What it means

A grant with no sub (subject) must be time-bounded: generateToken requires both nbf and exp when a grant is given but sub is omitted, so anonymous/workspace-grant tokens cannot live forever.

Source

Thrown at foundations/core/packages/token/src/token.ts:79

    grant?: PermissionsGrant
    nbf?: number
    exp?: number
    sub?: PersonUuid
  }
): string {
  if (!validate(accountUuid)) {
    throw new TokenError(`Invalid account uuid: "${accountUuid}"`)
  }
  if (workspaceUuid !== undefined && !validate(workspaceUuid)) {
    throw new TokenError(`Invalid workspace uuid: "${workspaceUuid}"`)
  }
  const { grant, nbf, exp, sub } = options ?? {}
  if (grant?.workspace !== undefined && !validate(grant?.workspace)) {
    throw new TokenError(`Invalid grant workspace uuid: "${grant?.workspace}"`)
  }

  if (grant != null && sub == null && (nbf == null || exp == null)) {
    throw new TokenError('nbf and exp are required when sub is not provided')
  }

  const service = getMetadata(serverPlugin.metadata.Service)
  if (service !== undefined) {
    extra = { service, ...extra }
  }

  const sanitizedGrant: PermissionsGrant | undefined =
    grant !== undefined
      ? {
          workspace: grant.workspace,
          role: grant.role,
          grantedBy: grant.grantedBy,
          firstName: grant.firstName,
          lastName: grant.lastName,
          spaces: grant.spaces,
          extra: grant.extra
        }

View on GitHub (pinned to 63e28dc964)

Solutions

  1. Provide both nbf and exp in options when issuing a grant token without sub
  2. Set sub (a PersonUuid) if the token should be bound to a person instead
  3. Review token-issuing call sites to ensure grant tokens always have a validity window

Example fix

// before
generateToken(account, workspace, { grant: { workspace } })
// after
const now = Math.floor(Date.now() / 1000)
generateToken(account, workspace, { grant: { workspace }, nbf: now, exp: now + 3600 })
Defensive patterns

Strategy: validation

Validate before calling

if (grant != null && sub == null && (nbf == null || exp == null)) {
  throw new Error('provide nbf and exp for grant tokens without sub')
}

Try / catch

try {
  return generateToken(account, workspace, opts)
} catch (e) {
  if (e instanceof TokenError && e.message.includes('nbf and exp are required')) {
    const now = Math.floor(Date.now() / 1000)
    return generateToken(account, workspace, { ...opts, nbf: now, exp: now + opts.ttl ?? 3600 })
  }
  throw e
}

Prevention

When it happens

Trigger: Calling generateToken with grant set, sub left undefined, and either nbf or exp (or both) missing from options.

Common situations: Issuing service or workspace-scoped tokens where the developer forgot expiry; migrating code that previously used sub and now omits it without adding nbf/exp.

Related errors


AI-assisted analysis of hcengineering/platform@63e28dc964 (2026-08-29). Data as JSON: /api/errors/0727523aabc6dcea. Report an issue: GitHub.