Indiko.Blocks.Security.Authentication.ASPNetCore 2.1.1

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

Indiko.Blocks.Security.Authentication.ASPNetCore

JWT Bearer authentication implementation for ASP.NET Core APIs with token generation and validation.

Overview

This package provides complete JWT (JSON Web Token) authentication for ASP.NET Core applications, including token generation, validation, and SignalR hub support.

Features

  • JWT Bearer Authentication: Standard JWT token authentication
  • Token Generation: Built-in token provider
  • Token Validation: Automatic token validation middleware
  • SignalR Support: Query string token support for SignalR hubs
  • Configurable Validation: Control issuer, audience, lifetime validation
  • Development Mode: Relaxed HTTPS requirements in development
  • Comprehensive Logging: Detailed authentication event logging

Installation

dotnet add package Indiko.Blocks.Security.Authentication.ASPNetCore

Configuration

appsettings.json

{
  "AspNetCoreAuthenticationOptions": {
    "Enabled": true,
    "Secret": "your-256-bit-secret-key-minimum-32-characters",
    "Issuer": "https://api.example.com",
    "Audience": "api.example.com",
    "ValidateIssuer": true,
    "ValidateAudience": true,
    "ValidateLifetime": true,
    "ValidateIssuerSigningKey": true,
    "TokenExpirationMinutes": 60,
    "RefreshTokenExpirationDays": 7,
    "SignalRHubPath": "/hubs"
  }
}

Quick Start

// Authentication is auto-configured via block system
// Just ensure appsettings.json has the configuration above

Token Generation

Using ITokenProvider

[ApiController]
[Route("api/[controller]")]
public class AuthController : ControllerBase
{
    private readonly ITokenProvider _tokenProvider;
    private readonly IUserService _userService;

    public AuthController(ITokenProvider tokenProvider, IUserService userService)
    {
        _tokenProvider = tokenProvider;
        _userService = userService;
    }

    [HttpPost("login")]
    [AllowAnonymous]
    public async Task<IActionResult> Login([FromBody] LoginRequest request)
    {
        // Validate credentials
        var user = await _userService.ValidateCredentialsAsync(
            request.Username, 
            request.Password);

        if (user == null)
            return Unauthorized(new { message = "Invalid credentials" });

        // Create identity user
        var identityUser = new IdentityUser
        {
            UserId = user.Id.ToString(),
            Username = user.Username,
            Email = user.Email,
            Roles = new[] { "User", "Admin" }
        };

        // Generate tokens
        var tokenResponse = await _tokenProvider.GetToken(identityUser);

        return Ok(new
        {
            access_token = tokenResponse.AccessToken,
            token_type = "Bearer",
            expires_in = 3600,
            refresh_token = tokenResponse.RefreshToken
        });
    }
}

With Custom Claims

[HttpPost("login-with-claims")]
public async Task<IActionResult> LoginWithClaims([FromBody] LoginRequest request)
{
    var user = await _userService.ValidateCredentialsAsync(request.Username, request.Password);
    if (user == null) return Unauthorized();

    var identityUser = new IdentityUser
    {
        UserId = user.Id.ToString(),
        Username = user.Username,
        Email = user.Email,
        Roles = user.Roles.ToArray()
    };

    var customClaims = new Dictionary<string, string>
    {
        { "tenant_id", user.TenantId.ToString() },
        { "subscription_tier", user.SubscriptionTier },
        { "permissions", string.Join(",", user.Permissions) }
    };

    var tokenResponse = await _tokenProvider.GetToken(identityUser, customClaims);
    return Ok(tokenResponse);
}

Protected Endpoints

Require Authentication

[ApiController]
[Route("api/[controller]")]
[Authorize]  // Requires valid JWT token
public class UsersController : ControllerBase
{
    [HttpGet]
    public IActionResult GetUsers()
    {
        return Ok(users);
    }

