# OpenDeepWiki Security Best Practices for JWT Authentication in Production

> Discover OpenDeepWiki security best practices for JWT authentication in production. Secure your tokens with environment variables, HTTPS, claim validation, short expirations, and refresh token rotation.

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

---

**Store JWT secrets in environment variables or dedicated secret managers, enforce HTTPS, validate issuer and audience claims, set short token lifetimes (15–30 minutes), and implement refresh token rotation to secure OpenDeepWiki in production environments.**

OpenDeepWiki (AIDotNet/OpenDeepWiki) uses stateless JWT authentication implemented across `JwtOptions`, `JwtService`, and the ASP.NET Core authentication pipeline in **Program.cs**. To deploy this system securely, you must harden the default configuration beyond the development defaults. This guide covers production-ready JWT security measures derived directly from the OpenDeepWiki source code.

## Secure Secret Key Management in OpenDeepWiki

The JWT signing key is the most critical secret in your authentication system. In [`Program.cs`](https://github.com/AIDotNet/OpenDeepWiki/blob/main/Program.cs) (lines 78‑82), OpenDeepWiki reads the secret from the `Jwt:SecretKey` configuration section, falling back to the `JWT_SECRET_KEY` environment variable if the configuration value is missing.

**Production hardening steps:**

- **Never commit secrets to source control.** Ensure [`appsettings.Production.json`](https://github.com/AIDotNet/OpenDeepWiki/blob/main/appsettings.Production.json) excludes the secret or uses placeholder values only.
- **Use a dedicated secret manager.** Store the key in Azure Key Vault, AWS Secrets Manager, HashiCorp Vault, or Docker Secrets.
- **Generate a cryptographically strong key.** Use at least a 256‑bit (32‑byte) key encoded as Base64. Generate one via:

```bash
openssl rand -base64 32

```

- **Rotate keys regularly.** Implement a key rotation policy every 90 days or upon suspected compromise.

## Configure Token Validation Parameters

OpenDeepWiki validates tokens using `TokenValidationParameters` configured in **Program.cs** (lines 100‑106). The default implementation uses symmetric HMAC‑SHA256 signing and sets `ClockSkew = TimeSpan.Zero` to eliminate time‑based replay windows.

**Critical production settings:**

```csharp
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(opts =>
    {
        opts.RequireHttpsMetadata = true;  // Enforce HTTPS only
        opts.SaveToken = false;            // Do not store token in auth properties
        
        opts.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuerSigningKey = true,
            IssuerSigningKey = new SymmetricSecurityKey(
                Encoding.UTF8.GetBytes(jwtOptions.SecretKey)),
            ValidateIssuer = true,
            ValidIssuer = jwtOptions.Issuer,      // e.g., "https://api.opendeepwiki.com"
            ValidateAudience = true,
            ValidAudience = jwtOptions.Audience, // e.g., "OpenDeepWikiClient"
            ValidateLifetime = true,
            ClockSkew = TimeSpan.Zero            // Strict expiration enforcement
        };
    });

```

**Key hardening measures:**

- **Validate Issuer and Audience strictly.** Set `ValidIssuer` and `ValidAudience` to your production domain values, never use generic placeholders like `"MyApp"`.
- **Enforce HTTPS.** Set `RequireHttpsMetadata = true` to prevent token interception over insecure channels.
- **Synchronize server clocks.** With `ClockSkew = TimeSpan.Zero`, ensure all servers use NTP to prevent clock drift from causing validation failures.

## Harden Token Generation and Claims

The [`JwtService.cs`](https://github.com/AIDotNet/OpenDeepWiki/blob/main/JwtService.cs) (lines 26‑42) generates tokens using `JwtSecurityTokenHandler`. It embeds standard claims including `NameIdentifier`, `Name`, `Email`, `Role`, and a unique `Jti` (JWT ID) claim.

**Production best practices for token generation:**

```csharp
public string GenerateToken(User user, List<string> roles)
{
    var claims = new List<Claim>
    {
        new(ClaimTypes.NameIdentifier, user.Id),
        new(ClaimTypes.Name, user.Name),
        new(ClaimTypes.Email, user.Email),
        new(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()) // Unique token ID
    };
    
    claims.AddRange(roles.Select(r => new Claim(ClaimTypes.Role, r)));

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

    var token = new JwtSecurityToken(
        issuer: _options.Issuer,
        audience: _options.Audience,
        claims: claims,
        expires: DateTime.UtcNow.AddMinutes(_options.ExpirationMinutes), // Short lifetime
        signingCredentials: creds);

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

```

**Security considerations:**

- **Keep token lifetimes short.** Change the default 1440 minutes (24 hours) in `JwtOptions.ExpirationMinutes` to 15–30 minutes for high‑risk operations.
- **Include a unique `Jti` claim.** This enables token revocation tracking if you implement a blacklist.
- **Avoid sensitive data in claims.** Never include passwords, API keys, or unnecessary PII in the JWT payload.
- **Use asymmetric signing for scale.** Consider migrating from `HmacSha256` to `RS256` or `ES256` if you need to distribute validation across multiple services without sharing the private key.

## Production Deployment Checklist

Before deploying OpenDeepWiki to production, verify these security controls are active:

- **HTTPS Everywhere.** Configure reverse proxies (Nginx, Traefik, or Azure Front Door) to enforce TLS 1.2+ and redirect HTTP to HTTPS.
- **Refresh Token Implementation.** Store refresh tokens in secure, `HttpOnly`, `SameSite=Strict` cookies with a longer expiration (7–30 days). Rotate refresh tokens on every use.
- **Token Revocation.** Implement a Redis or distributed cache to store revoked `Jti` claims. Add middleware to check the cache before validating the token signature.
- **Key Rotation.** Schedule automated rotation of the `SecretKey` every 90 days. Use a `kid` (Key ID) header claim to support multiple active keys during transition periods.
- **Security Headers.** Add `Strict-Transport-Security`, `X-Content-Type-Options`, and `X-Frame-Options` headers to prevent injection attacks.
- **Monitoring.** Log authentication failures to SIEM tools. Alert on multiple failed attempts from the same IP or unusual token usage patterns (e.g., token used from two different countries within minutes).

## Summary

- **Never hardcode JWT secrets.** Use environment variables or secret managers like Azure Key Vault, and ensure the key is at least 256 bits.
- **Validate issuer, audience, and lifetime strictly.** Configure `TokenValidationParameters` in [`Program.cs`](https://github.com/AIDotNet/OpenDeepWiki/blob/main/Program.cs) with `ClockSkew = TimeSpan.Zero` and explicit `ValidIssuer`/`ValidAudience` values.
- **Use short-lived access tokens.** Set `ExpirationMinutes` to 15–30 minutes instead of the default 24 hours, and implement refresh tokens for session continuity.
- **Enable HTTPS and security headers.** Set `RequireHttpsMetadata = true` and configure reverse proxies to enforce TLS 1.2+.
- **Plan for revocation and rotation.** Include `Jti` claims in tokens, implement a Redis blacklist for logout, and schedule regular key rotation with `kid` headers.

## Frequently Asked Questions

### How should I store the JWT secret key in production?

Store the JWT secret key in a dedicated secret manager such as Azure Key Vault, AWS Secrets Manager, or HashiCorp Vault. Alternatively, use environment variables injected at runtime (e.g., `JWT_SECRET_KEY`), but never commit secrets to source control or include them in [`appsettings.json`](https://github.com/AIDotNet/OpenDeepWiki/blob/main/appsettings.json) files that are checked into Git. Ensure the key is cryptographically strong—at least 256 bits (32 bytes) encoded as Base64.

### What is the recommended token expiration time for production APIs?

For production APIs, set the access token expiration to 15–30 minutes instead of the default 1440 minutes (24 hours) defined in `JwtOptions.ExpirationMinutes`. Short-lived tokens reduce the window of opportunity for attackers if a token is compromised. Implement a refresh token mechanism with secure, `HttpOnly` cookies to maintain user sessions without requiring frequent re-authentication.

### How can I revoke JWTs when a user logs out or a token is compromised?

Since JWTs are stateless by default, implement a revocation list using a distributed cache like Redis. When generating tokens in [`JwtService.cs`](https://github.com/AIDotNet/OpenDeepWiki/blob/main/JwtService.cs), include a unique `Jti` (JWT ID) claim using `JwtRegisteredClaimNames.Jti`. When a user logs out, add that `Jti` to a Redis blacklist with an expiration matching the token's lifetime. Add middleware to check the cache before processing requests, rejecting tokens with blacklisted `Jti` values.

### Should I use symmetric (HMAC) or asymmetric (RSA/ECDSA) signing for OpenDeepWiki?

OpenDeepWiki currently uses symmetric HMAC-SHA256 (`SecurityAlgorithms.HmacSha256`) in [`JwtService.cs`](https://github.com/AIDotNet/OpenDeepWiki/blob/main/JwtService.cs). For single-server deployments, this is acceptable if the secret key is properly protected. However, for distributed microservices or when you need to validate tokens in multiple services without sharing the private key, migrate to asymmetric signing such as RS256 (RSA) or ES256 (ECDSA). This allows the authorization server to hold the private key while resource servers only need the public key for validation, enabling safer key rotation and separation of concerns.