Documentation

FGA API Reference

← All docs

FGA API Reference

Complete method reference for CheckAccess, GetAuthorizationFilter, CreateResource, PagedSpec, and more.

Complete reference for every public method in the FGA module.

ISqlOSFgaAuthService

The core authorization service. Injected via DI as ISqlOSFgaAuthService.


CheckAccessAsync

Check if a subject has access to a specific resource with a given permission. Walks up the resource hierarchy from the target resource to the root, looking for grants whose role includes the required permission.

var access = await authService.CheckAccessAsync(subjectId, "CHAIN_VIEW", "chain-1");
if (!access.Allowed)
    return Results.Json(new { error = "Permission denied" }, statusCode: 403);

Parameters

NameTypeDescription
subjectIdstringThe subject (user, agent, etc.) to check access for.
permissionKeystringThe permission key to check (e.g., "CHAIN_VIEW").
resourceIdstringThe resource ID to check access on.

Returns

Task<SqlOSFgaAccessCheckResult>

FieldTypeDescription
AllowedboolWhether the subject has the permission on the resource.
TraceList<SqlOSFgaAccessTrace>?Step-by-step trace of how the decision was made.
Errorstring?Error message if the check failed unexpectedly.

HasCapabilityAsync

Check if a subject has a specific permission at root level — i.e., anywhere in the resource tree. Use for global capability gates.

var canEdit = await authService.HasCapabilityAsync(subjectId, "CHAIN_EDIT");
if (!canEdit)
    return Results.Json(new { error = "Permission denied" }, statusCode: 403);

Parameters

NameTypeDescription
subjectIdstringThe subject to check.
permissionKeystringThe permission key to check.

Returns

Task<bool>true if the subject has any grant with a role that includes the permission.


GetAuthorizationFilterAsync

Produce a LINQ expression that filters an IQueryable<T> to only entities the subject can access. The expression translates to a SQL Server table-valued function call at query time.

var filter = await authService
    .GetAuthorizationFilterAsync<Chain>(subjectId, "CHAIN_VIEW");

var chains = await dbContext.Chains
    .Where(filter)
    .OrderBy(c => c.Name)
    .ToListAsync();

Parameters

NameTypeDescription
subjectIdstringThe subject to filter for.
permissionKeystringThe permission key that determines which resources are visible.

Type constraint: T : IHasResourceId — the entity must expose a ResourceId property.

Returns

Task<Expression<Func<T, bool>>> — an EF Core-compatible expression you pass to .Where().


TraceResourceAccessAsync

Produce a detailed, structured trace of a resource access decision. Shows every step of the hierarchy walk and which grants matched or didn't. Useful for debugging authorization issues.

var trace = await authService.TraceResourceAccessAsync(
    subjectId, "chain-1", "CHAIN_VIEW");
// trace contains the full decision path

Parameters

NameTypeDescription
subjectIdstringThe subject to trace access for.
resourceIdstringThe target resource.
permissionKeystringThe permission to trace.

Returns

Task<SqlOSFgaResourceAccessTrace> — structured trace of the full decision.


SqlOSFgaConvenienceExtensions

Extension methods that reduce endpoint boilerplate. Available via using SqlOS.Fga.Extensions.


CreateResource

Create an SqlOSFgaResource in the hierarchy. Adds to the change tracker but does not call SaveChangesAsync. Returns the resource ID so you can assign it to your domain entity.

var resourceId = context.CreateResource(
    parentId: "retail_root",
    name: request.Name,
    resourceTypeId: "chain"
);

var chain = new Chain { ResourceId = resourceId, Name = request.Name };
context.Chains.Add(chain);
await context.SaveChangesAsync();

Parameters

NameTypeRequiredDescription
contextISqlOSFgaDbContextYesThe DbContext (called as extension method).
parentIdstringYesParent resource ID in the hierarchy.
namestringYesDisplay name for the resource.
resourceTypeIdstringYesResource type identifier (e.g., "chain", "location").
idstring?NoCustom resource ID. If null, a GUID is generated.

Returns

string — the resource ID (either the provided id or the auto-generated GUID).


AuthorizedDetailAsync

Fetch a single entity by predicate, check authorization, and return the appropriate HTTP result in one call. Replaces the pattern of fetch → check → map → return.

return await authService.AuthorizedDetailAsync(
    context.Chains.Include(c => c.Locations),
    c => c.Id == id,
    subjectId,
    "CHAIN_VIEW",
    chain => new ChainDetailDto
    {
        Id = chain.Id,
        Name = chain.Name,
        LocationCount = chain.Locations.Count
    });

Parameters

NameTypeDescription
authServiceISqlOSFgaAuthServiceThe auth service (called as extension method).
queryIQueryable<TEntity>The queryable to fetch from (can include .Include()).
predicateExpression<Func<TEntity, bool>>Filter to find the single entity (e.g., c => c.Id == id).
subjectIdstringThe subject to authorize.
permissionKeystringThe permission to check on the entity's resource.
selectorFunc<TEntity, TDto>Maps the entity to a DTO for the response body.