    [HttpGet("me")]
    public IActionResult GetCurrentUser()
    {
        var userId = User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
        var username = User.FindFirst(ClaimTypes.Name)?.Value;
        var email = User.FindFirst(ClaimTypes.Email)?.Value;

        return Ok(new { userId, username, email });
    }
}

Role-Based Authorization

[Authorize(Roles = "Admin")]
[HttpDelete("{id}")]
public IActionResult DeleteUser(Guid id)
{
    // Only admins can delete
    return NoContent();
}

[Authorize(Roles = "Admin,Moderator")]
[HttpPut("{id}/ban")]
public IActionResult BanUser(Guid id)
{
    // Admins or Moderators can ban
    return NoContent();
}

Policy-Based Authorization

// In Startup.cs
services.AddAuthorization(options =>
{
    options.AddPolicy("RequireAdminRole", policy =>
        policy.RequireRole("Admin"));

    options.AddPolicy("RequirePremium", policy =>
        policy.RequireClaim("subscription_tier", "Premium", "Enterprise"));

    options.AddPolicy("MinimumAge", policy =>
        policy.Requirements.Add(new MinimumAgeRequirement(18)));
});

// In Controller
[Authorize(Policy = "RequirePremium")]
[HttpGet("premium-content")]
public IActionResult GetPremiumContent()
{
    return Ok(premiumContent);
}

SignalR Hub Authentication

The block automatically supports SignalR hub authentication via query strings:

// Client-side JavaScript
const connection = new signalR.HubConnectionBuilder()
    .withUrl("/hubs/chat", {
        accessTokenFactory: () => localStorage.getItem("access_token")
    })
    .build();

await connection.start();

Secured Hub

[Authorize]
public class ChatHub : Hub
{
    public override async Task OnConnectedAsync()
    {
        var username = Context.User?.Identity?.Name;
        Console.WriteLine($"{username} connected");
        await base.OnConnectedAsync();
    }

    public async Task SendMessage(string message)
    {
        var username = Context.User?.Identity?.Name;
        await Clients.All.SendAsync("ReceiveMessage", username, message);
    }
}

Token Validation

Tokens are automatically validated on every request to protected endpoints:

  1. Signature: Verified using the secret key
  2. Expiration: Checked if ValidateLifetime is true
  3. Issuer: Validated if ValidateIssuer is true
  4. Audience: Validated if ValidateAudience is true

Manual Validation

public class TokenValidator
{
    private readonly AspNetCoreAuthenticationOptions _options;

    public bool ValidateToken(string token, out ClaimsPrincipal principal)
    {
        var tokenHandler = new JwtSecurityTokenHandler();
        principal = null;

        try
        {
            var validationParameters = new TokenValidationParameters
            {
                ValidateIssuer = _options.ValidateIssuer,
                ValidateAudience = _options.ValidateAudience,
                ValidateLifetime = _options.ValidateLifetime,
                ValidIssuer = _options.Issuer,
                ValidAudience = _options.Audience,
                IssuerSigningKey = new SymmetricSecurityKey(
                    Encoding.UTF8.GetBytes(_options.Secret))
            };

            principal = tokenHandler.ValidateToken(token, validationParameters, out _);
            return true;
        }
        catch
        {
            return false;
        }
    }
}

Client Usage

JavaScript/TypeScript

// Login
const response = await fetch('/api/auth/login', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ username: 'user', password: 'pass' })
});

const { access_token } = await response.json();
localStorage.setItem('access_token', access_token);

// Protected request
const data = await fetch('/api/users', {
    headers: {
        'Authorization': `Bearer ${localStorage.getItem('access_token')}`
    }
});

C# HttpClient

var client = new HttpClient();
client.DefaultRequestHeaders.Authorization = 
    new AuthenticationHeaderValue("Bearer", accessToken);

var response = await client.GetAsync("https://api.example.com/api/users");

Refresh Tokens

[HttpPost("refresh")]
public async Task<IActionResult> RefreshToken([FromBody] RefreshTokenRequest request)
{
    // Validate refresh token
    var storedToken = await _tokenRepository.GetByTokenAsync(request.RefreshToken);
    if (storedToken == null || storedToken.IsExpired)
        return Unauthorized(new { message = "Invalid refresh token" });

    // Get user
    var user = await _userService.GetByIdAsync(storedToken.UserId);

    var identityUser = new IdentityUser
    {
        UserId = user.Id.ToString(),
        Username = user.Username,
        Email = user.Email,
        Roles = user.Roles.ToArray()
    };

    // Generate new tokens
    var tokenResponse = await _tokenProvider.GetToken(identityUser);

    // Revoke old refresh token
    await _tokenRepository.RevokeAsync(request.RefreshToken);

    return Ok(tokenResponse);
}

Event Logging

The block logs detailed authentication events:

  • OnMessageReceived: Token received from request
  • OnTokenValidated: Token successfully validated
  • OnAuthenticationFailed: Authentication failure
  • OnChallenge: Authentication challenge issued
  • OnForbidden: Access denied

Best Practices

  1. Strong Secrets: Use 256-bit (32+ characters) secret keys
  2. HTTPS: Always use HTTPS in production
  3. Short Expiration: Keep access token expiration short (15-60 minutes)
  4. Refresh Tokens: Implement refresh token flow
  5. Secure Storage: Don't log or expose secret keys
  6. Environment-Specific: Different secrets per environment

Target Framework

  • .NET 10

Dependencies

  • Indiko.Blocks.Security.Authentication.Abstractions
  • Microsoft.AspNetCore.Authentication.JwtBearer
  • System.IdentityModel.Tokens.Jwt

License

