579522c586
The cookie SecurePolicy was hard-coded to Always, so the auth cookie was always marked Secure and the browser never sent it over plain HTTP — making login impossible on the HTTP-only Docker dev cluster (login succeeded server-side but every following request was unauthenticated). Add SecurityOptions.RequireHttps- Cookie (default true — production stays HTTPS-only); when false the cookie uses SameAsRequest. The docker/ central nodes set it false.
106 lines
4.4 KiB
C#
106 lines
4.4 KiB
C#
namespace ScadaLink.Security;
|
|
|
|
public class SecurityOptions
|
|
{
|
|
public string LdapServer { get; set; } = string.Empty;
|
|
public int LdapPort { get; set; } = 389;
|
|
|
|
/// <summary>
|
|
/// Transport security mode for the LDAP connection. Defaults to LDAPS.
|
|
/// Use <see cref="LdapTransport.StartTls"/> to connect on the plaintext port
|
|
/// and upgrade the session before binding.
|
|
/// </summary>
|
|
public LdapTransport LdapTransport { get; set; } = LdapTransport.Ldaps;
|
|
|
|
/// <summary>
|
|
/// True when the configured transport provides encryption (LDAPS or StartTLS).
|
|
/// Retained for backward compatibility: assigning a value maps onto
|
|
/// <see cref="LdapTransport"/> (true => LDAPS, false => None).
|
|
/// </summary>
|
|
public bool LdapUseTls
|
|
{
|
|
get => LdapTransport != LdapTransport.None;
|
|
set => LdapTransport = value ? LdapTransport.Ldaps : LdapTransport.None;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Allow insecure (non-TLS) LDAP connections. ONLY for dev/test with GLAuth.
|
|
/// Must be false in production.
|
|
/// </summary>
|
|
public bool AllowInsecureLdap { get; set; } = false;
|
|
|
|
/// <summary>
|
|
/// Base DN for LDAP searches (e.g., "dc=example,dc=com").
|
|
/// </summary>
|
|
public string LdapSearchBase { get; set; } = string.Empty;
|
|
|
|
/// <summary>
|
|
/// Service account DN for LDAP user searches (e.g., "cn=admin,dc=example,dc=com").
|
|
/// Required for search-then-bind authentication. If empty, direct bind with
|
|
/// {LdapUserIdAttribute}={username},{LdapSearchBase} is attempted instead.
|
|
/// </summary>
|
|
public string LdapServiceAccountDn { get; set; } = string.Empty;
|
|
|
|
/// <summary>
|
|
/// LDAP attribute that identifies a user. Used both for the search-then-bind
|
|
/// filter (<c>({LdapUserIdAttribute}={username})</c>) and for constructing the
|
|
/// fallback bind DN when no service account is configured, so the two
|
|
/// authentication modes are interchangeable. Common values: <c>uid</c> (OpenLDAP),
|
|
/// <c>sAMAccountName</c> (Active Directory).
|
|
/// </summary>
|
|
public string LdapUserIdAttribute { get; set; } = "uid";
|
|
|
|
/// <summary>
|
|
/// Service account password for LDAP user searches.
|
|
/// </summary>
|
|
public string LdapServiceAccountPassword { get; set; } = string.Empty;
|
|
|
|
/// <summary>
|
|
/// LDAP attribute that contains the user's display name.
|
|
/// </summary>
|
|
public string LdapDisplayNameAttribute { get; set; } = "cn";
|
|
|
|
/// <summary>
|
|
/// LDAP attribute that contains group membership.
|
|
/// </summary>
|
|
public string LdapGroupAttribute { get; set; } = "memberOf";
|
|
|
|
/// <summary>
|
|
/// Network timeout, in milliseconds, applied to the LDAP socket connect and to
|
|
/// LDAP operations (bind/search). The synchronous Novell LDAP calls are wrapped
|
|
/// in <c>Task.Run</c>, where the <c>CancellationToken</c> only guards work-item
|
|
/// scheduling — it cannot interrupt an in-progress blocking call. This timeout is
|
|
/// the real safeguard: it bounds how long a hung LDAP server can pin a thread-pool
|
|
/// thread (Security-009). Default 10 seconds.
|
|
/// </summary>
|
|
public int LdapConnectionTimeoutMs { get; set; } = 10_000;
|
|
|
|
/// <summary>
|
|
/// Symmetric HMAC-SHA256 signing key for cookie-embedded JWTs. Must be at least
|
|
/// 32 bytes (256 bits) — validated at <see cref="JwtTokenService"/> construction.
|
|
/// </summary>
|
|
public string JwtSigningKey { get; set; } = string.Empty;
|
|
|
|
/// <summary>
|
|
/// Minimum signing-key length in bytes required for HMAC-SHA256 (256 bits).
|
|
/// </summary>
|
|
public const int MinJwtSigningKeyBytes = 32;
|
|
public int JwtExpiryMinutes { get; set; } = 15;
|
|
public int IdleTimeoutMinutes { get; set; } = 30;
|
|
|
|
/// <summary>
|
|
/// Minutes before token expiry to trigger refresh.
|
|
/// </summary>
|
|
public int JwtRefreshThresholdMinutes { get; set; } = 5;
|
|
|
|
/// <summary>
|
|
/// When true (default) the authentication cookie is always marked
|
|
/// <c>Secure</c> (sent only over HTTPS) — the correct production setting,
|
|
/// since the cookie carries the embedded JWT bearer credential. Set false
|
|
/// for an HTTP-only deployment such as the local Docker dev cluster: the
|
|
/// cookie then uses <c>SameAsRequest</c>, so it is still <c>Secure</c> on
|
|
/// any HTTPS request but is usable over plain HTTP.
|
|
/// </summary>
|
|
public bool RequireHttpsCookie { get; set; } = true;
|
|
}
|