.NET SDK
Async C# client for InferaDB with ASP.NET Core integration.
Coming soon. The .NET SDK is under active development. The API surface shown here is based on the Rust SDK and may change before release.
Async, strongly-typed client for InferaDB’s authorization APIs. Targets .NET 8+ with ASP.NET Core integration.
Installation
dotnet add package InferaDB.Sdk
Authentication
Three authentication methods:
| Method | Use Case | Security |
|---|---|---|
| Client Credentials (Ed25519 JWT) | Service-to-service | High |
| Bearer Token | User sessions, OAuth | Medium |
| API Key | Testing, simple integrations | Basic |
Client Credentials (Recommended)
using InferaDB;
var client = new InferaDBClient(new InferaDBOptions
{
Url = "https://engine.inferadb.com",
Credentials = new ClientCredentials
{
ClientId = "my-client",
PrivateKey = Ed25519PrivateKey.FromPemFile("client.pem"),
CertificateId = "cert-id",
},
});
Dependency Injection (Recommended for ASP.NET Core)
builder.Services.AddInferaDB(options =>
{
options.Url = "https://engine.inferadb.com";
options.Credentials = new ClientCredentials
{
ClientId = "my-client",
PrivateKey = Ed25519PrivateKey.FromPemFile("client.pem"),
CertificateId = "cert-id",
};
});
API Key
var client = new InferaDBClient(new InferaDBOptions
{
Url = "https://engine.inferadb.com",
ApiKey = Environment.GetEnvironmentVariable("INFERADB_API_KEY"),
});
Permission Checks
var vault = client.Organization("my-org").Vault("production");
// Simple check
bool allowed = await vault.CheckAsync("user:alice", "can_edit", "document:readme");
With ABAC Context
bool allowed = await vault.CheckAsync("user:alice", "can_view", "document:readme",
new CheckOptions
{
Context = new Dictionary<string, object> { ["ip_address"] = "10.0.0.1" },
});
Require — Throws on Deny
// Throws AccessDeniedException if permission is denied
await vault.RequireAsync("user:alice", "can_edit", "document:readme");
With Consistency Token
bool allowed = await vault.CheckAsync("user:alice", "can_view", "document:readme",
new CheckOptions { AtLeastAsFresh = revisionToken });
Batch Check
var results = await vault.CheckBatchAsync(
[
new CheckRequest("user:alice", "can_edit", "document:readme"),
new CheckRequest("user:bob", "can_view", "document:readme"),
]);
if (results.AllAllowed)
{
// all checks passed
}
Relationships
Write
// Returns a revision token
var token = await vault.Relationships.WriteAsync(
new Relationship("document:readme", "editor", "user:alice"));
Batch Write
await vault.Relationships.WriteBatchAsync(
[
new Relationship("document:readme", "editor", "user:alice"),
new Relationship("document:readme", "viewer", "user:bob"),
]);
List
// Returns IAsyncEnumerable<Relationship>
var rels = await vault.Relationships
.List(resource: "document:readme")
.ToListAsync();
Delete
await vault.Relationships.DeleteWhereAsync(
resource: "document:readme",
relation: "viewer",
subject: "user:bob");
Lookups
Both return IAsyncEnumerable<T>:
// What resources can Alice view?
var resources = await vault.Resources
.AccessibleBy("user:alice")
.WithPermission("can_view")
.ResourceType("document")
.ToListAsync();
// Who can edit this document?
var subjects = await vault.Subjects
.WithPermission("can_edit")
.OnResource("document:readme")
.ToListAsync();
Testing
MockClient (Fastest)
using InferaDB.Testing;
var client = MockClient.Builder()
.OnCheck("user:alice", "can_edit", "document:readme").Allow()
.OnCheck("user:bob", "can_edit", "document:readme").Deny()
.OnCheckAnySubject("can_view", "document:readme").Allow()
.DefaultDeny()
.VerifyOnDispose(true) // asserts all expectations were invoked on dispose
.Build();
InMemoryClient (Full Policy Evaluation)
using InferaDB.Testing;
var client = InMemoryClient.WithSchemaAndData(
schema: """
type document {
relation viewer
relation editor
relation can_view = viewer | editor
}
""",
data:
[
new Relationship("document:readme", "editor", "user:alice"),
new Relationship("document:readme", "viewer", "user:bob"),
]);
TestVault (Real Instance)
using InferaDB.Testing;
await using var vault = await TestVault.CreateAsync(org, schemaIpl);
// vault auto-cleans up on dispose
// call vault.Preserve() to keep data for debugging
Error Handling
using InferaDB;
try
{
await vault.RequireAsync("user:alice", "can_edit", "document:readme");
}
catch (AccessDeniedException)
{
// permission denied
}
catch (InferaDBException ex)
{
if (ex.IsRetriable)
{
// retry after ex.RetryAfter
}
logger.LogError("Authorization error: Kind={Kind}, RequestId={RequestId}",
ex.Kind, ex.RequestId);
}
ErrorKind enum: Unauthorized, Forbidden, NotFound, RateLimited, SchemaViolation, Unavailable, Timeout, InvalidArgument.
Framework Integrations
ASP.NET Core Policy
using InferaDB.AspNetCore;
builder.Services.AddAuthorization(options =>
{
options.AddInferaDBPolicy("CanEditDocument", policy =>
{
policy.RequirePermission("can_edit");
policy.WithSubject(ctx => $"user:{ctx.User.FindFirst("sub")?.Value}");
policy.WithResource(ctx =>
$"document:{ctx.HttpContext?.GetRouteValue("id")}");
});
});
[Authorize(Policy = "CanEditDocument")]
[HttpPut("{id}")]
public async Task<IActionResult> UpdateDocument(string id, DocumentDto dto)
{
// only reached if authorized
return Ok(await documentService.Update(id, dto));
}
Minimal API
app.MapGet("/documents/{id}", async (
string id,
IInferaDBClient client,
ClaimsPrincipal user,
IDocumentService documentService) =>
{
var vault = client.Organization("my-org").Vault("production");
var userId = user.FindFirst("sub")?.Value
?? throw new UnauthorizedAccessException();
await vault.RequireAsync($"user:{userId}", "can_view", $"document:{id}");
return Results.Ok(await documentService.FindById(id));
})
.RequireAuthorization();