Skip to content

Security Features

GrydAuth includes enterprise-grade security features to protect your application against common threats.

Zero Trust Context Policies

Zero Trust now uses explicit validation contexts to avoid false positives and security drift:

  • PreAuthentication: used in unauthenticated flows such as POST /api/v1/auth/login and password reset request.
  • Authenticated: used in authenticated flows such as refresh token, switch tenant, and protected APIs.

Policy Scope

  • PreAuthentication runs only checks that are safe without a resolved user identity.
  • Authenticated runs full behavioral checks that depend on a known user/session.
  • Session integrity is explicitly skipped for bootstrap actions (login, login_postauth, switch_tenant) and required for token/session continuation actions (for example refresh_token and authenticated API requests).

This separation prevents login from using user-bound checks before user identity exists.

Policy Resolver (SOLID)

Zero Trust check orchestration is now centralized in a dedicated policy resolver:

  • IZeroTrustPolicyResolver
  • DefaultZeroTrustPolicyResolver

This removes rule branching from the validator internals and keeps policy decisions in one place (Context + Action), improving maintainability and reducing behavior drift across handlers/middleware.

Decision Model

  • Blocking is now driven by explicit blocking signals only (hard-block).
  • Risk score and soft signals produce step-up recommendation (RequiresAdditionalAuth) instead of automatic deny.
  • RiskThreshold is used for step-up escalation, not for blind request denial.
  • Login hard-block now returns code AUTH_RISK_FORBIDDEN (HTTP 403).

Threat Intelligence Promotion Rules

  • High-risk telemetry events are no longer enough by themselves to blacklist an IP.
  • Threat intel promotion now requires explicit event allow-list (for example: manual_ip_block, external_threat_feed_hit, credential_stuffing_detected) or explicit metadata override.
  • This prevents self-poisoning scenarios where internal validation telemetry blocks legitimate users.

Cache Isolation and TTL

  • Zero Trust cache state is now context-scoped (preauth vs auth) for threat intel and rate-limit dimensions.
  • Login-related threat intel keys use shorter retention (ThreatIntelPreAuth = 15 minutes).
  • Authenticated threat intel keeps standard retention (ThreatIntel = 1 hour).
  • This reduces cross-flow contamination and limits accidental lockout duration in pre-auth flows.

Canonical Security Session Store

Session integrity checks now use a dedicated store abstraction (ISecuritySessionStore) instead of ad-hoc cache key construction.

  • Canonical key: grydauth:security:session:{userId}:{sessionId}
  • Read/write/remove operations are centralized in SecuritySessionStore
  • Zero Trust session validation no longer depends on implicit string keys
  • Successful login persists a security session entry that is later consumed by authenticated zero-trust checks.
  • Middleware resolves session ID in this order: session_id claim, X-Security-Session-Id/X-Session-Id headers, then ASP.NET session ID fallback.

Operational Recovery Endpoint

To clear stale Zero Trust state without flushing Redis globally:

  • POST /api/v1/security/clear-state
  • Permission: admin:system
  • Request body:
json
{
  "ipAddress": "127.0.0.1",
  "userId": "11111111-1111-1111-1111-111111111111"
}

At least one of ipAddress or userId must be provided.

Observability Signals

Zero Trust now emits explicit decision telemetry:

  • zero_trust.context: PreAuthentication or Authenticated
  • zero_trust.decision: allow, step_up, blocked
  • zero_trust.violations_count
  • zero_trust.warnings_count
  • zero_trust.requires_step_up

Structured logs also capture threat-intel source and promotion decisions for forensic analysis.

Error Contract Mapping

Zero Trust handler failures now use centralized mapping (ZeroTrustFailureMapper):

  • Session-related blocking violations map to:
    • message key: Authentication.Session.InvalidOrExpired
    • code: SESSION_INVALIDATED (HTTP 401 via canonical error-code mapping)
  • Other zero-trust blocking violations keep their original operation-specific contract (for example AUTH_RISK_FORBIDDEN in login).

Frontend Consumption Rules

  • POST /api/v1/auth/login:
    • 403 + AUTH_RISK_FORBIDDEN => bloqueio por risco (sem logout automatico).
    • 401 + SESSION_INVALIDATED => sessao invalida (iniciar reautenticacao).
  • Endpoints autenticados:
    • 401 (SESSION_INVALIDATED, TOKEN_EXPIRED, TOKEN_INVALID, TOKEN_REVOKED) => limpar sessao local.
    • 403 (MISSING_PERMISSION, FORBIDDEN, TENANT_FORBIDDEN, CROSS_TENANT_FORBIDDEN) => manter sessao e exibir sem acesso.

Regression Coverage

Security regression tests now cover:

  • pre-auth vs post-auth zero-trust contexts in login flow
  • regression: login_postauth must not be blocked by missing pre-existing session
  • hard-block semantics vs soft-signal step-up semantics
  • anti self-poisoning rule for zero_trust_validation events
  • API contract for login risk blocking (403 + AUTH_RISK_FORBIDDEN)
  • middleware session ID propagation precedence (claim > header > ASP.NET session)

Rate Limiting

Protect endpoints against brute-force attacks and abuse.

Configuration

csharp
builder.Services.AddGrydAuth(options =>
{
    options.RateLimiting.Enabled = true;
    
    // Global rate limit
    options.RateLimiting.GlobalLimit = new RateLimitConfig
    {
        PermitLimit = 1000,
        Window = TimeSpan.FromMinutes(1)
    };
    
    // Auth endpoints (stricter)
    options.RateLimiting.AuthEndpointsLimit = new RateLimitConfig
    {
        PermitLimit = 10,
        Window = TimeSpan.FromMinutes(1)
    };
    
    // Per-user limits
    options.RateLimiting.PerUserLimit = new RateLimitConfig
    {
        PermitLimit = 100,
        Window = TimeSpan.FromMinutes(1)
    };
});

Custom Rate Limit Policies

csharp
builder.Services.AddRateLimiter(options =>
{
    options.AddPolicy("sensitive-operations", context =>
        RateLimitPartition.GetFixedWindowLimiter(
            partitionKey: context.User.Identity?.Name ?? context.Connection.RemoteIpAddress?.ToString(),
            factory: _ => new FixedWindowRateLimiterOptions
            {
                PermitLimit = 5,
                Window = TimeSpan.FromMinutes(15)
            }));
});

// Usage
[EnableRateLimiting("sensitive-operations")]
public IActionResult DeleteAccount() { ... }

Audit Logging

Track all security-related events.

Enable Audit Logging

csharp
builder.Services.AddGrydAuth(options =>
{
    options.AuditLogging.Enabled = true;
    options.AuditLogging.LogSuccessfulLogins = true;
    options.AuditLogging.LogFailedLogins = true;
    options.AuditLogging.LogPermissionChanges = true;
    options.AuditLogging.LogTokenOperations = true;
    options.AuditLogging.RetentionDays = 90;
});

Audit Events

EventDescription
UserLoginSuccessful login
UserLoginFailedFailed login attempt
UserLogoutUser logout
UserRegisteredNew user registration
PasswordChangedPassword change
PasswordResetRequestedPassword reset request
RoleAssignedRole assigned to user
RoleRevokedRole removed from user
PermissionGrantedPermission granted
PermissionRevokedPermission revoked
TokenRefreshedToken refreshed
TokenRevokedToken manually revoked
AccountLockedAccount locked due to failed attempts
AccountUnlockedAccount unlocked

Querying Audit Logs

http
GET /api/v1/audit-logs?userId=550e8400-e29b-41d4-a716-446655440000&eventType=UserLogin&from=2026-01-01&to=2026-01-31
Authorization: Bearer {admin-token}

Response:

json
{
  "items": [
    {
      "id": "123e4567-e89b-12d3-a456-426614174000",
      "userId": "550e8400-e29b-41d4-a716-446655440000",
      "eventType": "UserLogin",
      "ipAddress": "192.168.1.100",
      "userAgent": "Mozilla/5.0...",
      "timestamp": "2026-01-15T10:30:00Z",
      "success": true,
      "metadata": {
        "method": "Password",
        "mfaUsed": false
      }
    }
  ],
  "totalCount": 42,
  "page": 1,
  "pageSize": 20
}

Custom Audit Events

