OpenDeepWiki Security Best Practices for JWT Authentication in Production

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 (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 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:
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:

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 (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:

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 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 files that are checked into Git. Ensure the key is cryptographically strong—at least 256 bits (32 bytes) encoded as Base64.

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, 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. 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.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →