Appearance
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 asPOST /api/v1/auth/loginand password reset request.Authenticated: used in authenticated flows such as refresh token, switch tenant, and protected APIs.
Policy Scope
PreAuthenticationruns only checks that are safe without a resolved user identity.Authenticatedruns 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 examplerefresh_tokenand 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:
IZeroTrustPolicyResolverDefaultZeroTrustPolicyResolver
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-uprecommendation (RequiresAdditionalAuth) instead of automatic deny. RiskThresholdis used for step-up escalation, not for blind request denial.- Login hard-block now returns code
AUTH_RISK_FORBIDDEN(HTTP403).
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 (
preauthvsauth) 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_idclaim,X-Security-Session-Id/X-Session-Idheaders, 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:PreAuthenticationorAuthenticatedzero_trust.decision:allow,step_up,blockedzero_trust.violations_countzero_trust.warnings_countzero_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(HTTP401via canonical error-code mapping)
- message key:
- Other zero-trust blocking violations keep their original operation-specific contract (for example
AUTH_RISK_FORBIDDENin 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_postauthmust not be blocked by missing pre-existing session - hard-block semantics vs soft-signal step-up semantics
- anti self-poisoning rule for
zero_trust_validationevents - 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
| Event | Description |
|---|---|
UserLogin | Successful login |
UserLoginFailed | Failed login attempt |
UserLogout | User logout |
UserRegistered | New user registration |
PasswordChanged | Password change |
PasswordResetRequested | Password reset request |
RoleAssigned | Role assigned to user |
RoleRevoked | Role removed from user |
PermissionGranted | Permission granted |
PermissionRevoked | Permission revoked |
TokenRefreshed | Token refreshed |
TokenRevoked | Token manually revoked |
AccountLocked | Account locked due to failed attempts |
AccountUnlocked | Account 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_currentSecurity 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