See LICENSE file in the repository root.

  • Indiko.Blocks.Security.Authentication.Abstractions - Authentication abstractions
  • Indiko.Blocks.Security.AuthenticationProvider.Blazor - Blazor authentication
  • Indiko.Hosting.Web - Web API hosting
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
2.1.2 273 12/18/2025
2.1.1 672 12/2/2025
2.1.0 665 12/2/2025
2.0.0 290 9/17/2025
1.7.23 188 9/8/2025
1.7.22 179 9/8/2025
1.7.21 192 8/14/2025
1.7.20 206 6/23/2025
1.7.19 178 6/3/2025
1.7.18 183 5/29/2025
1.7.17 201 5/26/2025
1.7.15 136 4/12/2025
1.7.14 162 4/11/2025
1.7.13 162 3/29/2025
1.7.12 173 3/28/2025
1.7.11 169 3/28/2025
1.7.10 174 3/28/2025
1.7.9 176 3/28/2025
1.7.8 159 3/28/2025
1.7.5 193 3/17/2025
1.7.4 166 3/16/2025
1.7.3 186 3/16/2025
1.7.2 192 3/16/2025
1.7.1 213 3/11/2025
1.6.8 219 3/11/2025
1.6.7 260 3/4/2025
1.6.6 147 2/26/2025
1.6.5 160 2/20/2025
1.6.4 136 2/20/2025
1.6.3 158 2/5/2025
1.6.2 152 1/24/2025
1.6.1 140 1/24/2025
1.6.0 144 1/16/2025
1.5.2 148 1/16/2025
1.5.1 181 11/3/2024
1.5.0 161 10/26/2024
1.3.2 158 10/24/2024
1.3.0 153 10/10/2024
1.2.5 166 10/9/2024
1.2.4 148 10/8/2024
1.2.1 149 10/3/2024
1.2.0 180 9/29/2024
1.1.1 156 9/23/2024
1.1.0 174 9/18/2024
1.0.33 166 9/15/2024
1.0.28 165 8/28/2024
1.0.27 188 8/24/2024
1.0.26 175 7/7/2024
1.0.25 177 7/6/2024
1.0.24 168 6/25/2024
1.0.23 194 6/1/2024
1.0.22 181 5/14/2024
1.0.21 155 5/14/2024
1.0.20 190 4/8/2024
1.0.19 172 4/3/2024
1.0.18 189 3/23/2024
1.0.17 173 3/19/2024
1.0.16 177 3/19/2024
1.0.15 207 3/11/2024
1.0.14 194 3/10/2024
1.0.13 193 3/6/2024
1.0.12 225 3/1/2024
1.0.11 177 3/1/2024
1.0.10 186 3/1/2024
1.0.9 170 3/1/2024
1.0.8 196 2/19/2024
1.0.7 187 2/17/2024
1.0.6 198 2/17/2024
1.0.5 193 2/17/2024
1.0.4 189 2/7/2024
1.0.3 182 2/6/2024
1.0.1 169 2/6/2024
1.0.0 226 1/9/2024
1.0.0-preview99 206 12/22/2023
1.0.0-preview98 169 12/21/2023
1.0.0-preview97 169 12/21/2023
1.0.0-preview96 171 12/20/2023
1.0.0-preview95 162 12/20/2023
1.0.0-preview94 164 12/18/2023
1.0.0-preview93 176 12/13/2023
1.0.0-preview92 167 12/13/2023
1.0.0-preview91 166 12/12/2023
1.0.0-preview90 176 12/11/2023
1.0.0-preview89 179 12/11/2023
1.0.0-preview88 196 12/6/2023
1.0.0-preview87 160 12/6/2023
1.0.0-preview86 177 12/6/2023
1.0.0-preview85 176 12/6/2023
1.0.0-preview84 183 12/5/2023
1.0.0-preview83 178 12/5/2023
1.0.0-preview82 170 12/5/2023
1.0.0-preview81 176 12/4/2023
1.0.0-preview80 161 12/1/2023
1.0.0-preview77 170 12/1/2023
1.0.0-preview76 170 12/1/2023
1.0.0-preview75 171 12/1/2023
1.0.0-preview74 181 11/26/2023
1.0.0-preview73 185 11/7/2023
1.0.0-preview72 175 11/6/2023
1.0.0-preview71 179 11/3/2023
1.0.0-preview70 171 11/2/2023
1.0.0-preview69 158 11/2/2023
1.0.0-preview68 168 11/2/2023
1.0.0-preview67 161 11/2/2023
1.0.0-preview66 157 11/2/2023
1.0.0-preview65 175 11/2/2023
1.0.0-preview64 176 11/2/2023
1.0.0-preview63 157 11/2/2023
1.0.0-preview62 179 11/1/2023
1.0.0-preview61 153 11/1/2023
1.0.0-preview60 177 11/1/2023
1.0.0-preview59 168 11/1/2023
1.0.0-preview58 171 10/31/2023
1.0.0-preview57 169 10/31/2023
1.0.0-preview56 165 10/31/2023
1.0.0-preview55 174 10/31/2023
1.0.0-preview54 157 10/31/2023
1.0.0-preview53 143 10/31/2023
1.0.0-preview52 170 10/31/2023
1.0.0-preview51 161 10/31/2023
1.0.0-preview50 173 10/31/2023
1.0.0-preview48 174 10/31/2023
1.0.0-preview46 181 10/31/2023
1.0.0-preview45 159 10/31/2023
1.0.0-preview44 164 10/31/2023
1.0.0-preview43 157 10/31/2023
1.0.0-preview42 165 10/30/2023
1.0.0-preview41 166 10/30/2023
1.0.0-preview40 176 10/27/2023
1.0.0-preview39 179 10/27/2023
1.0.0-preview38 167 10/27/2023
1.0.0-preview37 182 10/27/2023
1.0.0-preview36 165 10/27/2023
1.0.0-preview35 151 10/27/2023
1.0.0-preview34 152 10/27/2023
1.0.0-preview33 170 10/26/2023
1.0.0-preview32 176 10/26/2023
1.0.0-preview101 158 1/5/2024