Dosaic.Plugins.Authorization.Zitadel 1.2.31

dotnet add package Dosaic.Plugins.Authorization.Zitadel --version 1.2.31
                    
NuGet\Install-Package Dosaic.Plugins.Authorization.Zitadel -Version 1.2.31
                    
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="Dosaic.Plugins.Authorization.Zitadel" Version="1.2.31" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Dosaic.Plugins.Authorization.Zitadel" Version="1.2.31" />
                    
Directory.Packages.props
<PackageReference Include="Dosaic.Plugins.Authorization.Zitadel" />
                    
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 Dosaic.Plugins.Authorization.Zitadel --version 1.2.31
                    
#r "nuget: Dosaic.Plugins.Authorization.Zitadel, 1.2.31"
                    
#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 Dosaic.Plugins.Authorization.Zitadel@1.2.31
                    
#: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=Dosaic.Plugins.Authorization.Zitadel&version=1.2.31
                    
Install as a Cake Addin
#tool nuget:?package=Dosaic.Plugins.Authorization.Zitadel&version=1.2.31
                    
Install as a Cake Tool

Dosaic.Plugins.Authorization.Zitadel

Dosaic.Plugins.Authorization.Zitadel is a plugin that provides OAuth2 token-introspection authentication against a Zitadel server. It registers the official Zitadel authentication scheme, validates Bearer tokens via the Zitadel introspection endpoint (using a JWT application profile), and exposes a management service for programmatically creating and managing service accounts and user accounts.

Features

  • OAuth2 token introspection — validates Bearer tokens through Zitadel's introspection endpoint using an Application JWT profile; no manual key fetching required
  • Built-in response caching — introspection results are cached in the distributed cache to reduce round-trips; cache duration and key prefix are configurable
  • Discovery policy control — configure whether HTTPS is required for the discovery endpoint, and whether issuer/endpoint validation is enforced
  • IManagementService — injectable service to list service accounts, create service accounts (with JWT keys), create human user accounts, and obtain Bearer tokens for service accounts
  • Authentication failure logging — all authentication failures are logged automatically via the plugin's ILogger

Installation

dotnet add package Dosaic.Plugins.Authorization.Zitadel

Or add the package reference directly to your .csproj:

<PackageReference Include="Dosaic.Plugins.Authorization.Zitadel" Version="" />

Configuration

The plugin is configured under the Zitadel key via the [Configuration("Zitadel")] attribute on ZitadelConfiguration.

Configuration class

[Configuration("Zitadel")]
public class ZitadelConfiguration
{
    // Zitadel project ID (used to build the audience scope for service account tokens)
    public required string ProjectId { get; set; }

    // Zitadel organization ID (used when creating users/service accounts)
    public required string OrganizationId { get; set; }

    // Base URL of the Zitadel instance, e.g. "https://my-org.zitadel.cloud"
    public required string Host { get; set; }

    // When true (default), the authority URL uses https://; set to false for local/dev environments
    public bool UseHttps { get; set; } = true;

    // Whether to validate the issuer during OIDC discovery (default: true)
    public bool ValidateIssuer { get; set; } = true;

    // Whether to validate discovery endpoints (default: true)
    public bool ValidateEndpoints { get; set; } = true;

    // Enable caching of introspection results (default: true)
    public bool EnableCaching { get; set; } = true;

    // How long introspection results are cached (default: 1 minute)
    public int CacheDurationInMinutes { get; set; } = 1;

    // Prefix for cache keys (default: "ZITADEL_")
    public string CacheKeyPrefix { get; set; } = "ZITADEL_";

    // JSON string of the Zitadel Application JWT profile used for introspection
    public required string JwtProfile { get; set; }

    // JSON string of the Zitadel Service Account JWT profile used by IManagementService
    public string ServiceAccount { get; set; }
}

Note: Passing an http:// host while UseHttps = true will throw an InvalidConfigurationException at startup.

appsettings.yml example

Zitadel:
  projectId: "23456789012345678"
  organizationId: "123456789012345678"
  host: https://my-org.zitadel.cloud
  useHttps: true
  validateIssuer: true
  validateEndpoints: true
  enableCaching: true
  cacheDurationInMinutes: 1
  cacheKeyPrefix: "ZITADEL_"
  jwtProfile: |
    {
      "type": "application",
      "keyId": "...",
      "key": "-----BEGIN RSA PRIVATE KEY-----\n...\n-----END RSA PRIVATE KEY-----\n",
      "appId": "...",
      "clientId": "..."
    }
  serviceAccount: |
    {
      "type": "serviceaccount",
      "keyId": "...",
      "key": "-----BEGIN RSA PRIVATE KEY-----\n...\n-----END RSA PRIVATE KEY-----\n",
      "userId": "..."
    }

Disable HTTPS for local development

Zitadel:
  host: http://localhost:8080
  useHttps: false
  validateIssuer: false
  validateEndpoints: false
  projectId: "1"
  organizationId: "1"
  jwtProfile: "{}"
  serviceAccount: "{}"

When useHttps is false, the plugin uses http:// for the authority URL and relaxes discovery validation — useful when running Zitadel locally via Docker.

Usage

Minimal API endpoints

