SqlOS 3.5.0

dotnet add package SqlOS --version 3.5.0
                    
NuGet\Install-Package SqlOS -Version 3.5.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="SqlOS" Version="3.5.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="SqlOS" Version="3.5.0" />
                    
Directory.Packages.props
<PackageReference Include="SqlOS" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add SqlOS --version 3.5.0
                    
#r "nuget: SqlOS, 3.5.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package SqlOS@3.5.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=SqlOS&version=3.5.0
                    
Install as a Cake Addin
#tool nuget:?package=SqlOS&version=3.5.0
                    
Install as a Cake Tool

SqlOS

Embedded auth server and fine-grained authorization for .NET — one NuGet package, zero external services.

License: MIT NuGet .NET 9

SqlOS adds auth and fine-grained authorization to your .NET app. OAuth, login UI, orgs, SAML, OIDC, and FGA live in your SQL Server. An embedded admin UI ships with the package.

Think WorkOS / AuthKit, but self-hosted and your database.

Why SqlOS?

External auth services SqlOS
Data lives on someone else's servers Data lives in your SQL Server
Per-MAU pricing that scales against you MIT-licensed, no usage fees
Another vendor dependency to manage Single NuGet package, ships with your app
Limited customization of login flows Full control — branded AuthPage, custom OIDC, SAML

Features

AuthServer

  • OAuth 2.0 with PKCE/sqlos/auth/authorize, /sqlos/auth/token, metadata, and JWKS
  • Branded AuthPage — hosted /sqlos/auth/login, /sqlos/auth/signup, and /sqlos/auth/logged-out
  • Client Onboarding Modes — seeded/manual owned apps, CIMD discovered clients, and optional DCR compatibility clients
  • Resource Indicators — bind resource end to end and mint audience-aware access tokens
  • Organizations & Users — multi-tenant user management with memberships and roles
  • Password Credentials — secure local authentication with session management
  • Social Login — Google, Microsoft, Apple, and any custom OIDC provider
  • SAML SSO — enterprise single sign-on with home realm discovery by email domain
  • Sessions & Refresh Tokens — full lifecycle management with revocation
  • Signing Key Rotation — automatic RS256 key rotation with configurable intervals
  • Email OTP — passwordless sign-in with Azure Communication Services Email
  • Audit Logging — track authentication events across your system

FGA (Fine-Grained Authorization)

  • Hierarchical Resource Authorization — define resource types, permissions, and roles
  • Access Grants — assign permissions to users, user groups, and service accounts
  • EF Core Query Filters — filter authorized resources directly in LINQ queries
  • Access Tester — verify authorization decisions through the dashboard

Embedded Admin Dashboard

  • Auth Admin — manage organizations, users, clients, OIDC/SAML connections, security settings, sessions, and audit events
  • FGA Admin — manage resources, grants, roles, permissions, and test access decisions
  • Password-Protected — optional password auth mode for production deployments

Quick Start

  1. Add the package

    dotnet add package SqlOS
    
  2. Use SQL Server for your EF DbContext
    SqlOS uses the same database as your context. Point EF at SQL Server like any other app.

  3. Wire your DbContext
    Add the two SqlOS interfaces. Add the FGA IsResourceAccessible query. Call UseSqlOS in OnModelCreating:

    public sealed class AppDbContext : DbContext, ISqlOSAuthServerDbContext, ISqlOSFgaDbContext
    {
        public IQueryable<SqlOSFgaAccessibleResource> IsResourceAccessible(
            string subjectId,
            string permissionKey)
            => FromExpression(() => IsResourceAccessible(subjectId, permissionKey));
    
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);
            modelBuilder.UseSqlOS(GetType());
        }
    }
    
  4. Register SqlOS on the host

    builder.AddSqlOS<AppDbContext>(options =>
    {
        options.AuthServer.SeedOwnedWebApp(
            "todo-web",
            "Todo Web App",
            "https://app.example.com/auth/callback");
    });
    
  5. Map routes after Build()

    var app = builder.Build();
    app.MapSqlOS();
    

On startup, SqlOS updates its own schema. Default URLs: admin at /sqlos, OAuth at /sqlos/auth. Change the prefix with DashboardBasePath if you need to.

Dashboard Access

Protect the dashboard in production with a password:

options.Dashboard.AuthMode = SqlOSDashboardAuthMode.Password;
options.Dashboard.Password = builder.Configuration["SqlOS:Dashboard:Password"];

Or via environment variables:

SqlOS__Dashboard__AuthMode=Password
SqlOS__Dashboard__Password=<strong-password>

Email OTP with Azure Communication Services

SqlOS includes an Azure Communication Services Email sender for passwordless email-code login. Provision ACS Email with the helper script:

AZURE_SUBSCRIPTION_ID=<subscription-id> \
AZURE_RESOURCE_GROUP=<resource-group> \
AZURE_DNS_ZONE_NAME=example.com \
AZURE_DNS_ZONE_RESOURCE_GROUP=<dns-zone-resource-group> \
ACS_EMAIL_DOMAIN=example.com \
ACS_EMAIL_SENDER_USERNAME=no-reply \
ACS_EMAIL_SENDER_DISPLAY_NAME="Example" \
./scripts/azure/setup-acs-email.sh --apply-dns --yes

AZURE_DNS_ZONE_NAME is the DNS zone apex (for example example.com), not a resource group. AZURE_DNS_ZONE_RESOURCE_GROUP is the resource group that contains that Azure DNS zone; it defaults to AZURE_RESOURCE_GROUP when omitted. Set it when the zone is in a different group than the ACS Email resources, or drop that line from the command when they match.

The script creates an ACS Email Service, ACS Communication Service, customer-managed email domain, sender username, and optional Azure DNS verification records. If your DNS is not hosted in Azure, omit --apply-dns; the script prints the records to create manually.

Store the connection string securely, then configure SqlOS:

SqlOS__EmailOtp__AzureCommunicationServicesConnectionString=<acs-connection-string>
SqlOS__EmailOtp__FromAddress=no-reply@example.com
builder.AddSqlOS<AppDbContext>(options =>
{
    options.AuthServer.ConfigureEmailOtp(email =>
    {
        email.AzureCommunicationServicesConnectionString =
            builder.Configuration["SqlOS:EmailOtp:AzureCommunicationServicesConnectionString"];
        email.FromAddress = builder.Configuration["SqlOS:EmailOtp:FromAddress"];
        email.ApplicationName = "Example";
    });

    options.AuthServer.SeedAuthPage(page =>
    {
        page.EnabledCredentialTypes = ["password", "email_otp"];
    });
});

Hosted AuthPage, headless browser flows, invite acceptance, and backend SDK usage all use the same OTP primitives. A backend can run a passwordless signup without adding its own REST API surface:

var start = await sqlosAuth.RequestEmailOtpSignupAsync(
    new SqlOSEmailOtpSignupStartRequest(
        DisplayName: "Jane Doe",
        Email: "jane@example.com",
        ClientId: "example-web",
        OrganizationName: "Example Co",
        OrganizationId: null,
        CustomFields: null),
    httpContext);

var login = await sqlosAuth.VerifyEmailOtpSignupAsync(
    new SqlOSEmailOtpSignupVerifyRequest(
        start.SignupToken,
        start.ChallengeToken,
        code),
    httpContext);

Headless browser clients use /sqlos/auth/headless/email-otp/start, /sqlos/auth/headless/email-otp/verify, /sqlos/auth/headless/signup/email-otp/start, and /sqlos/auth/headless/signup/email-otp/verify.

Run ./scripts/azure/setup-acs-email.sh --help for dry-run, DNS, and connection-string options.

Invite by Email

SqlOS can send one-time organization invitations that are bound to the invited email address. The hosted accept page is:

GET /sqlos/auth/invitations/accept?token=...

Admins can create, resend, revoke, and copy invite links from the organization Invitations tab in the Auth dashboard. Backend code can use the SDK facade:

var invite = await sqlosAuth.CreateEmailInvitationAsync(
    new SqlOSCreateEmailInvitationRequest(
        OrganizationId: organizationId,
        Email: "jane@example.com",
        Role: "member",
        ClientId: "web",
        RedirectUri: "https://app.example.com/auth/callback"),
    httpContext);

Invite acceptance works with Email OTP, password login/signup when enabled, and trusted SSO. See Email Invitations.

Todo Sample

If your goal is:

"I want SqlOS to work with hosted auth, resource metadata, and MCP-style public clients."

Start with:

dotnet run --project examples/SqlOS.Todo.AppHost/SqlOS.Todo.AppHost.csproj

That sample stays intentionally narrow:

  • hosted AuthPage first
  • passwordless email-code sign in/sign up when TodoSample__EnableEmailOtp=true
  • protected-resource metadata
  • audience-aware token validation
  • local preregistration with todo-local
  • public-client onboarding with CIMD and optional DCR

Read more:

Example App

The repo includes a full working example powered by .NET Aspire:

dotnet run --project examples/SqlOS.Example.AppHost/SqlOS.Example.AppHost.csproj

That starts SQL Server, the sample API, the Todo sample, and the web frontends in one stack. Use it when you want breadth: password login, headless auth, OIDC, SAML, sessions, org workflows, FGA, and the hosted-first MCP-oriented Todo flow side by side.

If you build headless auth on a different browser origin than the SqlOS host, make those browser requests credentialed so SqlOS can persist and reuse its auth-page session cookie. Follow-up /sqlos/auth/authorize?prompt=none requests should then silently succeed when that session exists, or return login_required when it does not.

URL
Dashboard http://localhost:5062/sqlos/
Auth Admin http://localhost:5062/sqlos/admin/auth/
FGA Admin http://localhost:5062/sqlos/admin/fga/
Web App http://localhost:3010/
Todo App http://localhost:5080/

Requirements

  • .NET 9.0+
  • SQL Server (any edition, including LocalDB)
  • EF Core 9.0+

Testing

dotnet build SqlOS.sln
./scripts/unit-tests.sh
./scripts/integration-tests.sh
./scripts/docs-check.sh

Repo Layout

src/SqlOS                                # The library
tests/SqlOS.Tests                        # Unit tests
tests/SqlOS.IntegrationTests             # Integration tests
tests/SqlOS.Benchmarks                   # Performance benchmarks
examples/SqlOS.Todo.Api                  # Canonical hosted-first Todo sample
examples/SqlOS.Todo.AppHost              # Aspire runner for the Todo sample
examples/SqlOS.Todo.IntegrationTests     # Todo sample end-to-end tests
examples/SqlOS.Example.Api               # ASP.NET API example
examples/SqlOS.Example.Web               # Next.js frontend example
examples/SqlOS.Example.AppHost           # Aspire orchestration

Documentation

Testing Email OTP in the Todo Sample

Run it like this:

ACS_COMMUNICATION_SERVICE_NAME=sqlos-dev-comm
AZURE_RESOURCE_GROUP=rg-sqlos-web-prod
ACS_FROM_ADDRESS=no-reply@sqlos.dev

ACS_CONN=$(az communication list-key \
  --name "$ACS_COMMUNICATION_SERVICE_NAME" \
  --resource-group "$AZURE_RESOURCE_GROUP" \
  --query primaryConnectionString \
  -o tsv)

TodoSample__EnableEmailOtp=true \
SqlOS__EmailOtp__AzureCommunicationServicesConnectionString="$ACS_CONN" \
SqlOS__EmailOtp__FromAddress="$ACS_FROM_ADDRESS" \
dotnet run --project examples/SqlOS.Todo.AppHost/SqlOS.Todo.AppHost.csproj

Testing the CLI Auth

In another terminal:

dotnet run --project examples/SqlOS.Todo.Cli -- login Open the printed URL, sign in through SqlOS AuthPage, approve the Todo CLI request, then run:

dotnet run --project examples/SqlOS.Todo.Cli -- whoami dotnet run --project examples/SqlOS.Todo.Cli -- add "Ship CLI OAuth" dotnet run --project examples/SqlOS.Todo.Cli -- list dotnet run --project examples/SqlOS.Todo.Cli -- toggle <todo-id>

Then open http://localhost:5080/.

Use Email code sign in or Email code sign up. In this mode the Todo app only starts the OAuth request; the SqlOS hosted auth page sends the OTP, verifies the code, creates the account on signup, and redirects back with the authorization code.

License

MIT

Product Compatible and additional computed target framework versions.
.NET net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
3.5.0 68 5/11/2026
3.4.0 64 5/10/2026
3.2.0 381 4/9/2026
3.1.0 189 3/29/2026
2.2.0 106 3/21/2026
2.1.0 103 3/16/2026
2.0.0 104 3/16/2026