Linbik.YARP
1.0.7
See the version list below for details.
dotnet add package Linbik.YARP --version 1.0.7
NuGet\Install-Package Linbik.YARP -Version 1.0.7
<PackageReference Include="Linbik.YARP" Version="1.0.7" />
<PackageVersion Include="Linbik.YARP" Version="1.0.7" />
<PackageReference Include="Linbik.YARP" />
paket add Linbik.YARP --version 1.0.7
#r "nuget: Linbik.YARP, 1.0.7"
#:package Linbik.YARP@1.0.7
#addin nuget:?package=Linbik.YARP&version=1.0.7
#tool nuget:?package=Linbik.YARP&version=1.0.7
Linbik.YARP
YARP (Yet Another Reverse Proxy) integration for Linbik multi-service authentication.
π Features
Multi-Service Token Management (v2.0+)
- Automatic Token Injection - Inject service-specific JWT tokens into proxied requests
- Per-Service Token Caching - Cache tokens by service package name
- Automatic Refresh - Refresh expired tokens using refresh token
- Authorization Code Exchange - Exchange authorization codes for tokens
- Backward Compatibility - Supports legacy single-token mode
Legacy Features (Deprecated)
- Single token provider (use multi-service provider)
- Manual token management (use automatic refresh)
π¦ Installation
dotnet add package Linbik.YARP
π§ Configuration
Basic Setup
services.AddLinbikYARP(options =>
{
options.LinbikServerUrl = "https://linbik.com";
options.TokenEndpoint = "/oauth/token";
options.RefreshEndpoint = "/oauth/refresh";
options.MainServiceId = Guid.Parse("your-main-service-guid");
options.MainServiceApiKey = "linbik_your_api_key";
options.EnableAutomaticRefresh = true;
options.TokenCacheExpirationMinutes = 55; // Refresh before 60min expiration
});
Simple Proxy Setup (Recommended)
For most use cases, use UseLinbikYarp():
// In Program.cs
// 1. Add services with configuration (fluent chain)
builder.Services.AddLinbik(builder.Configuration)
.AddLinbikJwtAuth();
// 2. Configure integration services in appsettings.json
// See IntegrationServices Configuration section below
var app = builder.Build();
// 3. Map integration proxy endpoint
app.UseLinbikYarp(); // Maps /{packageName}/{**path}
Endpoint Pattern: /{packageName}/{path} routes to the integration service's BaseUrl.
Example:
- Request:
GET /payment-gateway/api/charge - Routes to:
https://payment.example.com/api/charge - With:
Authorization: Bearer {jwt_from_cookie}
IntegrationServices Configuration
{
"YARP": {
"IntegrationServices": {
"payment-gateway": {
"BaseUrl": "https://payment.example.com"
},
"survey-service": {
"BaseUrl": "https://survey.example.com"
},
"courier-service": {
"BaseUrl": "https://courier.example.com"
}
}
}
}
How Cookie-Based JWT Injection Works
1. User authenticates β Integration tokens stored in cookies
Cookie: integration_payment-gateway = "eyJhbG..."
Cookie: integration_survey-service = "eyJhbG..."
2. Client request β /payment-gateway/api/charge
3. UseLinbikYarp():
a. Extract {packageName} from URL β "payment-gateway"
b. Read cookie: integration_payment-gateway
c. Lookup BaseUrl from IntegrationServices config
d. Proxy request to BaseUrl with Authorization header
4. Target service receives:
GET /api/charge
Authorization: Bearer eyJhbG...
YARP Route Configuration
{
"ReverseProxy": {
"Routes": {
"payment-route": {
"ClusterId": "payment-cluster",
"Match": {
"Path": "/api/payment/{**catch-all}"
},
"Transforms": [
{
"RequestHeader": "X-Service-Package",
"Set": "payment-gateway"
}
]
},
"courier-route": {
"ClusterId": "courier-cluster",
"Match": {
"Path": "/api/courier/{**catch-all}"
},
"Transforms": [
{
"RequestHeader": "X-Service-Package",
"Set": "courier-service"
}
]
}
},
"Clusters": {
"payment-cluster": {
"Destinations": {
"destination1": {
"Address": "https://payment-gateway.com"
}
}
},
"courier-cluster": {
"Destinations": {
"destination1": {
"Address": "https://courier-service.com"
}
}
}
}
}
}
π» Usage
1. Token Provider Interface
public interface ITokenProvider
{
// Authorization Methods (v2.0+)
Task<string?> GetTokenForServiceAsync(string servicePackage, HttpContext context);
Task ExchangeAuthorizationCodeAsync(string authorizationCode, HttpContext context);
Task RefreshTokensAsync(HttpContext context);
void ClearTokenCache(HttpContext context);
// Legacy Methods (Deprecated)
[Obsolete] Task<string?> GetTokenAsync(HttpContext context);
}
2. Exchange Authorization Code
After OAuth callback:
[HttpGet("/oauth/callback")]
public async Task<IActionResult> Callback(string code)
{
// Exchange code for tokens and store in cookies
await _tokenProvider.ExchangeAuthorizationCodeAsync(code, HttpContext);
return RedirectToAction("Dashboard");
}
3. YARP Transform for Token Injection
public class LinbikTokenTransform : RequestTransform
{
private readonly ITokenProvider _tokenProvider;
public LinbikTokenTransform(ITokenProvider tokenProvider)
{
_tokenProvider = tokenProvider;
}
public override async ValueTask ApplyAsync(RequestTransformContext context)
{
// Get target service package from request header
var servicePackage = context.HttpContext.Request.Headers["X-Service-Package"].ToString();
if (!string.IsNullOrEmpty(servicePackage))
{
// Get cached token for this service
var token = await _tokenProvider.GetTokenForServiceAsync(
servicePackage,
context.HttpContext
);
if (!string.IsNullOrEmpty(token))
{
// Inject token into Authorization header
context.ProxyRequest.Headers.Authorization =
new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
}
}
}
}
Register transform:
services.AddReverseProxy()
.LoadFromConfig(Configuration.GetSection("ReverseProxy"))
.AddTransforms(builderContext =>
{
builderContext.AddRequestTransform<LinbikTokenTransform>();
});
4. Complete Example
// Program.cs
var builder = WebApplication.CreateBuilder(args);
// Add Linbik services (fluent chain)
builder.Services.AddLinbik(builder.Configuration)
.AddLinbikJwtAuth(builder.Configuration)
.AddLinbikYarp(builder.Configuration);
var app = builder.Build();
app.EnsureLinbik();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
// Map Linbik endpoints (login, logout, refresh)
app.UseLinbikJwtAuth();
// Map integration proxy (automatic token injection from cookies)
app.UseLinbikYarp();
app.MapControllers();
app.Run();
π Token Flow
Initial Authorization
User β /linbik/login β Linbik β Authorization Code β /linbik/login (callback)
β
ExchangeAuthorizationCodeAsync()
β
Store tokens in cookies:
- Cookie: linbikRefreshToken = "refresh_abc..."
- Cookie: integration_payment-gateway = "jwt_token_1"
- Cookie: integration_courier-service = "jwt_token_2"
Request Proxying
Client Request β UseLinbikYarp()
β
Extract {packageName} from URL path
β
Read cookie: integration_{packageName}
β
Lookup BaseUrl from IntegrationServices config
β
Inject Authorization: Bearer {token}
β
Proxy to target service
π Cookie Storage Structure
Cookies format:
Cookie: linbikRefreshToken = "refresh_abc123..." (HttpOnly, Secure, 14 days)
Cookie: integration_payment-gateway = "eyJhbGci..." (HttpOnly, Secure, 1 hour)
Cookie: integration_courier-service = "eyJhbGci..." (HttpOnly, Secure, 1 hour)
π Security Features
Cookie-Based Token Storage
β
Tokens stored in HttpOnly cookies (prevents XSS)
β
Secure flag for HTTPS-only transmission
β
SameSite=None for cross-origin requests
β
Short expiration for integration tokens (1 hour)
β
Longer expiration for refresh token (14 days)
Automatic Token Refresh
β
Refresh tokens before expiration
β
Refresh token stored securely in HttpOnly cookie
β
Failed refresh clears cookies and requires re-authentication
Token Validation
β
Expiration check on each request
β
Service package name validation
β
Automatic cookie clearing on errors
π― Use Cases
1. E-Commerce Platform
MyShop (Main Service)
βββ Payment Gateway (Integration Service)
βββ Courier Service (Integration Service)
βββ Notification Service (Integration Service)
User makes purchase:
1. MyShop receives payment request
2. YARP routes to /api/payment/charge
3. LinbikTokenTransform injects payment-gateway JWT
4. Payment Gateway processes payment
5. MyShop routes to /api/courier/ship
6. LinbikTokenTransform injects courier-service JWT
7. Courier Service creates shipment
2. Microservices Architecture
API Gateway (YARP + Linbik)
βββ User Service (Integration Service)
βββ Order Service (Integration Service)
βββ Inventory Service (Integration Service)
βββ Analytics Service (Integration Service)
Each microservice gets its own JWT token with specific claims.
οΏ½ Service-to-Service (S2S) Communication (v2.4+)
Overview
S2S allows services to communicate directly without user context. Use cases:
- Webhooks/Callbacks: Payment Gateway β Merchant notification
- Background Sync: Inventory β Order synchronization
- Health Checks: Service monitoring between services
- Batch Processing: Data transfer between services
Key Difference from User-Context
| Aspect | User-Context | S2S |
|---|---|---|
| Token Type | User JWT (from cookie) | S2S JWT (from API) |
| Claims | UserId, Username, DisplayName | SourceServiceId, SourcePackageName, Role |
| Use Case | User-initiated requests | Service-initiated requests |
| Attribute | [LinbikUserServiceAuthorize] |
[LinbikS2SAuthorize] / [LinbikS2SAuthorize("Service")] / [LinbikS2SAuthorize("Linbik")] |
S2S Token Flow
1. Service A needs to call Service B
2. Service A β Linbik: POST /auth/s2s-token (ApiKey + TargetServiceIds)
3. Linbik β Service A: S2S JWT tokens for each target
4. Service A β Service B: Request with S2S JWT
5. Service B validates JWT with [LinbikS2SAuthorize]
Configuration
{
"Linbik": {
"ServiceId": "your-service-guid",
"ApiKey": "linbik_your_api_key",
"S2STokenEndpoint": "/auth/s2s-token",
"S2STokenLifetimeMinutes": 60,
"S2SAutoRefresh": true,
"S2SRefreshThreshold": 0.75,
"S2STargetServices": {
"payment-gateway": "guid-of-payment-service",
"courier-service": "guid-of-courier-service"
},
"YARP": {
"SourcePackageName": "my-service",
"S2STimeoutSeconds": 30,
"IntegrationServices": {
"payment-gateway": {
"TargetBaseUrl": "https://payment.example.com"
}
}
}
}
}
Using IS2SServiceClient
The IS2SServiceClient provides typed HTTP methods with:
- β Automatic S2S token injection
- β
LBaseResponse<T>enforcement - β Token caching and auto-refresh
- β Both config-based and dynamic targets
Config-Based Targets (Package Name)
public class MyController : ControllerBase
{
private readonly IS2SServiceClient _s2sClient;
// Call by package name (must be in config)
public async Task<IActionResult> SyncWithPayment()
{
var result = await _s2sClient.PostAsync<SyncRequest, SyncResponse>(
"payment-gateway", // package name from config
"/api/integration/s2s/sync",
new SyncRequest { EntityType = "order", EntityId = "123" }
);
if (!result.IsSuccess)
{
return BadRequest(result.FriendlyMessage);
}
return Ok(result.Data);
}
}
Dynamic Targets (Service ID) - Callbacks/Webhooks
For scenarios where target service is not pre-configured (e.g., callbacks):
public class PaymentController : ControllerBase
{
private readonly IS2SServiceClient _s2sClient;
// Notify merchant about payment completion
public async Task<IActionResult> NotifyMerchant(Order order)
{
// Merchant's service ID is stored in order (not in config!)
var merchantServiceId = order.MerchantLinbikServiceId;
var result = await _s2sClient.PostByIdAsync<PaymentNotification, NotifyResponse>(
merchantServiceId, // dynamic service ID
"/api/webhooks/payment",
new PaymentNotification
{
OrderId = order.Id.ToString(),
Status = "completed",
Amount = order.Amount
}
);
// ServiceUrl fetched automatically from Linbik
return result.IsSuccess ? Ok() : StatusCode(500);
}
}
Protecting S2S Endpoints
Use [LinbikS2SAuthorize] attribute on endpoints that receive S2S requests. With optional role parameter, you can restrict access to specific token types:
[ApiController]
[Route("api/integration")]
public class IntegrationController : ControllerBase
{
// S2S endpoint - accepts ANY S2S token (service or platform)
[LinbikS2SAuthorize]
[HttpPost("s2s/sync")]
public IActionResult S2SSync([FromBody] SyncRequest request)
{
var sourceServiceId = User.FindFirst("source_service_id")?.Value;
var sourcePackageName = User.FindFirst("source_package_name")?.Value;
var role = User.FindFirst("role")?.Value; // "Service" or "Linbik"
return Ok(new { success = true, sourceServiceId, sourcePackageName });
}
// S2S webhook - only accepts service-to-service tokens (role=Service)
[LinbikS2SAuthorize("Service")]
[HttpPost("s2s/webhook/{eventType}")]
public IActionResult S2SWebhook(string eventType, [FromBody] WebhookPayload payload)
{
var sourceServiceId = User.FindFirst("source_service_id")?.Value;
return Ok(new { processed = true, eventType });
}
// Platform event - only accepts platform tokens (role=Linbik)
// Use for: key rotation, integration lifecycle, admin commands
[LinbikS2SAuthorize("Linbik")]
[HttpPost("s2s/platform-event")]
public IActionResult OnPlatformEvent([FromBody] PlatformEventPayload payload)
{
// Only Linbik platform can call this endpoint
return Ok(new { processed = true });
}
}
S2S Claim Types
Claims available in S2S JWT tokens:
token_type = "s2s"
source_service_id = "guid-of-calling-service"
source_package_name = "calling-service-package"
role = "Service" | "Linbik" // NEW: distinguishes service vs platform tokens
iat = issued at timestamp
exp = expiration timestamp
iss = "Linbik"
aud = "target-service-guid"
π‘οΈ Cross-Scheme Protection:
OnTokenValidatedevents in Linbik.Server ensure that S2S tokens cannot be used on[LinbikUserServiceAuthorize]endpoints and vice versa. This is enforced viatoken_typeclaim validation.
LinbikProxyPolicy
AddCommonYarpServices() automatically registers the LinbikProxyPolicy authorization policy as RequireAuthenticatedUser(). This policy is referenced in YARP route configurations and does not need to be defined by the consumer application.
IS2STokenProvider API
For advanced scenarios, you can use the token provider directly:
public interface IS2STokenProvider
{
// Config-based (package name)
Task<string?> GetS2STokenAsync(string packageName, ...);
Task<LinbikS2SIntegration?> GetS2SIntegrationAsync(string packageName, ...);
// Dynamic (service ID) - for callbacks
Task<LinbikS2SIntegration?> GetS2SIntegrationByIdAsync(Guid targetServiceId, ...);
// Cache management
Task RefreshS2STokensAsync(...);
void ClearCache();
TimeSpan? GetTimeUntilExpiry();
}
π Migration from v1.x
// β Old way (v1.x - Single token)
var token = await _tokenProvider.GetTokenAsync(context);
context.Request.Headers["Authorization"] = $"Bearer {token}";
// β
New way (v2.0+ - Per-service tokens from cookies)
var token = await _tokenProvider.GetTokenForServiceAsync("payment-gateway", context);
context.Request.Headers["Authorization"] = $"Bearer {token}";
// Even better: Use UseLinbikYarp (automatic)
// No manual token management needed!
// Tokens are read from cookies and injected automatically.
π Documentation
π License
This library is currently a work in progress and is not ready for production use.
Contact: info@linbik.com
Version: 2.4.0
Last Updated: 28 Εubat 2026
| Product | Versions 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. |
-
net9.0
- Linbik.Core (>= 1.0.7)
- Yarp.ReverseProxy (>= 2.3.0)
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.0-preview.3 | 52 | 5/30/2026 |
| 1.2.0-preview.2 | 82 | 4/26/2026 |
| 1.2.0-preview.1 | 108 | 4/2/2026 |
| 1.1.0 | 406 | 12/5/2025 |
| 1.1.0-beta0007 | 104 | 3/6/2026 |
| 1.0.7 | 160 | 3/6/2026 |
| 1.0.1 | 233 | 12/14/2025 |
| 1.0.0 | 116 | 3/6/2026 |
| 0.60.1 | 112 | 3/6/2026 |
| 0.46.0 | 366 | 11/30/2025 |
| 0.45.12 | 376 | 11/30/2025 |
| 0.45.7 | 242 | 8/31/2025 |
| 0.45.6 | 215 | 8/31/2025 |
| 0.45.5 | 246 | 8/27/2025 |
| 0.44.0 | 274 | 8/24/2025 |
| 0.42.0 | 136 | 8/23/2025 |
| 0.41.0 | 139 | 8/15/2025 |
| 0.40.0 | 194 | 8/14/2025 |
| 0.39.0 | 199 | 8/14/2025 |
| 0.38.0 | 200 | 8/14/2025 |