Type constraint: TEntity : class, IHasResourceId

Returns

Task<IResult> — one of:

ConditionHTTP Result
Entity not found404 Not Found
Access denied403 { "error": "Permission denied" }
Access granted200 with the mapped DTO

PagedSpec Builder

Fluent builder for creating cursor-paginated, sorted, searchable, authorization-filtered queries.


PagedSpec.For

Start building a paged specification. The idSelector identifies the unique tiebreaker column for cursor pagination.

var spec = PagedSpec.For<Chain>(c => c.Id)
    .RequirePermission("CHAIN_VIEW")
    .SortByString("name", c => c.Name, isDefault: true)
    .SortByString("description", c => c.Description ?? "")
    .Search(search, c => c.Name, c => c.Description)
    .Configure(q => q.Include(c => c.Locations))
    .Build(pageSize, cursor, sortBy, sortDir);

Builder Methods

MethodParametersDescription
For<T>(idSelector)Expression<Func<T, string>>Start builder with the ID/tiebreaker column.
.RequirePermission(key)stringSet the FGA permission key for authorization filtering.
.SortByString(name, selector, isDefault?)string, Expression<Func<T, string>>, boolRegister a named string sort column.
.Search(search, ...fields)string?, params Expression<Func<T, string>>[]Add text search across the given fields.
.Where(predicate)Expression<Func<T, bool>>Add a static filter.
.Configure(action)Action<IQueryable<T>>Configure the query (e.g., .Include()).
.Build(pageSize, cursor, sortBy, sortDir)int, string?, string?, string?Build the final PagedSpecification<T>.

ISpecificationExecutor

Executes a PagedSpecification<T> against a queryable, combining user filters with FGA authorization and cursor pagination. Injected via DI.


ExecuteAsync

Execute a paged specification and return a paginated result.

var result = await executor.ExecuteAsync(
    context.Chains, spec, subjectId,
    c => new ChainDto
    {
        Id = c.Id,
        Name = c.Name,
        LocationCount = c.Locations.Count
    });

// result.Items     — the page of DTOs
// result.NextCursor — pass to next request for pagination
// result.HasMore   — whether more pages exist

Parameters

NameTypeDescription
dbSet / queryDbSet<TEntity> or IQueryable<TEntity>The base query.
specificationPagedSpecification<TEntity>The spec built by PagedSpec.For<T>().Build().
subjectIdstringThe subject for FGA authorization filtering.
selectorFunc<TEntity, TDto>Maps entities to DTOs.
cancellationTokenCancellationTokenOptional cancellation token.

Type constraint: TEntity : class, IHasResourceId

Returns

Task<PaginatedResult<TDto>>

FieldTypeDescription
ItemsList<TDto>The current page of results.
NextCursorstring?Opaque cursor for the next page. null if no more pages.
HasMoreboolWhether additional pages exist.

CountAsync

Count total matching entities (with authorization filtering) without fetching data.

var total = await executor.CountAsync(context.Chains, spec, subjectId);

Parameters

NameTypeDescription
dbSetDbSet<TEntity>The base query.
specificationPagedSpecification<TEntity>The spec.
subjectIdstringThe subject for authorization.

Returns

Task<long> — total count of accessible, matching entities.


IHasResourceId

Interface that connects domain entities to FGA resources. Required for GetAuthorizationFilterAsync, AuthorizedDetailAsync, and ISpecificationExecutor.

public interface IHasResourceId
{
    string ResourceId { get; }
}

Implementation

public class Chain : IHasResourceId
{
    public string Id { get; set; } = Guid.NewGuid().ToString();
    public string ResourceId { get; set; } = "";
    public string Name { get; set; } = "";
}

FGA Models

SqlOSFgaAccessCheckResult

public class SqlOSFgaAccessCheckResult
{
    public bool Allowed { get; set; }
    public List<SqlOSFgaAccessTrace>? Trace { get; set; }
    public string? Error { get; set; }
}

SqlOSFgaAccessTrace

public class SqlOSFgaAccessTrace
{
    public string Step { get; set; }
    public string Detail { get; set; }
    public string? ResourceId { get; set; }
    public string? ResourceName { get; set; }
    public string? GrantId { get; set; }
    public string? RoleName { get; set; }
    public string? SubjectName { get; set; }
}

SqlOSFgaResource

public class SqlOSFgaResource
{
    public string Id { get; set; }
    public string ParentId { get; set; }
    public string Name { get; set; }
    public string ResourceTypeId { get; set; }
    public bool IsActive { get; set; }
}

SqlOSFgaGrant

public class SqlOSFgaGrant
{
    public string Id { get; set; }
    public string SubjectId { get; set; }
    public string ResourceId { get; set; }
    public string RoleId { get; set; }
    public string? Description { get; set; }
}

SqlOSFgaSubject

public class SqlOSFgaSubject
{
    public string Id { get; set; }
    public string SubjectTypeId { get; set; }
    public string DisplayName { get; set; }
    public string? OrganizationId { get; set; }
    public string? ExternalRef { get; set; }
}