Linbik.Server
1.1.0
dotnet add package Linbik.Server --version 1.1.0
NuGet\Install-Package Linbik.Server -Version 1.1.0
<PackageReference Include="Linbik.Server" Version="1.1.0" />
<PackageVersion Include="Linbik.Server" Version="1.1.0" />
<PackageReference Include="Linbik.Server" />
paket add Linbik.Server --version 1.1.0
#r "nuget: Linbik.Server, 1.1.0"
#:package Linbik.Server@1.1.0
#addin nuget:?package=Linbik.Server&version=1.1.0
#tool nuget:?package=Linbik.Server&version=1.1.0
Linbik.Server
Server-side repository interfaces, JWT validation, and authentication middleware for Linbik integration services.
🚀 Features
Authorization Server (v2.0+)
- Authorization Code Management - Generate, validate, and consume codes
- Refresh Token Management - Issue and revoke refresh tokens
- User Profile Management - Retrieve user profile data
- Service Integration - Manage service-to-service relationships
- User Consent Management - Track user permissions for services
- Repository Pattern - Clean abstraction for data access
Integration Service Features (v2.1+)
- IntegrationTokenValidator - RSA public key based JWT validation
- IntegrationAuthMiddleware - Automatic JWT validation in request pipeline
- LinbikTokenClaims - Strongly typed claims for user identity
- HttpContext Extensions - Easy access to validated claims
Legacy Features (Deprecated)
- Simple login validation (use authorization code flow)
- Basic app validation (use service validation)
📦 Installation
dotnet add package Linbik.Server
🔧 Configuration
Basic Setup (Authorization Server)
services.AddLinbikServer(options =>
{
options.ServerUrl = "https://linbik.com";
options.AuthorizationEndpoint = "/auth/{serviceId}/{codeChallenge?}";
options.TokenEndpoint = "/oauth/token";
options.RefreshEndpoint = "/oauth/refresh";
options.ConsentEndpoint = "/auth/consent";
options.RequireHttps = true;
options.RequirePKCE = false; // Optional but recommended
});
Integration Service Setup (JWT Validation)
For services that receive integration tokens from main services:
// In Program.cs
// 1. Add services with public key configuration
builder.Services.AddLinbikServer(options =>
{
options.ServiceId = "your-service-guid";
options.PublicKey = builder.Configuration["Linbik:PublicKey"];
options.ClockSkewMinutes = 5;
options.ValidateAudience = true;
options.ValidateIssuer = true;
});
// 2. Use authentication middleware
app.UseLinbikIntegrationAuth();
// 3. Access claims in controllers
[ApiController]
public class PaymentController : ControllerBase
{
[HttpPost("/charge")]
public IActionResult Charge([FromBody] ChargeRequest request)
{
// Get validated claims from middleware
var claims = HttpContext.GetLinbikClaims();
var userId = HttpContext.GetLinbikUserId();
var userName = HttpContext.GetLinbikUserName();
// Process payment for this Linbik user...
return Ok();
}
}
ServerOptions Properties
| Property | Type | Default | Description |
|---|---|---|---|
ServiceId |
string |
null | Your integration service ID (for audience validation) |
PublicKey |
string |
null | RSA public key (PEM or Base64 format) |
ClockSkewMinutes |
int |
5 | Allowed clock skew for token validation |
ValidateAudience |
bool |
true | Validate JWT audience claim |
ValidateIssuer |
bool |
true | Validate JWT issuer claim |
📚 Main Interface
ILinbikServerRepository
Complete repository interface for authorization operations:
public interface ILinbikServerRepository
{
// Authorization Code Management
Task<string> CreateAuthorizationCodeAsync(/* parameters */);
Task<(bool isValid, AuthorizationCodeData? data)> ValidateAndUseAuthorizationCodeAsync(string code, Guid serviceId);
// Refresh Token Management
Task<string> CreateRefreshTokenAsync(/* parameters */);
Task<(bool isValid, RefreshTokenData? data)> ValidateRefreshTokenAsync(string token, Guid serviceId);
Task<bool> RevokeRefreshTokenAsync(string token);
// User & Profile Management
Task<UserData?> GetUserByIdAsync(Guid userId);
Task<ProfileData?> GetUserProfileAsync(Guid userId, Guid profileId);
// Service Management
Task<ServiceData?> GetServiceByIdAsync(Guid serviceId);
Task<ServiceData?> GetServiceByApiKeyAsync(string apiKey);
Task<List<ServiceData>> GetGrantedIntegrationServicesAsync(Guid userId, Guid mainServiceId);
// User Consent Management
Task SaveUserConsentsAsync(Guid userId, Guid mainServiceId, Guid profileId, List<Guid> grantedServiceIds);
// Legacy Methods (Deprecated)
[Obsolete] Task<bool> ValidateUserCredentialsAsync(string email, string password);
}
🔐 JWT Validation (Integration Services)
IntegrationTokenValidator
Validates integration JWT tokens using RSA public key:
public class IntegrationTokenValidator
{
public IntegrationTokenValidator(IOptions<ServerOptions> options);
// Validate JWT and extract claims
public LinbikTokenClaims? ValidateToken(string token);
}
LinbikTokenClaims Model
public class LinbikTokenClaims
{
public string UserId { get; set; }
public string UserName { get; set; }
public string NickName { get; set; }
public string Subject { get; set; }
public string Audience { get; set; }
public string Issuer { get; set; }
public DateTime IssuedAt { get; set; }
public DateTime ExpiresAt { get; set; }
}
HttpContext Extensions
Easy access to validated Linbik claims:
// Get all claims
var claims = HttpContext.GetLinbikClaims();
// Get individual values
var userId = HttpContext.GetLinbikUserId();
var userName = HttpContext.GetLinbikUserName();
var nickName = HttpContext.GetLinbikNickName();
// Check if user is authenticated via Linbik
var isAuthenticated = HttpContext.HasLinbikUser();
Complete Integration Example
// Program.cs
var builder = WebApplication.CreateBuilder(args);
// Add Linbik server with public key
builder.Services.AddLinbikServer(options =>
{
options.ServiceId = "payment-gateway";
options.PublicKey = builder.Configuration["Linbik:PublicKey"];
});
var app = builder.Build();
// Enable Linbik auth middleware
app.UseLinbikIntegrationAuth();
app.MapPost("/charge", (HttpContext ctx, ChargeRequest request) =>
{
if (!ctx.HasLinbikUser())
return Results.Unauthorized();
var userId = ctx.GetLinbikUserId();
// Process payment...
return Results.Ok(new { success = true });
});
app.Run();
💻 Usage Examples
1. Authorization Code Flow
Generate Authorization Code
public class AuthController : Controller
{
private readonly ILinbikServerRepository _repository;
[HttpGet("/auth/{serviceId}/{codeChallenge?}")]
public async Task<IActionResult> Authorize(Guid serviceId, string? codeChallenge)
{
// 1. Check user authentication (cookie)
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
if (userId == null) return RedirectToAction("Login");
// 2. Get service details
var service = await _repository.GetServiceByIdAsync(serviceId);
if (service == null) return NotFound("Service not found");
// 3. Get integration services (if any)
var integrations = await _repository.GetGrantedIntegrationServicesAsync(
Guid.Parse(userId),
serviceId
);
// 4. Show consent screen or auto-generate code
// (Implementation depends on your UI)
// 5. Generate authorization code
var code = await _repository.CreateAuthorizationCodeAsync(
serviceId: serviceId,
userId: Guid.Parse(userId),
profileId: userProfileId,
grantedIntegrationServiceIds: integrations.Select(i => i.Id).ToArray(),
codeChallenge: codeChallenge,
expiresAt: DateTime.UtcNow.AddMinutes(10)
);
// 6. Redirect to service callback
return Redirect($"{service.BaseUrl}{service.CallbackPath}?code={code}");
}
}
Token Exchange
[HttpPost("/oauth/token")]
public async Task<IActionResult> ExchangeToken()
{
// 1. Extract headers
var apiKey = Request.Headers["ApiKey"].ToString();
var code = Request.Headers["Code"].ToString();
// 2. Validate service by API key
var service = await _repository.GetServiceByApiKeyAsync(apiKey);
if (service == null) return Unauthorized("Invalid API key");
// 3. Validate and consume authorization code
var (isValid, authData) = await _repository.ValidateAndUseAuthorizationCodeAsync(
code,
service.Id
);
if (!isValid) return BadRequest("Invalid or expired code");
// 4. Get user profile
var profile = await _repository.GetUserProfileAsync(
authData.UserId,
authData.ProfileId
);
// 5. Get integration services
var integrationServices = await _repository.GetGrantedIntegrationServicesAsync(
authData.UserId,
service.Id
);
// 6. Generate JWT for each integration service
var integrationTokens = new List<IntegrationToken>();
foreach (var integration in integrationServices)
{
var token = await _jwtHelper.CreateTokenAsync(
claims: new[] {
new Claim("userId", authData.UserId.ToString()),
new Claim("userName", profile.UserName),
new Claim("nickName", profile.NickName)
},
privateKey: integration.PrivateKey,
audience: integration.Id.ToString(),
expirationMinutes: 60
);
integrationTokens.Add(new IntegrationToken
{
ServiceId = integration.Id,
ServiceName = integration.Name,
ServicePackage = integration.PackageName,
BaseUrl = integration.BaseUrl,
Token = token,
ExpiresIn = 3600,
ExpiresAt = DateTime.UtcNow.AddMinutes(60)
});
}
// 7. Create refresh token
var refreshToken = await _repository.CreateRefreshTokenAsync(
serviceId: service.Id,
userId: authData.UserId,
profileId: authData.ProfileId,
grantedIntegrationServiceIds: integrationServices.Select(i => i.Id).ToArray(),
expiresAt: DateTime.UtcNow.AddDays(30)
);
// 8. Return response
return Ok(new MultiServiceTokenResponse
{
UserId = authData.UserId,
UserName = profile.UserName,
NickName = profile.NickName,
Integrations = integrationTokens,
RefreshToken = refreshToken,
RefreshTokenExpiresAt = DateTimeOffset.UtcNow.AddDays(30).ToUnixTimeSeconds(),
CodeChallenge = authData.CodeChallenge // For PKCE validation
});
}
2. Refresh Token Flow
[HttpPost("/oauth/refresh")]
public async Task<IActionResult> RefreshToken()
{
var apiKey = Request.Headers["ApiKey"].ToString();
var refreshToken = Request.Headers["RefreshToken"].ToString();
// 1. Validate service
var service = await _repository.GetServiceByApiKeyAsync(apiKey);
if (service == null) return Unauthorized();
// 2. Validate refresh token
var (isValid, tokenData) = await _repository.ValidateRefreshTokenAsync(
refreshToken,
service.Id
);
if (!isValid) return BadRequest("Invalid or expired refresh token");
// 3. Get user profile
var profile = await _repository.GetUserProfileAsync(
tokenData.UserId,
tokenData.ProfileId
);
// 4. Re-generate integration tokens (same logic as token exchange)
// ...
return Ok(new MultiServiceTokenResponse { /* ... */ });
}
3. User Consent Management
[HttpPost("/auth/consent")]
public async Task<IActionResult> SaveConsent(ConsentModel model)
{
await _repository.SaveUserConsentsAsync(
userId: model.UserId,
mainServiceId: model.ServiceId,
profileId: model.ProfileId,
grantedServiceIds: model.SelectedIntegrationIds
);
// Continue with authorization code generation...
return RedirectToAction("Authorize");
}
🗄️ Data Models
AuthorizationCodeData
public class AuthorizationCodeData
{
public string Code { get; set; }
public Guid ServiceId { get; set; }
public Guid UserId { get; set; }
public Guid ProfileId { get; set; }
public Guid[] GrantedIntegrationServiceIds { get; set; }
public string? CodeChallenge { get; set; } // For PKCE
public DateTime ExpiresAt { get; set; }
public bool IsUsed { get; set; }
}
RefreshTokenData
public class RefreshTokenData
{
public string Token { get; set; }
public Guid ServiceId { get; set; }
public Guid UserId { get; set; }
public Guid ProfileId { get; set; }
public Guid[] GrantedIntegrationServiceIds { get; set; }
public DateTime ExpiresAt { get; set; }
public bool IsRevoked { get; set; }
}
ServiceData
public class ServiceData
{
public Guid Id { get; set; }
public string Name { get; set; }
public string PackageName { get; set; }
public string BaseUrl { get; set; }
public string CallbackPath { get; set; }
public string ApiKey { get; set; }
public string? PrivateKey { get; set; } // For integration services
public string? PublicKey { get; set; } // For integration services
public bool IsIntegrationService { get; set; }
public string? AllowedIPs { get; set; } // CIDR notation
}
🔒 Security Considerations
API Key Validation
✅ Always validate API key before token operations
✅ Use secure random generation for API keys
✅ Consider hashing API keys in database (TODO in Linbik.App)
Authorization Code Security
✅ Single-use codes (mark as used immediately)
✅ Short expiration (5-10 minutes recommended)
✅ Bind to specific service ID
✅ Optional PKCE support for public clients
Refresh Token Security
✅ Long expiration (30 days default)
✅ Store securely with revocation capability
✅ Invalidate on logout or suspicious activity
✅ Rotate on each refresh (optional)
IP Whitelisting
// Check if request IP is allowed
if (!string.IsNullOrEmpty(service.AllowedIPs))
{
var clientIp = HttpContext.Connection.RemoteIpAddress?.ToString();
if (!IsIpAllowed(clientIp, service.AllowedIPs))
return Forbid("IP not allowed");
}
🔄 Migration from v1.x
// ❌ Old way (v1.x)
var isValid = await _repository.ValidateUserCredentialsAsync(email, password);
// ✅ New way (v2.0+)
// Use authorization code flow with cookies
// Client redirects to /auth/{serviceId}
// Server generates authorization code
// Client exchanges code for tokens
📖 Documentation
📄 License
This library is currently a work in progress and is not ready for production use.
Contact: info@linbik.com
Version: 2.0.0 (Authorization Server)
Last Updated: 1 Kasım 2025
| 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.1.0)
- Microsoft.AspNetCore.Authentication.JwtBearer (>= 9.0.8)
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.1.0 | 370 | 12/5/2025 |
| 1.0.1 | 209 | 12/14/2025 |
| 0.46.0 | 343 | 11/30/2025 |
| 0.45.12 | 342 | 11/30/2025 |
| 0.45.7 | 219 | 8/31/2025 |
| 0.45.6 | 200 | 8/31/2025 |
| 0.45.5 | 223 | 8/27/2025 |
| 0.44.0 | 234 | 8/24/2025 |
| 0.42.0 | 104 | 8/23/2025 |
| 0.41.0 | 114 | 8/15/2025 |
| 0.40.0 | 181 | 8/14/2025 |
| 0.39.0 | 181 | 8/14/2025 |
| 0.38.0 | 180 | 8/14/2025 |
| 0.37.0 | 352 | 7/25/2025 |
| 0.36.0 | 575 | 7/22/2025 |
| 0.1.0 | 519 | 7/21/2025 |
| 0.1.0-ci0025 | 566 | 7/22/2025 |
| 0.0.1 | 183 | 6/16/2025 |