csharp
public class OrderService
{
    private readonly IAuditLogger _auditLogger;
    
    public async Task<Order> CreateOrderAsync(CreateOrderRequest request)
    {
        var order = await _orderRepository.CreateAsync(request);
        
        await _auditLogger.LogAsync(new AuditEvent
        {
            EventType = "OrderCreated",
            EntityType = "Order",
            EntityId = order.Id.ToString(),
            Metadata = new Dictionary<string, object>
            {
                ["amount"] = order.TotalAmount,
                ["items"] = order.Items.Count
            }
        });
        
        return order;
    }
}

Geo-Location Tracking

Track login locations and detect suspicious activity.

Configuration

csharp
builder.Services.AddGrydAuth(options =>
{
    options.GeoLocation.Enabled = true;
    options.GeoLocation.Provider = GeoLocationProvider.MaxMind;
    options.GeoLocation.BlockSuspiciousLogins = true;
    options.GeoLocation.TrustedCountries = ["US", "CA", "GB", "DE"];
});

MaxMind GeoIP Setup

csharp
builder.Services.AddMaxMindGeoIp(options =>
{
    options.AccountId = Configuration["MaxMind:AccountId"]!;
    options.LicenseKey = Configuration["MaxMind:LicenseKey"]!;
    options.DatabasePath = Path.Combine(AppContext.BaseDirectory, "GeoLite2-City.mmdb");
    options.AutoUpdate = true;
    options.UpdateIntervalDays = 7;
});

Location-Based Alerts

csharp
public class LocationSecurityHandler : ILoginEventHandler
{
    private readonly INotificationService _notifications;
    private readonly IGeoLocationService _geoLocation;
    
    public async Task HandleLoginAsync(LoginEvent loginEvent)
    {
        var currentLocation = await _geoLocation.GetLocationAsync(loginEvent.IpAddress);
        var lastLocation = await GetLastLoginLocationAsync(loginEvent.UserId);
        
        if (lastLocation != null && IsUnusualLocation(currentLocation, lastLocation))
        {
            await _notifications.SendAsync(
                loginEvent.UserId,
                "New Login Location",
                $"We detected a login from {currentLocation.City}, {currentLocation.Country}. " +
                "If this wasn't you, please change your password immediately."
            );
        }
    }
    
    private bool IsUnusualLocation(GeoLocation current, GeoLocation last)
    {
        // Check if locations are suspiciously far apart for the time elapsed
        var distance = CalculateDistance(current, last);
        var timeElapsed = current.Timestamp - last.Timestamp;
        var maxPossibleSpeed = 1000; // km/h (roughly commercial airline speed)
        
        return distance / timeElapsed.TotalHours > maxPossibleSpeed;
    }
}

Token Security

Token Invalidation

csharp
// Invalidate specific token
await _tokenService.RevokeTokenAsync(refreshToken);

// Invalidate all user tokens
await _tokenService.RevokeAllUserTokensAsync(userId);

// Invalidate with reason
await _tokenService.RevokeTokenAsync(refreshToken, new RevocationReason
{
    Type = RevocationType.SecurityConcern,
    Description = "Suspicious activity detected"
});

Token Versioning

GrydAuth uses token versioning to invalidate tokens instantly:

csharp
public class User : AggregateRoot<Guid>
{
    public long TokenVersion { get; private set; } = 1;
    
    public void IncrementTokenVersion()
    {
        TokenVersion++;
        AddDomainEvent(new TokensInvalidatedEvent(Id, TokenVersion));
    }
}

All tokens issued before the current version are automatically rejected.

Blacklist Caching

csharp
builder.Services.AddGrydAuth(options =>
{
    options.TokenBlacklist.Enabled = true;
    options.TokenBlacklist.CacheProvider = BlacklistCacheProvider.Redis;
    options.TokenBlacklist.CacheDuration = TimeSpan.FromDays(7);
});

builder.Services.AddStackExchangeRedisCache(options =>
{
    options.Configuration = Configuration.GetConnectionString("Redis");
    options.InstanceName = "GrydAuth:";
});

Security Headers

GrydAuth automatically adds security headers:

csharp
app.UseGrydAuth(); // Adds security middleware

