{"record":{"id":"c72a954489cb34b3","repo":"tursodatabase/turso","slug":"auth-token-requires-an-https-sync-url-unless-the-h","errorCode":null,"errorMessage":"Auth Token requires an HTTPS sync URL unless the host is localhost or loopback.","messagePattern":"Auth Token requires an HTTPS sync URL unless the host is localhost or loopback\\.","errorType":"validation","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"bindings/dotnet/src/Turso.Data/TursoSyncDatabaseOptions.cs","lineNumber":163,"sourceCode":"            Scheme = scheme,\n            Port = RemoteUri.IsDefaultPort ? -1 : RemoteUri.Port,\n            UserName = string.Empty,\n            Password = string.Empty,\n        };\n        return builder.Uri;\n    }\n\n    internal void Validate()\n    {\n        ArgumentException.ThrowIfNullOrWhiteSpace(Path);\n        ArgumentException.ThrowIfNullOrWhiteSpace(ClientName);\n\n        var normalizedUri = GetNormalizedRemoteUri();\n        if (!string.IsNullOrWhiteSpace(AuthToken)\n            && normalizedUri.Scheme != Uri.UriSchemeHttps\n            && !normalizedUri.IsLoopback)\n        {\n            throw new InvalidOperationException(\n                \"Auth Token requires an HTTPS sync URL unless the host is localhost or loopback.\");\n        }\n\n        if (LongPollTimeout is { } timeout\n            && (timeout < TimeSpan.FromMilliseconds(1) || timeout.TotalMilliseconds > int.MaxValue))\n        {\n            throw new ArgumentOutOfRangeException(\n                nameof(LongPollTimeout),\n                timeout,\n                $\"Long-poll timeout must be between 1 and {int.MaxValue} milliseconds.\");\n        }\n\n        ValidateNativeSize(PushOperationsThreshold, nameof(PushOperationsThreshold));\n        ValidateNativeSize(PullBytesThreshold, nameof(PullBytesThreshold));\n        PartialSync?.Validate();\n\n        if (PartialSync is not null && !BootstrapIfEmpty)\n            throw new InvalidOperationException(\"Partial sync requires BootstrapIfEmpty=True.\");","sourceCodeStart":145,"sourceCodeEnd":181,"githubUrl":"https://github.com/tursodatabase/turso/blob/c1e59287258d99b309e362a63f48822256e2f65f/bindings/dotnet/src/Turso.Data/TursoSyncDatabaseOptions.cs#L145-L181","documentation":"Validate refuses a configuration where an AuthToken is supplied but the normalized sync URL is plain HTTP and the host is not localhost/loopback. Sending an auth token in cleartext over the network to a non-local host would leak credentials, so the combination is disallowed.","triggerScenarios":"Creating a TursoSyncDatabase with options where AuthToken is set, RemoteUri uses scheme http (or turso/libsql never hit this since they normalize to https), and the normalized host is not loopback (e.g. http://192.168.1.10:8080) — TursoSyncDatabaseOptions.cs:159-165.","commonSituations":"Pointing at an on-prem sync server over LAN HTTP while still sending a token, migrating a localhost dev config (http://localhost) to a staging server without switching to HTTPS, forgetting that turso/libsql schemes are https but http:// is not.","solutions":["Change the remote URL to https:// (or turso:// / libsql:// which normalize to HTTPS).","If the server genuinely has no TLS, use it only on localhost/loopback or deploy TLS in front of it.","Remove the AuthToken if the endpoint intentionally requires no auth (not recommended for non-local hosts)."],"exampleFix":"// before\nvar opts = new TursoSyncDatabaseOptions(path, new Uri(\"http://sync.internal:8080\")) { AuthToken = token };\n// after\nvar opts = new TursoSyncDatabaseOptions(path, new Uri(\"https://sync.internal:8443\")) { AuthToken = token };","handlingStrategy":"validation","validationCode":"static void EnsureTokenOverHttps(Uri remoteUri, string? authToken)\n{\n    if (string.IsNullOrWhiteSpace(authToken)) return;\n    var scheme = remoteUri.Scheme.ToLowerInvariant();\n    var isHttps = scheme is \"https\" or \"turso\" or \"libsql\";\n    if (!isHttps && !remoteUri.IsLoopback)\n        throw new InvalidOperationException(\"Refusing to send AuthToken over plain HTTP to a non-local host.\");\n}","typeGuard":"static bool TokenTransportIsSafe(Uri u, string? token) =>\n    string.IsNullOrWhiteSpace(token) || u.Scheme.ToLowerInvariant() is \"https\" or \"turso\" or \"libsql\" || u.IsLoopback;","tryCatchPattern":"try { var db = new TursoSyncDatabase(opts); }\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"HTTPS sync URL\"))\n{\n    // switch scheme to https (or deploy TLS) before retrying\n}","preventionTips":["Use https (or turso/libsql schemes) for every non-loopback sync endpoint.","Only use http:// against localhost/loopback dev servers.","Add a config-lint step that fails builds when AuthToken is paired with a non-HTTPS remote URL."],"tags":["dotnet","sync","security","https","auth-token"],"backgroundTag":"insecure-http-with-credentials","analyzedSha":"c1e59287258d99b309e362a63f48822256e2f65f","analyzedAt":"2026-08-31T11:17:35.598Z","contentChangedAt":"2026-08-31T11:17:35.598Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}