Voyager.Common.Proxy.Server.Owin 1.7.7

There is a newer prerelease version of this package available.
See the version list below for details.
dotnet add package Voyager.Common.Proxy.Server.Owin --version 1.7.7
                    
NuGet\Install-Package Voyager.Common.Proxy.Server.Owin -Version 1.7.7
                    
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="Voyager.Common.Proxy.Server.Owin" Version="1.7.7" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Voyager.Common.Proxy.Server.Owin" Version="1.7.7" />
                    
Directory.Packages.props
<PackageReference Include="Voyager.Common.Proxy.Server.Owin" />
                    
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 Voyager.Common.Proxy.Server.Owin --version 1.7.7
                    
#r "nuget: Voyager.Common.Proxy.Server.Owin, 1.7.7"
                    
#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 Voyager.Common.Proxy.Server.Owin@1.7.7
                    
#: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=Voyager.Common.Proxy.Server.Owin&version=1.7.7
                    
Install as a Cake Addin
#tool nuget:?package=Voyager.Common.Proxy.Server.Owin&version=1.7.7
                    
Install as a Cake Tool

Voyager.Common.Proxy.Server.Owin

OWIN integration for Voyager.Common.Proxy.Server - automatically generates HTTP endpoints from service interfaces for .NET Framework 4.8.

Installation

Install-Package Voyager.Common.Proxy.Server.Owin

Quick Start

// Define your service interface
public interface IUserService
{
    Task<User> GetUserAsync(int id, CancellationToken cancellationToken);
    Task<User> CreateUserAsync(CreateUserRequest request);
    Task<IEnumerable<User>> SearchUsersAsync(string name, int? limit);
    Task DeleteUserAsync(int id);
}

// Configure in OWIN Startup
public class Startup
{
    public void Configuration(IAppBuilder app)
    {
        var container = new UnityContainer();
        container.RegisterType<IUserService, UserService>();

        // Using factory
        app.Use(ServiceProxyOwinMiddleware.Create<IUserService>(
            () => container.Resolve<IUserService>()));

        // Or using IServiceProvider
        app.Use(ServiceProxyOwinMiddleware.Create<IUserService>(serviceProvider));
    }
}

This generates the following endpoints:

Method Route Parameters
GET /User/GetUser/{id} id from route, cancellationToken injected
POST /User/CreateUser request from body
GET /User/SearchUsers name, limit from query string
DELETE /User/DeleteUser/{id} id from route

Features

  • Automatic endpoint generation from service interfaces
  • Convention-based routing matching Voyager.Common.Proxy.Client
  • Parameter binding from route, query string, and request body
  • Result<T> support - automatically unwraps successful results or returns appropriate error responses
  • Authorization support - [RequireAuthorization] and [AllowAnonymous] attributes
  • Raw OWIN signatures - uses Func<IDictionary<string, object>, Task> for maximum compatibility
  • Works with any OWIN host - Katana, Microsoft.Owin, custom hosts

Authorization

The middleware supports authorization using [RequireAuthorization] and [AllowAnonymous] attributes:

// Require authorization for all methods
[RequireAuthorization]
public interface IUserService
{
    Task<Result<User>> GetUserAsync(int id);
    Task<Result<User>> CreateUserAsync(CreateUserRequest request);

    // Allow anonymous access to this method
    [AllowAnonymous]
    Task<Result<User>> GetPublicProfileAsync(int id);
}

// Require specific roles
[RequireAuthorization(Roles = "Admin,Manager")]
public interface IAdminService
{
    Task<Result> DeleteUserAsync(int id);
}

The middleware checks the OWIN environment for the authenticated user:

  • server.User
  • owin.User
  • Microsoft.Owin.Security.User (Katana)

Authorization Responses

  • 401 Unauthorized - User is not authenticated
  • 403 Forbidden - User is authenticated but doesn't have required role(s)

OWIN Authentication Setup

Make sure authentication middleware runs before the service proxy middleware:

public void Configuration(IAppBuilder app)
{
    // Authentication middleware first
    app.UseCookieAuthentication(new CookieAuthenticationOptions
    {
        AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie
    });

    // Then service proxy
    app.Use(ServiceProxyOwinMiddleware.Create<IUserService>(factory));
}

Note: Policy-based authorization is an ASP.NET Core feature and is not fully supported in OWIN. When policies are specified, the middleware only checks if the user is authenticated.

Permission Checking

For fine-grained permission control, use the options-based configuration with a permission checker. This runs before each method invocation:

// Simple inline permission checking
app.Use(ServiceProxyOwinMiddleware.Create<IVIPService>(options =>
{
    options.ServiceFactory = () => container.Resolve<IVIPService>();
    options.PermissionChecker = async ctx =>
    {
        if (ctx.User?.Identity?.IsAuthenticated != true)
            return PermissionResult.Unauthenticated();

        // Check based on method and parameters
        if (ctx.Method.Name == "DeleteAsync" && !ctx.User.IsInRole("Admin"))
            return PermissionResult.Denied("Admin role required");

        return PermissionResult.Granted();
    };
}));

Context-Aware Factory

When your service needs access to the request context (e.g., to inject per-request identity):

app.Use(ServiceProxyOwinMiddleware.Create<IVIPService>(options =>
{
    options.ContextAwareFactory = env =>
    {
        var user = env["server.User"] as ClaimsPrincipal;
        var identity = PilotIdentityFactory.Create(user);
        return new VIPService(identity, actionModule);
    };
    options.PermissionChecker = async ctx =>
    {
        // Permission logic here
        return PermissionResult.Granted();
    };
}));

