SqlOS

Fine-Grained Auth

List Filtering

Filter EF Core queries by authorization at query time.

4 sections

GetAuthorizationFilterAsync returns a filter for Where(...). Lists only rows the subject may see. Use this on list endpoints.

Basic usage#

CSHARP
var filter = await authService
    .GetAuthorizationFilterAsync<Chain>(subjectId, "CHAIN_VIEW");
 
var chains = await dbContext.Chains
    .Where(filter)
    .OrderBy(c => c.Name)
    .ToListAsync();

The subject only sees chains they have access to. The filter translates to a SQL query -- no in-memory filtering.

Use PagedSpec for built-in pagination, sorting, search, and authorization:

CSHARP
var spec = PagedSpec.For<Chain>(c => c.Id)
    .RequirePermission("CHAIN_VIEW")
    .SortByString("name", c => c.Name, isDefault: true)
    .Search(searchQuery, c => c.Name, c => c.Description)
    .Build(pageSize: 20, cursor, sortBy, sortDir);
 
var result = await executor.ExecuteAsync(
    dbContext.Chains, spec, subjectId,
    chain => new ChainDto
    {
        Id = chain.Id,
        Name = chain.Name,
        Description = chain.Description,
        CreatedAt = chain.CreatedAt
    });
 
// result.Items     → authorized, searched, sorted, paged
// result.NextCursor → cursor for next page
// result.HasMore   → whether more results exist

Full endpoint example#

From the retail example app:

CSHARP
app.MapGet("/api/chains", async (
    ExampleAppDbContext context,
    ISqlOSFgaAuthService authService,
    ISpecificationExecutor executor,
    HttpContext http,
    string? search, int? pageSize, string? cursor,
    string? sortBy, string? sortDir) =>
{
    var subjectId = http.GetSubjectId();
 
    var spec = PagedSpec.For<Chain>(c => c.Id)
        .RequirePermission(RetailPermissionKeys.ChainView)
        .SortByString("name", c => c.Name, isDefault: true)
        .Search(search, c => c.Name, c => c.Description)
        .Build(pageSize ?? 20, cursor, sortBy, sortDir);
 
    return await executor.ExecuteAsync(
        context.Chains.Include(c => c.Locations),
        spec, subjectId,
        chain => new ChainListDto { /* ... */ });
});

When to use#

  • List endpoints -- filter results by what the user can see
  • Search -- combine authorization with text search and sorting
  • Dashboards -- aggregate only accessible data

For single-resource access, use AuthorizedDetailAsync. For mutations, use CheckAccessAsync.