Indiko.Blocks.Security.Authentication.Abstractions
2.1.1
dotnet add package Indiko.Blocks.Security.Authentication.Abstractions --version 2.1.1
NuGet\Install-Package Indiko.Blocks.Security.Authentication.Abstractions -Version 2.1.1
<PackageReference Include="Indiko.Blocks.Security.Authentication.Abstractions" Version="2.1.1" />
<PackageVersion Include="Indiko.Blocks.Security.Authentication.Abstractions" Version="2.1.1" />
<PackageReference Include="Indiko.Blocks.Security.Authentication.Abstractions" />
paket add Indiko.Blocks.Security.Authentication.Abstractions --version 2.1.1
#r "nuget: Indiko.Blocks.Security.Authentication.Abstractions, 2.1.1"
#:package Indiko.Blocks.Security.Authentication.Abstractions@2.1.1
#addin nuget:?package=Indiko.Blocks.Security.Authentication.Abstractions&version=2.1.1
#tool nuget:?package=Indiko.Blocks.Security.Authentication.Abstractions&version=2.1.1
Indiko.Blocks.Security.Authentication.Abstractions
Core authentication abstractions for implementing secure authentication and token management in the Indiko framework.
Overview
This package provides fundamental contracts and models for implementing authentication systems, including JWT tokens, identity management, and token providers.
Features
- ITokenProvider: Interface for token generation and management
- TokenResponse: Standardized token response model
- IdentityUser: User identity representation
- IdentityClient: Client/service identity representation
- AuthenticationOptions: Configuration model for authentication
- ClaimsIdentity Extensions: Helper methods for claims manipulation
- Token Validation: Exception handling for token validation
Installation
dotnet add package Indiko.Blocks.Security.Authentication.Abstractions
Key Interfaces
ITokenProvider
Interface for generating authentication tokens.
public interface ITokenProvider
{
Task<TokenResponse> GetToken(IdentityUser user, CancellationToken cancellationToken = default);
Task<TokenResponse> GetToken(IdentityUser user, Dictionary<string, string> customClaims,
CancellationToken cancellationToken = default);
Task<TokenResponse> GetToken(IdentityClient client, CancellationToken cancellationToken = default);
}
Models
TokenResponse
Represents authentication token response.
public sealed class TokenResponse
{
public Dictionary<string, string> Properties { get; init; }
public required string AccessToken { get; init; }
public required string IdentityToken { get; init; }
public required string RefreshToken { get; init; }
}
IdentityUser
Represents an authenticated user.
public class IdentityUser
{
public string UserId { get; set; }
public string Username { get; set; }
public string Email { get; set; }
public string[] Roles { get; set; }
public Dictionary<string, string> Claims { get; set; }
}
IdentityClient
Represents a client application or service.
public class IdentityClient
{
public string ClientId { get; set; }
public string ClientSecret { get; set; }
public string[] Scopes { get; set; }
}
Usage Examples
Implementing Token Provider
public class JwtTokenProvider : ITokenProvider
{
private readonly AuthenticationOptions _options;
private readonly ILogger<JwtTokenProvider> _logger;
public JwtTokenProvider(
IOptions<AuthenticationOptions> options,
ILogger<JwtTokenProvider> logger)
{
_options = options.Value;
_logger = logger;
}
public async Task<TokenResponse> GetToken(
IdentityUser user,
CancellationToken cancellationToken = default)
{
var claims = new List<Claim>
{
new(ClaimTypes.NameIdentifier, user.UserId),
new(ClaimTypes.Name, user.Username),
new(ClaimTypes.Email, user.Email)
};
foreach (var role in user.Roles)
{
claims.Add(new Claim(ClaimTypes.Role, role));
}
var token = GenerateJwtToken(claims);
var refreshToken = GenerateRefreshToken();
return new TokenResponse
{
AccessToken = token,
IdentityToken = token,
RefreshToken = refreshToken,
Properties = new Dictionary<string, string>
{
{ "expires_in", "3600" },
{ "token_type", "Bearer" }
}
};
}
public async Task<TokenResponse> GetToken(
IdentityUser user,
Dictionary<string, string> customClaims,
CancellationToken cancellationToken = default)
{
var tokenResponse = await GetToken(user, cancellationToken);
// Add custom claims to properties
foreach (var claim in customClaims)
{
tokenResponse.Properties[claim.Key] = claim.Value;
}
return tokenResponse;
}
public async Task<TokenResponse> GetToken(
IdentityClient client,
CancellationToken cancellationToken = default)
{
// Client credentials flow
var claims = new List<Claim>
{
new("client_id", client.ClientId),
new("scope", string.Join(" ", client.Scopes))
};
var token = GenerateJwtToken(claims);
return new TokenResponse
{
AccessToken = token,
IdentityToken = token,
RefreshToken = string.Empty,
Properties = new Dictionary<string, string>
{
{ "expires_in", "3600" },
{ "token_type", "Bearer" }
}
};
}
}
Authentication Service
public class AuthenticationService
{
private readonly ITokenProvider _tokenProvider;
private readonly IUserRepository _userRepository;
public AuthenticationService(
ITokenProvider tokenProvider,
IUserRepository userRepository)
{
_tokenProvider = tokenProvider;
_userRepository = userRepository;
}
public async Task<TokenResponse> AuthenticateAsync(
string username,
string password)
{
// Validate credentials
var user = await _userRepository.ValidateCredentialsAsync(username, password);
if (user == null)
throw new UnauthorizedAccessException("Invalid credentials");
// Create identity user
var identityUser = new IdentityUser
{
UserId = user.Id.ToString(),
Username = user.Username,
Email = user.Email,
Roles = user.Roles.ToArray()
};
// Generate tokens
return await _tokenProvider.GetToken(identityUser);
}
public async Task<TokenResponse> AuthenticateWithCustomClaimsAsync(
string username,
string password,
Dictionary<string, string> customClaims)
{
var user = await _userRepository.ValidateCredentialsAsync(username, password);
if (user == null)
throw new UnauthorizedAccessException("Invalid credentials");
var identityUser = new IdentityUser
{
UserId = user.Id.ToString(),
Username = user.Username,
Email = user.Email,
Roles = user.Roles.ToArray()
};
return await _tokenProvider.GetToken(identityUser, customClaims);
}
}
ClaimsIdentity Extensions
Helper methods for working with claims.
using Indiko.Blocks.Security.Authentication.Abstractions.Extensions;
public class UserService
{
private readonly IHttpContextAccessor _httpContextAccessor;
public string GetCurrentUserId()
{
var identity = _httpContextAccessor.HttpContext?.User?.Identity as ClaimsIdentity;
return identity?.GetUserId();
}
public string GetCurrentUserEmail()
{
var identity = _httpContextAccessor.HttpContext?.User?.Identity as ClaimsIdentity;
return identity?.GetUserEmail();
}
public string[] GetCurrentUserRoles()
{
var identity = _httpContextAccessor.HttpContext?.User?.Identity as ClaimsIdentity;
return identity?.GetRoles();
}
public bool HasRole(string role)
{
var identity = _httpContextAccessor.HttpContext?.User?.Identity as ClaimsIdentity;
return identity?.HasRole(role) ?? false;
}
}
Authentication Options
Configuration model for authentication settings.
public class AuthenticationOptions
{
public string Authority { get; set; }
public string Audience { get; set; }
public string ClientId { get; set; }
public string ClientSecret { get; set; }
public string[] Scopes { get; set; }
public string SigningKey { get; set; }
public string Issuer { get; set; }
public int TokenExpirationMinutes { get; set; } = 60;
public int RefreshTokenExpirationDays { get; set; } = 7;
}
Configuration Example
{
"AuthenticationOptions": {
"Authority": "https://identity.example.com",
"Audience": "api.example.com",
"ClientId": "my-api",
"ClientSecret": "secret",
"Scopes": ["openid", "profile", "email", "api"],
"SigningKey": "your-256-bit-secret-key-here",
"Issuer": "https://api.example.com",
"TokenExpirationMinutes": 60,
"RefreshTokenExpirationDays": 7
}
}
Exception Handling
TokenValidationException
Custom exception for token validation errors.
public class TokenValidationException : Exception
{
public TokenValidationException(string message) : base(message)
{
}
public TokenValidationException(string message, Exception innerException)
: base(message, innerException)
{
}
}
Usage
public class TokenValidator
{
public void ValidateToken(string token)
{
try
{
// Validate token logic
if (string.IsNullOrEmpty(token))
throw new TokenValidationException("Token is null or empty");
// Additional validation
}
catch (Exception ex)
{
throw new TokenValidationException("Token validation failed", ex);
}
}
}
Authentication Flows
User Authentication Flow
public class LoginController : ControllerBase
{
private readonly ITokenProvider _tokenProvider;
private readonly IUserRepository _userRepository;
[HttpPost("login")]
public async Task<IActionResult> Login([FromBody] LoginRequest request)
{
// 1. Validate credentials
var user = await _userRepository.ValidateCredentialsAsync(
request.Username,
request.Password);
if (user == null)
return Unauthorized(new { error = "Invalid credentials" });
// 2. Create identity user
var identityUser = new IdentityUser
{
UserId = user.Id.ToString(),
Username = user.Username,
Email = user.Email,
Roles = user.Roles.ToArray(),
Claims = new Dictionary<string, string>
{
{ "tenant_id", user.TenantId.ToString() },
{ "subscription_tier", user.SubscriptionTier }
}
};
// 3. Generate tokens
var tokenResponse = await _tokenProvider.GetToken(identityUser);
// 4. Return tokens
return Ok(new
{
access_token = tokenResponse.AccessToken,
refresh_token = tokenResponse.RefreshToken,
expires_in = tokenResponse.Properties["expires_in"],
token_type = tokenResponse.Properties["token_type"]
});
}
}
Client Credentials Flow
public class ClientAuthenticationService
{
private readonly ITokenProvider _tokenProvider;
public async Task<TokenResponse> AuthenticateClientAsync(
string clientId,
string clientSecret)
{
// Validate client credentials
if (!IsValidClient(clientId, clientSecret))
throw new UnauthorizedAccessException("Invalid client credentials");
var client = new IdentityClient
{
ClientId = clientId,
ClientSecret = clientSecret,
Scopes = new[] { "api.read", "api.write" }
};
return await _tokenProvider.GetToken(client);
}
}
Token Refresh
public class TokenRefreshService
{
private readonly ITokenProvider _tokenProvider;
private readonly IRefreshTokenRepository _refreshTokenRepository;
public async Task<TokenResponse> RefreshTokenAsync(string refreshToken)
{
// Validate refresh token
var storedToken = await _refreshTokenRepository.GetByTokenAsync(refreshToken);
if (storedToken == null || storedToken.IsExpired)
throw new TokenValidationException("Invalid or expired refresh token");
// Get user
var user = await GetUserByIdAsync(storedToken.UserId);
var identityUser = new IdentityUser
{
UserId = user.Id.ToString(),
Username = user.Username,
Email = user.Email,
Roles = user.Roles.ToArray()
};
// Generate new tokens
var newTokens = await _tokenProvider.GetToken(identityUser);
// Revoke old refresh token
await _refreshTokenRepository.RevokeAsync(refreshToken);
return newTokens;
}
}
Best Practices
- Secure Storage: Never store tokens in plain text
- Token Expiration: Use appropriate expiration times (short for access, longer for refresh)
- Refresh Tokens: Implement token refresh for better UX
- Claims: Include only necessary claims in tokens
- Validation: Always validate tokens on protected endpoints
- HTTPS: Always use HTTPS in production
- Secret Management: Use secure secret management (Azure Key Vault, AWS Secrets Manager)
Target Framework
- .NET 10
Dependencies
Indiko.Blocks.Common.Abstractions
License
See LICENSE file in the repository root.
Related Packages
Indiko.Blocks.Security.Authentication.ASPNetCore- ASP.NET Core authentication implementationIndiko.Blocks.Security.AuthenticationProvider.Abstractions- Authentication provider abstractionsIndiko.Blocks.Security.AuthenticationProvider.Blazor- Blazor authentication providerIndiko.Hosting.Web- Web API hosting
| Product | Versions 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. |
-
net10.0
- Indiko.Blocks.Common.Abstractions (>= 2.1.1)
NuGet packages (2)
Showing the top 2 NuGet packages that depend on Indiko.Blocks.Security.Authentication.Abstractions:
| Package | Downloads |
|---|---|
|
Indiko.Blocks.Security.Authentication.OpenIdDict
Building Blocks Security Authentication Open Id Dict |
|
|
Indiko.Blocks.Security.Authentication.ASPNetCore
Building Blocks Security Authentication ASPNet Core |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 2.1.1 | 649 | 12/2/2025 |
| 2.1.0 | 651 | 12/2/2025 |
| 2.0.0 | 296 | 9/17/2025 |
| 1.7.23 | 198 | 9/8/2025 |
| 1.7.22 | 195 | 9/8/2025 |
| 1.7.21 | 202 | 8/14/2025 |
| 1.7.20 | 222 | 6/23/2025 |
| 1.7.19 | 203 | 6/3/2025 |
| 1.7.18 | 205 | 5/29/2025 |
| 1.7.17 | 201 | 5/26/2025 |
| 1.7.15 | 154 | 4/12/2025 |
| 1.7.14 | 178 | 4/11/2025 |
| 1.7.13 | 165 | 3/29/2025 |
| 1.7.12 | 177 | 3/28/2025 |
| 1.7.11 | 186 | 3/28/2025 |
| 1.7.10 | 177 | 3/28/2025 |
| 1.7.9 | 206 | 3/28/2025 |
| 1.7.8 | 182 | 3/28/2025 |
| 1.7.5 | 205 | 3/17/2025 |
| 1.7.4 | 192 | 3/16/2025 |
| 1.7.3 | 221 | 3/16/2025 |
| 1.7.2 | 208 | 3/16/2025 |
| 1.7.1 | 239 | 3/11/2025 |
| 1.7.0 | 220 | 3/11/2025 |
| 1.6.8 | 240 | 3/11/2025 |
| 1.6.7 | 267 | 3/4/2025 |
| 1.6.6 | 168 | 2/26/2025 |
| 1.6.5 | 187 | 2/20/2025 |
| 1.6.4 | 167 | 2/20/2025 |
| 1.6.3 | 170 | 2/5/2025 |
| 1.6.2 | 140 | 1/24/2025 |
| 1.6.1 | 152 | 1/24/2025 |
| 1.6.0 | 150 | 1/16/2025 |
| 1.5.2 | 148 | 1/16/2025 |
| 1.5.1 | 200 | 11/3/2024 |
| 1.5.0 | 209 | 10/26/2024 |
| 1.3.2 | 178 | 10/24/2024 |
| 1.3.0 | 174 | 10/10/2024 |
| 1.2.5 | 186 | 10/9/2024 |
| 1.2.4 | 188 | 10/8/2024 |
| 1.2.1 | 178 | 10/3/2024 |
| 1.2.0 | 187 | 9/29/2024 |
| 1.1.1 | 202 | 9/23/2024 |
| 1.1.0 | 230 | 9/18/2024 |
| 1.0.33 | 236 | 9/15/2024 |
| 1.0.28 | 199 | 8/28/2024 |
| 1.0.27 | 218 | 8/24/2024 |
| 1.0.26 | 205 | 7/7/2024 |
| 1.0.25 | 203 | 7/6/2024 |
| 1.0.24 | 203 | 6/25/2024 |
| 1.0.23 | 191 | 6/1/2024 |
| 1.0.22 | 216 | 5/14/2024 |
| 1.0.21 | 213 | 5/14/2024 |
| 1.0.20 | 205 | 4/8/2024 |
| 1.0.19 | 213 | 4/3/2024 |
| 1.0.18 | 208 | 3/23/2024 |
| 1.0.17 | 214 | 3/19/2024 |
| 1.0.16 | 219 | 3/19/2024 |
| 1.0.15 | 223 | 3/11/2024 |
| 1.0.14 | 211 | 3/10/2024 |
| 1.0.13 | 230 | 3/6/2024 |
| 1.0.12 | 264 | 3/1/2024 |
| 1.0.11 | 206 | 3/1/2024 |
| 1.0.10 | 203 | 3/1/2024 |
| 1.0.9 | 207 | 3/1/2024 |
| 1.0.8 | 235 | 2/19/2024 |
| 1.0.7 | 214 | 2/17/2024 |
| 1.0.6 | 221 | 2/17/2024 |
| 1.0.5 | 197 | 2/17/2024 |
| 1.0.4 | 210 | 2/7/2024 |
| 1.0.3 | 235 | 2/6/2024 |
| 1.0.1 | 185 | 2/6/2024 |
| 1.0.0 | 245 | 1/9/2024 |
| 1.0.0-preview99 | 225 | 12/22/2023 |
| 1.0.0-preview98 | 202 | 12/21/2023 |
| 1.0.0-preview97 | 186 | 12/21/2023 |
| 1.0.0-preview96 | 173 | 12/20/2023 |
| 1.0.0-preview95 | 170 | 12/20/2023 |
| 1.0.0-preview94 | 189 | 12/18/2023 |
| 1.0.0-preview93 | 194 | 12/13/2023 |
| 1.0.0-preview92 | 206 | 12/13/2023 |
| 1.0.0-preview91 | 195 | 12/12/2023 |
| 1.0.0-preview90 | 178 | 12/11/2023 |
| 1.0.0-preview89 | 223 | 12/11/2023 |
| 1.0.0-preview88 | 226 | 12/6/2023 |
| 1.0.0-preview87 | 196 | 12/6/2023 |
| 1.0.0-preview86 | 192 | 12/6/2023 |
| 1.0.0-preview85 | 217 | 12/6/2023 |
| 1.0.0-preview84 | 202 | 12/5/2023 |
| 1.0.0-preview83 | 196 | 12/5/2023 |
| 1.0.0-preview82 | 183 | 12/5/2023 |
| 1.0.0-preview81 | 211 | 12/4/2023 |
| 1.0.0-preview80 | 187 | 12/1/2023 |
| 1.0.0-preview77 | 198 | 12/1/2023 |
| 1.0.0-preview76 | 190 | 12/1/2023 |
| 1.0.0-preview75 | 196 | 12/1/2023 |
| 1.0.0-preview74 | 187 | 11/26/2023 |
| 1.0.0-preview73 | 244 | 11/7/2023 |
| 1.0.0-preview72 | 222 | 11/6/2023 |
| 1.0.0-preview71 | 236 | 11/3/2023 |
| 1.0.0-preview70 | 189 | 11/2/2023 |
| 1.0.0-preview69 | 195 | 11/2/2023 |
| 1.0.0-preview68 | 189 | 11/2/2023 |
| 1.0.0-preview67 | 205 | 11/2/2023 |
| 1.0.0-preview66 | 169 | 11/2/2023 |
| 1.0.0-preview65 | 195 | 11/2/2023 |
| 1.0.0-preview64 | 230 | 11/2/2023 |
| 1.0.0-preview63 | 195 | 11/2/2023 |
| 1.0.0-preview62 | 198 | 11/1/2023 |
| 1.0.0-preview61 | 184 | 11/1/2023 |
| 1.0.0-preview60 | 203 | 11/1/2023 |
| 1.0.0-preview59 | 186 | 11/1/2023 |
| 1.0.0-preview58 | 225 | 10/31/2023 |
| 1.0.0-preview57 | 203 | 10/31/2023 |
| 1.0.0-preview56 | 190 | 10/31/2023 |
| 1.0.0-preview55 | 222 | 10/31/2023 |
| 1.0.0-preview54 | 185 | 10/31/2023 |
| 1.0.0-preview53 | 199 | 10/31/2023 |
| 1.0.0-preview52 | 210 | 10/31/2023 |
| 1.0.0-preview51 | 222 | 10/31/2023 |
| 1.0.0-preview50 | 196 | 10/31/2023 |
| 1.0.0-preview48 | 203 | 10/31/2023 |
| 1.0.0-preview46 | 201 | 10/31/2023 |
| 1.0.0-preview45 | 193 | 10/31/2023 |
| 1.0.0-preview44 | 218 | 10/31/2023 |
| 1.0.0-preview43 | 195 | 10/31/2023 |
| 1.0.0-preview42 | 223 | 10/30/2023 |
| 1.0.0-preview41 | 216 | 10/30/2023 |
| 1.0.0-preview40 | 188 | 10/27/2023 |
| 1.0.0-preview39 | 213 | 10/27/2023 |
| 1.0.0-preview38 | 183 | 10/27/2023 |
| 1.0.0-preview37 | 196 | 10/27/2023 |
| 1.0.0-preview36 | 178 | 10/27/2023 |
| 1.0.0-preview35 | 205 | 10/27/2023 |
| 1.0.0-preview34 | 193 | 10/27/2023 |
| 1.0.0-preview33 | 236 | 10/26/2023 |
| 1.0.0-preview32 | 221 | 10/26/2023 |
| 1.0.0-preview101 | 177 | 1/5/2024 |