tokens/search · authorization & scope flow
flowchart TD
    REQ["POST /1/tokens/search body: { accountId? }"]
    FA["fetchAccount app.js:138 — global on all routes reads accountId from POST body"]
    FA_Q{accountId in body?}
    FA_SET["ctx.state.account = Account.findById(body.accountId)"]
    FA_UNSET["ctx.state.account = undefined"]
    AUTH["authenticate + fetchCredentials ctx.state.authUser populated from JWT"]
    RP["requirePermissions { endpoint: 'tokens', level: 'read', allowAccountContext: true }"]
    SD["shouldDenyAccess — auth.js:103"]
    SD_PROV["provider-level check canAccess({ providerId })"]
    SD_ACCT{account-level check ctx.state.account set?}
    DENY["← 403 Forbidden"]
    GRANT["access granted"]
    CTRL["controller search() query = { deleted: false, providerId }"]
    SCOPE["scope enforcement block our fix — added to controller"]
    SCOPE_Q{hasProviderAccess?}
    PROV_PATH["body accountId filter applied as optional filter"]
    ACCT_PATH["force query.accountId = authUser.accountId"]
    MONGO["MongoDB query → response"]
    REQ --> FA
    FA --> FA_Q
    FA_Q -- yes --> FA_SET
    FA_Q -- no --> FA_UNSET
    FA_SET --> AUTH
    FA_UNSET --> AUTH
    AUTH --> RP
    RP --> SD
    SD --> SD_PROV
    SD_PROV -- "has provider role" --> GRANT
    SD_PROV -- "no provider role" --> SD_ACCT
    SD_ACCT -- "undefined → skipped → no branch grants access" --> DENY
    SD_ACCT -- "= other account no permission for it" --> DENY
    SD_ACCT -- "= own account has permission" --> GRANT
    GRANT --> CTRL
    CTRL --> SCOPE
    SCOPE --> SCOPE_Q
    SCOPE_Q -- "yes (provider user)" --> PROV_PATH
    SCOPE_Q -- "no (account user)" --> ACCT_PATH
    PROV_PATH --> MONGO
    ACCT_PATH --> MONGO