Typed Permission Checker

For complex scenarios, implement IServicePermissionChecker<TService>:

public class VIPServicePermissionChecker : IServicePermissionChecker<IVIPService>
{
    private readonly ActionModule _actionModule;

    public VIPServicePermissionChecker(ActionModule actionModule)
    {
        _actionModule = actionModule;
    }

    public async Task<PermissionResult> CheckPermissionAsync(PermissionContext context)
    {
        var identity = PilotIdentity.FromPrincipal(context.User);
        var action = _actionModule.GetActionForMethod(context.Method.Name);

        // Use existing Action pattern for permission checking
        var result = await action.CheckPermissionsOnlyAsync(
            BuildRequest(context.Parameters), identity);

        return result.IsSuccess
            ? PermissionResult.Granted()
            : PermissionResult.Denied(result.Error.Message);
    }
}

// Usage
app.Use(ServiceProxyOwinMiddleware.Create<IVIPService>(options =>
{
    options.ServiceFactory = () => container.Resolve<IVIPService>();
    options.PermissionCheckerInstance = container.Resolve<VIPServicePermissionChecker>();
}));

PermissionContext

The permission checker receives a PermissionContext with:

Property Description
User The authenticated IPrincipal (may be null)
ServiceType The service interface type
Method The MethodInfo being invoked
Endpoint The EndpointDescriptor with route info
Parameters Dictionary of parameter name to deserialized value
RawContext The OWIN environment dictionary

PermissionResult

Return values from permission checker:

PermissionResult.Granted()              // Allow the request
PermissionResult.Denied("reason")       // 403 Forbidden
PermissionResult.Unauthenticated()      // 401 Unauthorized

OWIN Compatibility

This package uses raw OWIN delegate signatures instead of IAppBuilder extensions for maximum compatibility with SDK-style projects. The middleware factory returns a standard OWIN middleware wrapper:

Func<AppFunc, AppFunc> middleware = ServiceProxyOwinMiddleware.Create<IUserService>(factory);

Diagnostics and Observability

The OWIN middleware supports diagnostic events for logging and observability. Configure diagnostics through the options.

Basic Setup

using Voyager.Common.Proxy.Diagnostics;
using Microsoft.Extensions.Logging;

public class Startup
{
    public void Configuration(IAppBuilder app)
    {
        var loggerFactory = LoggerFactory.Create(builder => builder.AddConsole());
        var logger = loggerFactory.CreateLogger<LoggingProxyDiagnostics>();
        var diagnosticsHandler = new LoggingProxyDiagnostics(logger);

        app.Use(ServiceProxyOwinMiddleware.Create<IUserService>(options =>
        {
            options.ServiceFactory = () => container.Resolve<IUserService>();
            options.DiagnosticsHandlers = new IProxyDiagnostics[] { diagnosticsHandler };
        }));
    }
}

Adding User Context

To include user information in diagnostic events:

public class OwinProxyRequestContext : IProxyRequestContext
{
    private readonly IDictionary<string, object> _environment;

    public OwinProxyRequestContext(IDictionary<string, object> environment)
    {
        _environment = environment;
    }

    public string? UserLogin
    {
        get
        {
            var user = _environment.TryGetValue("server.User", out var u) ? u as ClaimsPrincipal : null;
            return user?.Identity?.Name;
        }
    }

    public string? UnitId
    {
        get
        {
            var user = _environment.TryGetValue("server.User", out var u) ? u as ClaimsPrincipal : null;
            return user?.FindFirst("unit_id")?.Value;
        }
    }

    public string? UnitType => "Agent";
    public IReadOnlyDictionary<string, string>? CustomProperties => null;
}

// Configure with request context factory
app.Use(ServiceProxyOwinMiddleware.Create<IUserService>(options =>
{
    options.ServiceFactory = () => container.Resolve<IUserService>();
    options.DiagnosticsHandlers = new IProxyDiagnostics[] { diagnosticsHandler };
    options.RequestContextFactory = env => new OwinProxyRequestContext(env);
}));

Custom Diagnostics Handler

public class MetricsDiagnostics : ProxyDiagnosticsHandler
{
    private readonly IMetricsService _metrics;

    public MetricsDiagnostics(IMetricsService metrics) => _metrics = metrics;

    public override void OnRequestCompleted(RequestCompletedEvent e)
    {
        _metrics.RecordHistogram("owin_request_duration_ms", e.Duration.TotalMilliseconds,
            new[] { ("service", e.ServiceName), ("method", e.MethodName) });
    }
}

// Multiple handlers
options.DiagnosticsHandlers = new IProxyDiagnostics[]
{
    new LoggingProxyDiagnostics(logger),
    new MetricsDiagnostics(metricsService)
};

Server-Side Events

Event When Emitted
OnRequestStarting When request is received
OnRequestCompleted After response is sent (success or business error)
OnRequestFailed When exception occurs during processing

Note: Server-side does not emit OnRetryAttempt or OnCircuitBreakerStateChanged - these are client-side patterns.

Target Framework

  • net48 (.NET Framework 4.8)
  • Voyager.Common.Proxy.Client - Client-side proxy generation
  • Voyager.Common.Proxy.Server.AspNetCore - ASP.NET Core server integration
Product Compatible and additional computed target framework versions.
.NET Framework net48 is compatible.  net481 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.7.8-preview.1 29 2/9/2026
1.7.7 38 2/6/2026
1.7.7-preview.1.1 29 2/6/2026