// Headers added:
// X-Content-Type-Options: nosniff
// X-Frame-Options: DENY
// X-XSS-Protection: 1; mode=block
// Referrer-Policy: strict-origin-when-cross-origin
// Content-Security-Policy: default-src 'self'

Custom Security Headers

csharp
builder.Services.AddGrydAuth(options =>
{
    options.SecurityHeaders.EnableHSTS = true;
    options.SecurityHeaders.HSTSMaxAge = TimeSpan.FromDays(365);
    options.SecurityHeaders.IncludeSubDomains = true;
    options.SecurityHeaders.CustomHeaders = new Dictionary<string, string>
    {
        ["Permissions-Policy"] = "geolocation=(), microphone=(), camera=()"
    };
});

Input Validation

Automatic Sanitization

csharp
builder.Services.AddGrydAuth(options =>
{
    options.InputValidation.SanitizeHtml = true;
    options.InputValidation.PreventSqlInjection = true;
    options.InputValidation.MaxRequestBodySize = 1024 * 1024; // 1MB
});

Request Validation

csharp
public class LoginRequestValidator : AbstractValidator<LoginRequest>
{
    public LoginRequestValidator()
    {
        RuleFor(x => x.Email)
            .NotEmpty()
            .EmailAddress()
            .MaximumLength(256);
        
        RuleFor(x => x.Password)
            .NotEmpty()
            .MinimumLength(8)
            .MaximumLength(128)
            .Must(NotContainSqlInjection).WithMessage("Invalid characters detected");
    }
    
    private bool NotContainSqlInjection(string password)
    {
        var sqlPatterns = new[] { "'", "--", ";", "/*", "*/", "xp_" };
        return !sqlPatterns.Any(p => password.Contains(p, StringComparison.OrdinalIgnoreCase));
    }
}

CORS Configuration

csharp
builder.Services.AddCors(options =>
{
    options.AddPolicy("GrydAuthPolicy", policy =>
    {
        policy
            .WithOrigins(
                "https://yourapp.com",
                "https://admin.yourapp.com"
            )
            .AllowAnyHeader()
            .AllowAnyMethod()
            .AllowCredentials()
            .SetPreflightMaxAge(TimeSpan.FromMinutes(10));
    });
});

app.UseCors("GrydAuthPolicy");

Encryption

Data Encryption

csharp
builder.Services.AddGrydAuth(options =>
{
    options.Encryption.EncryptSensitiveData = true;
    options.Encryption.Algorithm = EncryptionAlgorithm.AES256;
    options.Encryption.KeyVaultUrl = Configuration["Azure:KeyVaultUrl"];
});

Password Hashing

GrydAuth uses bcrypt for password hashing:

csharp
builder.Services.AddGrydAuth(options =>
{
    options.PasswordHashing.Algorithm = HashingAlgorithm.BCrypt;
    options.PasswordHashing.WorkFactor = 12; // Higher = more secure but slower
});

Security Monitoring

Health Checks

csharp
builder.Services.AddHealthChecks()
    .AddGrydAuthHealthChecks();

app.MapHealthChecks("/health", new HealthCheckOptions
{
    ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
});

Security Metrics

csharp
builder.Services.AddGrydAuth(options =>
{
    options.Metrics.Enabled = true;
    options.Metrics.Provider = MetricsProvider.Prometheus;
});

// Metrics exposed:
// gryd_auth_login_attempts_total
// gryd_auth_login_failures_total
// gryd_auth_tokens_issued_total
// gryd_auth_tokens_revoked_total
// gryd_auth_active_sessions_current

Security Checklist

Before going to production, ensure:

  • [ ] JWT secret is stored in secure vault (not appsettings.json)
  • [ ] HTTPS is enforced everywhere
  • [ ] Rate limiting is enabled for auth endpoints
  • [ ] Audit logging is enabled
  • [ ] Password policy meets requirements
  • [ ] Token expiration times are appropriate
  • [ ] CORS is properly configured
  • [ ] Security headers are enabled
  • [ ] Database connections are encrypted (SSL/TLS)
  • [ ] Sensitive data is encrypted at rest
  • [ ] Regular security audits are scheduled

Released under the MIT License.