Fine-Grained Auth
Access Checks
Point-check whether a subject can perform an action.
Use CheckAccessAsync for a yes/no answer: can this subject perform this action on this resource?
FGA checks a subject ID; it does not validate bearer tokens. Protect the route group for this API's exact audience first, then derive the subject ID from GetSqlOSValidatedToken(). Never trust a subject ID supplied by the client.
var access = await authService.CheckAccessAsync(
subjectId,
"WORKSPACE_MANAGE",
resourceId);
if (!access.Allowed)
return Results.Json(new { error = "Permission denied" }, statusCode: 403);Use this for:
Check if a subject has a permission on the configured root resource:
var canManageWorkspaces = await authService.HasCapabilityAsync(subjectId, "WORKSPACE_MANAGE");Use this for:
This method calls CheckAccessAsync for RootResourceId; it does not scan descendant resources to answer whether the subject has that permission somewhere.
Test access decisions visually in the dashboard at Fine-Grained Auth > Access Tester.

Select a subject, permission, and resource to see whether access is allowed and trace the decision path.
Create a protected entity after checking access to its parent resource. SqlOSDbContext<TContext> syncs the backing FGA resource row when the entity is saved.
using SqlOS.AuthServer.Extensions;
using SqlOS.Extensions;
const string apiAudience = "https://api.acme.test";
var api = app.MapGroup("/api")
.RequireSqlOSAccessToken(apiAudience);
api.MapPost("/workspaces", async (
CreateWorkspaceRequest request,
AppDbContext db,
ISqlOSFgaAuthService authService,
HttpContext http,
CancellationToken ct) =>
{
var subjectId = http.GetSqlOSValidatedToken()?.UserId;
if (string.IsNullOrWhiteSpace(subjectId))
return Results.Unauthorized();
var organizationResourceId = $"org::{request.OrganizationId}";
var access = await authService.CheckAccessAsync(
subjectId,
"WORKSPACE_MANAGE",
organizationResourceId);
if (!access.Allowed)
return Results.Json(new { error = "Permission denied" }, statusCode: 403);
var workspaceId = Guid.NewGuid();
var workspace = new Workspace
{
Id = workspaceId,
ResourceId = $"workspace::{workspaceId:D}",
OrganizationId = request.OrganizationId,
Name = request.Name.Trim()
};
db.Workspaces.Add(workspace);
await db.SaveChangesAsync(ct);
return Results.Created($"/api/workspaces/{workspace.Id}", workspace);
});