Security & Permissions
All Multi-Query endpoints require a valid JWT Bearer token with the TenantAdmin role. TenantID is extracted from the token and cannot be overridden by callers.
Authentication
The Multi-Query API uses standard BizFirst JWT Bearer authentication. Tokens are validated on every request using the following settings:
| Setting | Value | Config Key |
|---|---|---|
| ValidateIssuer | true |
JWT:Issuer |
| ValidateAudience | true |
JWT:Audience |
| ValidateLifetime | true |
— |
| ClockSkew | 5 minutes | — |
| Signing key | HMAC-SHA256 | JWT:Key |
All requests must include the token in the Authorization header:
Authorization: Bearer <your-jwt-token>
Authorization
Every Multi-Query controller action is decorated with [AuthorizeTenantAdminAttribute]. This attribute enforces that the authenticated principal holds at least one of the following roles:
- Admin — global platform administrator
- TenantAdmin — administrator scoped to a specific tenant
Requests from users without either role receive an HTTP 403 Forbidden response immediately after token validation, before any template or database logic executes. Standard user roles (Employee, Manager, etc.) do not have access to any Multi-Query endpoint.
// Applied to all Multi-Query controller actions
[AuthorizeTenantAdminAttribute]
[ApiLimit("MultiQuery.Execute")]
public async Task<IActionResult> ExecuteJson(string templateCode) { ... }
Tenant Isolation
The TenantID used to scope all SQL execution is extracted exclusively from the JWT claim CurrentTenantIDWithCheck_Int. It is injected into the root query and every child query in the tree. Callers cannot supply, influence, or override the TenantID through request parameters, body fields, template parameters, or expression context values.
This guarantee applies even to the run-direct endpoint: if the submitted SQL references @TenantID, that parameter is bound to the JWT-derived value — the caller cannot change it. Attempts to reference a different tenant's data are blocked at the SQL parameter binding layer.
Template authors should always include a WHERE TenantID = @TenantID clause in their root and child queries. The engine injects the value; the SQL must consume it.
Rate Limiting
Each operation class has its own rate limit bucket, configured via ApiLimit attributes. Limits are applied per-tenant and per-user. Exceeding the limit returns HTTP 429 Too Many Requests.
| Endpoint | Rate Limit Key | Limit Type |
|---|---|---|
GET …/multiquery/{code}GET …/{code}.jsonGET …/{code}.html |
ApiLimit("MultiQuery.Execute") |
eOperationLimitType.Custom |
POST …/scripts/run-stored |
ApiLimit("MultiQuery.RunStoredScript") |
Default |
POST …/scripts/run-direct |
ApiLimit("MultiQuery.RunDirectScript") |
Default |
Rate limit thresholds are configured in platform settings and can be adjusted per-environment. The eOperationLimitType.Custom designation on the Execute endpoints allows the platform administrator to set a higher ceiling for read-heavy reporting use cases.
Template SQL Security
Templates stored in dbo.Shared_Configurations are trusted SQL authored by platform administrators. The following rules govern what is permitted:
- Only
SELECTstatements are permitted in query templates — noINSERT,UPDATE,DELETE, or DDL - All user-supplied values (parameters passed via
EvaluationContextor request body) are bound as parameterized SQL parameters — never string-concatenated - Dynamic SQL construction (
EXEC sp_executesqlwith variable strings) is prohibited within templates - Stored scripts referenced by the run-stored endpoint must be reviewed and pre-approved before registration in
dbo.Shared_Configurations - Run-direct SQL is intended for trusted administrator tools only — it must never accept text sourced from end-user input
- Template JSON is stored and retrieved verbatim; no server-side template interpolation occurs outside of parameterized SQL binding
Audit Trail
Every call to a Multi-Query endpoint passes through the BizFirst standard request logging pipeline. The log entry includes the authenticated user identity, TenantID, endpoint path, template code (when applicable), HTTP status code, and execution duration. Include an
X-Request-ID header in your requests to correlate client-side traces with platform log entries.
CORS
The platform is configured with an AllowAll CORS policy in development and staging environments for ease of integration testing. In production environments you must restrict CORS to specific trusted origins:
// Production configuration — restrict to known origins
builder.Services.AddCors(options => {
options.AddPolicy("MultiQueryPolicy", policy => {
policy.WithOrigins(
"https://app.bizfirstai.com",
"https://admin.bizfirstai.com"
)
.AllowAnyHeader()
.AllowAnyMethod();
});
});
The
POST …/scripts/run-direct endpoint executes arbitrary SQL text submitted in the request body. While TenantID is always injected from the JWT, a poorly written query could expose large volumes of tenant data or cause performance degradation. Ensure that only highly trusted administrators hold the TenantAdmin role in production environments. Consider disabling the run-direct endpoint entirely in customer-facing deployments and reserving it for internal tooling only.