public class MyEndpoints : IPluginEndpointsConfiguration
{
    public void ConfigureEndpoints(IEndpointRouteBuilder endpointRouteBuilder, IServiceProvider serviceProvider)
    {
        endpointRouteBuilder.MapGet("/hello", () => "Hello World!")
            .RequireAuthorization();

        endpointRouteBuilder.MapGet("/admin", () => "Admin area")
            .RequireAuthorization(policy => policy.RequireRole("admin"));
    }
}

MVC controllers

[ApiController]
[Route("api/[controller]")]
public class ItemsController : ControllerBase
{
    [HttpGet]
    [Authorize]
    public IActionResult GetAll() => Ok();

    [HttpGet("public")]
    [AllowAnonymous]
    public IActionResult Public() => Ok("no auth needed");
}

How token validation works

  1. The Authorization: Bearer <token> header is extracted from the incoming request.
  2. The token is sent to Zitadel's introspection endpoint (<host>/oauth/v2/introspect) together with the Application JWT profile loaded from JwtProfile.
  3. Zitadel returns the token metadata (active, subject, scopes, roles, etc.) which is mapped to the ClaimsPrincipal.
  4. If EnableCaching = true, the introspection result is stored in the distributed cache under <CacheKeyPrefix><token-hash> for CacheDurationInMinutes to avoid repeated network calls.
  5. On any authentication failure the error is logged at Error level by the plugin's ILogger<ZitadelPlugin>.

Management service

IManagementService is registered automatically and can be injected to manage Zitadel users programmatically.

public interface IManagementService
{
    // List all machine (service account) users in the configured organization
    Task<List<ListServiceAccountResultItem>> ListServiceAccountsAsync();

    // Create a new service account user and return its JWT key content
    Task<ServiceAccountCreationResult> CreateServiceAccountAsync(
        string userid, string name, Timestamp keyExpirationTime, string description = "");

    // Obtain a Bearer token for a service account identified by its JSON key string
    Task<string> GetBearerTokenForServiceAccountAsync(string jsonString);

    // Create a human user account with an initial password
    Task<string> CreateUserAccountAsync(
        string userid, string email,
        string displayName, string givenName, string familyName, string password);
}

public record ServiceAccountCreationResult(string UserId, string KeyId, string KeyContent);

public record ListServiceAccountResultItem(
    string UserId, string State, string Name, string Description,
    bool HasSecret, string Owner, DateTime CreatedAt, DateTime UpdatedAt);

Example — list service accounts

public class MyService(IManagementService management)
{
    public async Task PrintServiceAccounts()
    {
        var accounts = await management.ListServiceAccountsAsync();
        foreach (var account in accounts)
            Console.WriteLine($"{account.UserId} — {account.Name} ({account.State})");
    }
}

Example — create a service account

var expirationTime = Timestamp.FromDateTime(DateTime.UtcNow.AddYears(1));
var result = await management.CreateServiceAccountAsync(
    userid: "my-service-id",
    name: "my-service",
    keyExpirationTime: expirationTime,
    description: "Service account for my-service");

Console.WriteLine($"UserId: {result.UserId}");
Console.WriteLine($"Key JSON: {result.KeyContent}");

Calling an API protected by Zitadel from a service account client

Use GetBearerTokenForServiceAccountAsync (or call the Zitadel SDK directly) to obtain a Bearer token for a service account and attach it to outgoing HTTP requests.

// Using IManagementService (reads service account JSON from configuration)
var token = await managementService.GetBearerTokenForServiceAccountAsync(jwtProfileJsonString);

var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Get, "http://<api-url>/api/endpoint");
request.Headers.Add("Authorization", "Bearer " + token);
var response = await client.SendAsync(request);

Or call the Zitadel SDK directly when you need additional scopes:

var authOptions = new ServiceAccount.AuthOptions();
// be sure to include this scope or your introspection will not be able to verify token for the indented project
authOptions.AdditionalScopes.Add("urn:zitadel:iam:org:project:id:123456789012345678:aud");

authOptions.AdditionalScopes.Add("offline_access");
authOptions.AdditionalScopes.Add("profile");
authOptions.AdditionalScopes.Add("email");

var token = await ServiceAccount.LoadFromJsonString("<jwtProfileJsonString>")
    .AuthenticateAsync("<HostUrl>", authOptions);

var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Get, "http://<api-url>/api/endpoint");
request.Headers.Add("Authorization", "Bearer " + token);
var response = await client.SendAsync(request);

The urn:zitadel:iam:org:project:id:<projectId>:aud scope tells Zitadel to include your project's audience in the token so the receiving API's introspection call succeeds.

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  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
1.2.31 45 5/28/2026
1.2.30 105 5/7/2026
1.2.29 100 5/5/2026
1.2.28 110 4/30/2026
1.2.27 95 4/29/2026
1.2.26 91 4/29/2026
1.2.25 114 4/27/2026
1.2.24 105 4/21/2026
1.2.23 112 4/14/2026
1.2.22 104 4/10/2026
1.2.21 108 4/10/2026
1.2.20 101 4/10/2026
1.2.19 104 4/9/2026
1.2.18 110 4/2/2026
1.2.17 100 4/1/2026
1.2.16 98 4/1/2026
1.2.15 100 3/31/2026
1.2.14 110 3/30/2026
1.2.13 110 3/26/2026
1.2.12 122 3/24/2026