# JWT Authentication Implementation in OpenDeepWiki: A Complete Technical Guide

> Learn how JWT authentication works in OpenDeepWiki. This guide details ASP.NET Core middleware, custom JwtService and AuthService for secure token handling and claims-based authorization.

- Repository: [AIDotNet/OpenDeepWiki](https://github.com/aidotnet/opendeepwiki)
- Tags: how-to-guide
- Published: 2026-02-16

---

**OpenDeepWiki implements JWT authentication using ASP.NET Core's JwtBearer middleware combined with custom `JwtService` and `AuthService` classes that handle token generation, validation, and role-based authorization through claims-based security.**

OpenDeepWiki is an open-source wiki platform built on .NET that leverages JWT (JSON Web Token) authentication to secure API endpoints and manage user sessions. This article examines the complete JWT authentication implementation in the [AIDotNet/OpenDeepWiki](https://github.com/AIDotNet/OpenDeepWiki) repository, covering configuration, token generation, validation, and authorization policies.

## JWT Authentication Architecture Overview

The authentication system in OpenDeepWiki follows a layered architecture that separates configuration, service logic, and middleware integration. At its core, the system relies on four primary components working in concert:

- **`JwtOptions`** – Configuration class holding signing keys, issuer, audience, and expiration settings
- **`JwtService`** – Core service responsible for token generation and manual validation
- **`AuthService`** – Business logic layer handling login/registration workflows
- **ASP.NET Core JwtBearer Middleware** – Pipeline component that automatically validates incoming request tokens

This architecture ensures that token generation is isolated from business logic, while the middleware handles the repetitive task of request validation.

## Core Configuration and Services

### JwtOptions Configuration

The `JwtOptions` class defines the configuration schema for JWT settings. In [`src/OpenDeepWiki/Services/Auth/JwtOptions.cs`](https://github.com/AIDotNet/OpenDeepWiki/blob/main/src/OpenDeepWiki/Services/Auth/JwtOptions.cs), the system expects configuration values from [`appsettings.json`](https://github.com/AIDotNet/OpenDeepWiki/blob/main/appsettings.json) or environment variables.

Key configuration parameters include:

- **SecretKey** – The symmetric key used for HMAC-SHA256 signing
- **Issuer** – The token issuer identifier
- **Audience** – The intended recipient of the token
- **ExpirationMinutes** – Token lifetime duration

In [`src/OpenDeepWiki/Program.cs`](https://github.com/AIDotNet/OpenDeepWiki/blob/main/src/OpenDeepWiki/Program.cs) (lines 73-81), the application binds these options and performs validation to ensure the `SecretKey` is configured:

```csharp
// Program.cs - Configuration binding
builder.Services.Configure<JwtOptions>(options =>
{
    builder.Configuration.GetSection("Jwt").Bind(options);
    if (string.IsNullOrEmpty(options.SecretKey))
    {
        throw new InvalidOperationException("JWT SecretKey is not configured.");
    }
});

```

### Service Registration in Program.cs

The dependency injection container is configured in [`src/OpenDeepWiki/Program.cs`](https://github.com/AIDotNet/OpenDeepWiki/blob/main/src/OpenDeepWiki/Program.cs) (lines 94-119) to register the authentication services and middleware. This registration includes:

1. **JWT Bearer Authentication** – Configuring `TokenValidationParameters` with the symmetric security key
2. **Authorization Services** – Adding role-based policy support
3. **Custom Service Registration** – Binding `IJwtService` to `JwtService` and `IAuthService` to `AuthService`

```csharp
// Program.cs - JWT Bearer configuration (lines 94-108)
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options =>
    {
        var jwtOptions = builder.Configuration.GetSection("Jwt").Get<JwtOptions>();
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuer = true,
            ValidateAudience = true,
            ValidateLifetime = true,
            ValidateIssuerSigningKey = true,
            ValidIssuer = jwtOptions.Issuer,
            ValidAudience = jwtOptions.Audience,
            IssuerSigningKey = new SymmetricSecurityKey(
                Encoding.UTF8.GetBytes(jwtOptions.SecretKey))
        };
    });

```

Additionally, lines 110-113 define the `AdminOnly` authorization policy for role-based access control:

```csharp
// Program.cs - Authorization policies (lines 110-113)
builder.Services.AddAuthorization(options =>
{
    options.AddPolicy("AdminOnly", policy => 
        policy.RequireClaim(ClaimTypes.Role, "Admin"));
});

```

## Token Generation and Validation

### Generating Tokens with JwtService

The `JwtService` class in [`src/OpenDeepWiki/Services/Auth/JwtService.cs`](https://github.com/AIDotNet/OpenDeepWiki/blob/main/src/OpenDeepWiki/Services/Auth/JwtService.cs) handles the cryptographic operations for token creation. The `GenerateToken` method (lines 22-47) constructs a `JwtSecurityToken` containing standard claims and role information.

The implementation uses **symmetric key signing** with HMAC-SHA256:

```csharp
// JwtService.cs - Token generation (lines 22-47)
public string GenerateToken(User user, IEnumerable<string> roles)
{
    var claims = new List<Claim>
    {
        new Claim(ClaimTypes.NameIdentifier, user.Id),
        new Claim(ClaimTypes.Name, user.UserName),
        new Claim(ClaimTypes.Email, user.Email),
        new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())
    };

    // Add role claims for authorization
    foreach (var role in roles)
    {
        claims.Add(new Claim(ClaimTypes.Role, role));
    }

    var key = new SymmetricSecurityKey(
        Encoding.UTF8.GetBytes(_jwtOptions.SecretKey));
    var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);

    var token = new JwtSecurityToken(
        issuer: _jwtOptions.Issuer,
        audience: _jwtOptions.Audience,
        claims: claims,
        expires: DateTime.Now.AddMinutes(_jwtOptions.ExpirationMinutes),
        signingCredentials: creds);

    return new JwtSecurityTokenHandler().WriteToken(token);
}

```

Key security features implemented:
- **Unique Token ID (JTI)** – Prevents token replay attacks using `Guid.NewGuid()`
- **Role-based Claims** – Stores user roles as `ClaimTypes.Role` for policy evaluation
- **Expiration** – Configurable lifetime via `ExpirationMinutes`

### Token Validation

The `JwtService` also provides manual token validation through the `ValidateToken` method (lines 49-73), which is used for internal authorization checks beyond the middleware's automatic validation:

```csharp
// JwtService.cs - Token validation (lines 49-73)
public bool ValidateToken(string token, out string userId)
{
    userId = null;
    var tokenHandler = new JwtSecurityTokenHandler();
    
    try
    {
        var validationParameters = new TokenValidationParameters
        {
            ValidateIssuerSigningKey = true,
            IssuerSigningKey = new SymmetricSecurityKey(
                Encoding.UTF8.GetBytes(_jwtOptions.SecretKey)),
            ValidateIssuer = true,
            ValidIssuer = _jwtOptions.Issuer,
            ValidateAudience = true,
            ValidAudience = _jwtOptions.Audience,
            ValidateLifetime = true,
            ClockSkew = TimeSpan.Zero
        };

        var principal = tokenHandler.ValidateToken(token, validationParameters, out _);
        userId = principal.FindFirst(ClaimTypes.NameIdentifier)?.Value;
        return !string.IsNullOrEmpty(userId);
    }
    catch
    {
        return false;
    }
}

```

This manual validation method extracts the `userId` from the `NameIdentifier` claim after cryptographic verification, providing a fallback for scenarios where the middleware's `HttpContext.User` is not available.

## Authentication Flow and Authorization

### Login and Registration Process

The `AuthService` class in [`src/OpenDeepWiki/Services/Auth/AuthService.cs`](https://github.com/AIDotNet/OpenDeepWiki/blob/main/src/OpenDeepWiki/Services/Auth/AuthService.cs) orchestrates the authentication workflow. During login (lines 14-18) and registration (lines 53-55), the service validates credentials and delegates token generation to `JwtService`:

```csharp
// AuthService.cs - Login flow (lines 14-18)
public async Task<LoginResponse> LoginAsync(LoginRequest request)
{
    // ... credential validation logic ...
    var user = await _userManager.FindByEmailAsync(request.Email);
    // ... password verification ...
    
    var roles = await _userManager.GetRolesAsync(user);
    var token = _jwtService.GenerateToken(user, roles);
    
    return new LoginResponse
    {
        AccessToken = token,
        ExpiresIn = _jwtOptions.ExpirationMinutes * 60,
        User = _mapper.Map<UserDto>(user)
    };
}

```

The `LoginResponse` model (defined in [`src/OpenDeepWiki/Models/Auth/LoginResponse.cs`](https://github.com/AIDotNet/OpenDeepWiki/blob/main/src/OpenDeepWiki/Models/Auth/LoginResponse.cs)) packages the `access_token`, expiration time in seconds, and serialized user information for the client.

### Role-Based Authorization Policies

OpenDeepWiki implements **claims-based authorization** using ASP.NET Core's policy system. As configured in [`Program.cs`](https://github.com/AIDotNet/OpenDeepWiki/blob/main/Program.cs) (lines 110-113), the `AdminOnly` policy restricts access to users possessing the "Admin" role claim:

```csharp
// Program.cs - Authorization policy configuration (lines 110-113)
builder.Services.AddAuthorization(options =>
{
    options.AddPolicy("AdminOnly", policy => 
        policy.RequireClaim(ClaimTypes.Role, "Admin"));
});

```

Controllers apply these policies using the `[Authorize]` attribute:

- `[Authorize]` – Requires valid JWT (any authenticated user)
- `[Authorize(Policy = "AdminOnly")]` – Requires Admin role claim

The middleware automatically populates `HttpContext.User` with claims extracted from the validated JWT, enabling role checks via `User.IsInRole("Admin")` or policy-based authorization.

## Practical Implementation Examples

### Client-Side Token Usage

When consuming the OpenDeepWiki API, clients first authenticate to receive a JWT, then include it in subsequent requests:

```csharp
// Authenticate and receive token
var loginDto = new {
    Email = "alice@example.com",
    Password = "SecretPwd123"
};

var response = await httpClient.PostAsJsonAsync("/api/auth/login", loginDto);
var result   = await response.Content.ReadFromJsonAsync<LoginResponse>();

string accessToken = result.AccessToken;
int expiresIn      = result.ExpiresIn;

// Attach token to subsequent requests
httpClient.DefaultRequestHeaders.Authorization =
    new AuthenticationHeaderValue("Bearer", accessToken);

// Access protected resource
var profile = await httpClient.GetFromJsonAsync<UserInfo>("/api/user/me");

```

### Server-Side Resource Protection

Controllers protect endpoints using authorization attributes, accessing claims from the validated token:

```csharp
[ApiController]
[Route("api/user")]
public class UserController : ControllerBase
{
    // Any authenticated user
    [HttpGet("me")]
    [Authorize]
    public async Task<ActionResult<UserInfo>> GetCurrentUser()
    {
        var userId = User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
        var user   = await _authService.GetUserInfoAsync(userId);
        return Ok(user);
    }

    // Admin-only access
    [HttpDelete("{id}")]
    [Authorize(Policy = "AdminOnly")]
    public async Task<IActionResult> DeleteUser(string id)
    {
        await _authService.DeleteUserAsync(id);
        return NoContent();
    }
}

```

### Manual Token Validation

For scenarios requiring token validation outside the middleware pipeline (such as background services or WebSocket connections), use the `JwtService.ValidateToken` method:

```csharp
public async Task<string?> ExtractUserIdFromToken(string token)
{
    if (_jwtService.ValidateToken(token, out var userId))
    {
        return userId;
    }
    
    return null; // Token invalid or expired
}

```

## Summary

OpenDeepWiki implements a production-ready JWT authentication system through the following key mechanisms:

- **Symmetric Key Signing** – Uses HMAC-SHA256 with a configurable secret key stored in `JwtOptions`
- **Claims-Based Identity** – Embeds user ID, username, email, and roles as standard claims (`NameIdentifier`, `Name`, `Email`, `Role`)
- **Dual Validation** – Combines automatic middleware validation via `JwtBearer` with manual `JwtService.ValidateToken` for flexibility
- **Policy-Driven Authorization** – Implements role-based access control through ASP.NET Core authorization policies like `AdminOnly`
- **Configurable Expiration** – Supports adjustable token lifetimes via `ExpirationMinutes` setting

The implementation spans [`Program.cs`](https://github.com/AIDotNet/OpenDeepWiki/blob/main/Program.cs) for middleware configuration, [`JwtService.cs`](https://github.com/AIDotNet/OpenDeepWiki/blob/main/JwtService.cs) for cryptographic operations, and [`AuthService.cs`](https://github.com/AIDotNet/OpenDeepWiki/blob/main/AuthService.cs) for business logic, providing a clean separation of concerns suitable for enterprise applications.

## Frequently Asked Questions

### How does OpenDeepWiki store the JWT signing key?

The signing key is stored in the `JwtOptions` configuration class, which binds to the `Jwt` section in [`appsettings.json`](https://github.com/AIDotNet/OpenDeepWiki/blob/main/appsettings.json) or environment variables. In [`src/OpenDeepWiki/Program.cs`](https://github.com/AIDotNet/OpenDeepWiki/blob/main/src/OpenDeepWiki/Program.cs) (lines 73-81), the application validates that `SecretKey` is not empty during startup to prevent runtime cryptographic failures.

### What claims are included in the OpenDeepWiki JWT tokens?

OpenDeepWiki tokens include standard identity claims: `NameIdentifier` (user ID), `Name` (username), `Email`, and `Jti` (unique token ID for replay protection). Additionally, role claims using `ClaimTypes.Role` are embedded for each user role to support the `AdminOnly` authorization policy and other role-based access controls.

### Can tokens be validated outside of the HTTP request pipeline?

Yes. While the `JwtBearer` middleware automatically validates tokens for incoming HTTP requests, the `JwtService` class in [`src/OpenDeepWiki/Services/Auth/JwtService.cs`](https://github.com/AIDotNet/OpenDeepWiki/blob/main/src/OpenDeepWiki/Services/Auth/JwtService.cs) (lines 49-73) provides a `ValidateToken` method for manual validation. This is useful for WebSocket connections, background services, or scenarios where `HttpContext` is not available.

### How does OpenDeepWiki handle role-based authorization?

OpenDeepWiki uses ASP.NET Core's policy-based authorization. In [`src/OpenDeepWiki/Program.cs`](https://github.com/AIDotNet/OpenDeepWiki/blob/main/src/OpenDeepWiki/Program.cs) (lines 110-113), an `AdminOnly` policy is defined that requires the `ClaimTypes.Role` claim with value "Admin". Controllers apply this using `[Authorize(Policy = "AdminOnly")]` attributes, while standard `[Authorize]` attributes require only valid authentication without specific role requirements.