Advanced connection information

Connection URI

The driver supports connection to URIs of the form

<SCHEME>://<HOST>[:<PORT>[?policy=<POLICY-NAME>]]
  • <SCHEME> is one among neo4j, neo4j+s, neo4j+ssc, bolt, bolt+s, bolt+ssc.

  • <HOST> is the host name where the Neo4j server is located.

  • <PORT> is optional, and denotes the port the Bolt protocol is available at.

  • <POLICY-NAME> is an optional server policy name. Server policies need to be set up prior to usage.

The driver does not support connection to a nested path, such as example.com/neo4j/. The server must be reachable from the domain root.

Connection protocols and security

Communication between the driver and the server is mediated by Bolt. The scheme of the server URI determines whether the connection is encrypted and, if so, what type of certificates are accepted.

URL scheme Encryption Comment

neo4j

Default for local setups

neo4j+s

(only CA-signed certificates)

Default for Aura

neo4j+ssc

(CA- and self-signed certificates)

The driver receives a routing table from the server upon successful connection, regardless of whether the instance is a proper cluster environment or a single-machine environment. The driver’s routing behavior works in tandem with Neo4j’s clustering by directing read/write transactions to appropriate cluster members. If you want to target a specific machine, use the bolt, bolt+s, or bolt+ssc URI schemes instead.

The connection scheme to use is not your choice, but is rather determined by the server requirements. You must know the right server scheme upfront, as no metadata is exposed prior to connection. If you are unsure, ask the database administrator.

Authentication methods

Basic authentication (default)

The basic authentication scheme relies on traditional username and password. These can either be the credentials for your local installation, or the ones provided with an Aura instance.

await using var driver = GraphDatabase.Driver(dbUri, AuthTokens.Basic(dbUser, dbPassword));

The basic authentication scheme can also be used to authenticate against an LDAP server (Enterprise Edition only).

Kerberos authentication

The Kerberos authentication scheme requires a base64-encoded ticket. It can only be used if the server has the Kerberos Add-on installed.

await using var driver = GraphDatabase.Driver(dbUri, AuthTokens.Kerberos(ticket));

Bearer authentication

The bearer authentication scheme requires a base64-encoded token provided by an Identity Provider through Neo4j’s Single Sign-On feature.

await using var driver = GraphDatabase.Driver(dbUri, AuthTokens.Bearer(ticket));

Custom authentication

Use AuthTokens.Custom() to log into a server having a custom authentication scheme.

await using var driver = GraphDatabase.Driver(
    dbUri,
    AuthTokens.Custom(principal, credentials, realm, scheme, parameters)
);
// `parameters` is optional

No authentication

If authentication is disabled on the server, the authentication parameter can be omitted entirely.

Rotating authentication tokens

It is possible to rotate authentication tokens that are expected to expire (e.g. SSO). You need to provide an IAuthTokenManager instance when instantiating the IDriver, rather than a static authentication token.

The easiest way to get started is to use one of built-in AuthTokenManagers implementations. AuthTokenManagers work with AuthTokenAndExpiration objects.

Rotating a bearer token expiring every 60 seconds
await using var driver = GraphDatabase.Driver(
    dbUri,
    AuthTokenManagers.Bearer(GetTokenAsync));

Task<AuthTokenAndExpiration> GetTokenAsync() {
    var token = getToken();

    // Assume we know tokens expire every 60 seconds
    var expiresIn = 60 * 1000;
    // Include a little buffer so that new token is fetched before the old one expires
    expiresIn -= 10_000;

    return Task.FromResult(new AuthTokenAndExpiration(token, expiresIn));
}

// Some way to get a token
string getToken() {
    return "token-string";
}
This feature must not be used for switching users. Auth managers must always return tokens for the same identity. You can switch users at both query level and session level.
IAuthTokenManager objects must not interact with the driver in any way, as this can cause deadlocks and undefined behavior.

Mutual TLS (client-side certificates as 2FA)

Mutual TLS (mTLS) allows you to use a client certificate as second factor for authenticating with the server. The certificate can only be used together with an authentication token and is not a replacement of regular authentication, unless authentication is disabled on the server.

The client’s certificate and public key must be placed in the server’s <NEO4J_HOME>/certificates/bolt/trusted directory. For more information on server setup, see Configure SSL over Bolt.

For mTLS to work, the driver’s connection with the server must be encrypted, i.e. the connection URI scheme must be either +s or +ssc (ex. neo4j+s://example.com:7687).

Use the config method WithClientCertificateProvider() to provide an IClientCertificateProvider.
Your implementation must provide a thread-safe GetCertificateAsync() method, returning a ValueTask<X509Certificate2>.

using System.Security.Cryptography.X509Certificates;

class ClientCertificateProvider {

    public async ValueTask<X509Certificate2> GetCertificateAsync() {
        string certPath = "path/to/cert.pem";
        string certPassword = "password";

        X509Certificate2 certificate = new X509Certificate2(certPath, certPassword, X509KeyStorageFlags.MachineKeySet);
        return await Task.FromResult(certificate);
    }

}

await using var driver = GraphDatabase.Driver(
    dbUri,
    AuthTokens.Basic(dbUser, dbPassword),
    conf => conf.WithClientCertificateProvider(ClientCertificateProvider)
);
await driver.VerifyConnectivityAsync();

Logging

To enable logging use .withLogger(ILogger) config method when creating a Driver object, providing an implementation of ILogger.

Enable debug logging with example implementation of ILogger
using Neo4j.Driver;

const string dbUri = "<database-uri>";
const string dbUser = "<username>";
const string dbPassword = "<password>";

var logger = new Neo4jLogger();
logger.EnableDebug();

await using var driver = GraphDatabase.Driver(
    dbUri, AuthTokens.Basic(dbUser, dbPassword),
    conf => conf.WithLogger(logger)
);
await driver.VerifyConnectivityAsync();


public class Neo4jLogger : ILogger {

    private bool debug = false;
    private bool trace = false;

    public void Log(string level, string message) {
        DateTime localDate = DateTime.Now;
        Console.WriteLine($"{localDate.TimeOfDay} {level} {message}");
    }

    public void Error(Exception cause, string message, params object[] args) {
        this.Log("ERROR", String.Format($"{message}", args));
    }
    public void Warn(Exception cause, string message, params object[] args) {
        this.Log("WARN", String.Format($"{message}", args));
    }

    public void Info(string message, params object[] args) {
        this.Log("INFO", String.Format($"{message}", args));
    }

    public void Debug(string message, params object[] args) {
        this.Log("DEBUG", String.Format($"{message}", args));
    }

    public void Trace(string message, params object[] args) {
        this.Log("TRACE", String.Format($"{message}", args));
    }

    public void EnableDebug() {
        this.debug = true;
    }

    public void EnableTrace() {
        this.trace = true;
    }

    public bool IsTraceEnabled() {
        return this.trace;
    }

    public bool IsDebugEnabled() {
        return this.debug;
    }
}
Example of log output upon successful connection
12:34:27.3134245 DEBUG [conn-localhost:7687-1] ~~ [CONNECT] neo4j://localhost:7687/
12:34:27.3322335 DEBUG [conn-localhost:7687-1] C: [HANDSHAKE] 60 60 B0 17 00 07 07 05 00 02 04 04 00 00 01 04 00 00 00 03
12:34:27.3336518 DEBUG [conn-localhost:7687-1] S: [HANDSHAKE] 5.7
12:34:27.3523440 DEBUG [conn-localhost:7687-1] C: HELLO [{user_agent, neo4j-dotnet/5.27}, {routing, [{address, localhost:7687}]}, {bolt_agent, [{product, neo4j-dotnet/5.27.38}, {platform, Linux Mint 22.1;X64}, {language_details, .NET 8.0.16}]}]
12:34:27.3526200 DEBUG [conn-localhost:7687-1] C: LOGON [{scheme, basic}, {principal, neo4j}, {credentials, ******}]
12:34:27.3599672 DEBUG [conn-localhost:7687-1] S: SUCCESS [{server, Neo4j/2025.06.0-30853}, {connection_id, bolt-228}, {hints, [{connection.recv_timeout_seconds, 120}]}]
12:34:27.3650548 DEBUG [conn-localhost:7687-1] Connection 'conn-localhost:7687-1' renamed to 'bolt-228'. The new name identifies the connection uniquely both on the client side and the server side.
12:34:27.3655529 DEBUG [bolt-228] S: SUCCESS []
12:34:27.3714612 DEBUG [bolt-228] C: RESET
12:34:27.3745223 DEBUG [bolt-228] S: SUCCESS []
12:34:27.3806841 DEBUG [bolt-228] C: ROUTE { 'address':'localhost:7687' } [] { }
12:34:27.3823965 DEBUG [bolt-228] S: SUCCESS [{rt, [{servers, [[{addresses, [localhost:7687]}, {role, WRITE}], [{addresses, [localhost:7687]}, {role, READ}], [{addresses, [localhost:7687]}, {role, ROUTE}]]}, {ttl, 300}, {db, neo4j}]}]
12:34:27.3888866 DEBUG [bolt-228] C: RESET
12:34:27.3893542 DEBUG [bolt-228] S: SUCCESS []
12:34:27.3917428 INFO Routing table is updated => RoutingTable{database=neo4j, routers=[neo4j://localhost:7687/], writers=[neo4j://localhost:7687/], readers=[neo4j://localhost:7687/], expiresAfter=300s}
12:34:27.3918886 DEBUG [bolt-228] C: RESET
12:34:27.3922620 DEBUG [bolt-228] S: SUCCESS []
12:34:27.3949034 DEBUG [pool-localhost:7687] Disposing Available Connection bolt-228
12:34:27.3961484 DEBUG [bolt-228] C: GOODBYE

Custom address resolver

When creating an IDriver object, you can specify a resolver function to resolve the connection address the driver is initialized with. Note that addresses that the driver receives in routing tables are not resolved with the custom resolver.

You specify a resolver through the .WithResolver() config method, which works with IServerAddressResolver objects.

The resolved address must have the same port and URI scheme. In the example below, the driver is initialized with a neo4j:// URI scheme and port 7687, so the resolved address provided in dbUri must also be available at neo4j:// and port 7687.

Connection to example.com is resolved to localhost
using Neo4j.Driver;

const string dbUri = "localhost";  // omit scheme and port
const string dbUser = "<username>";
const string dbPassword = "<password>";

// setting a port different than the one given in `driver` won't work
var address = ServerAddress.From(dbUri, 7687);

await using var driver = GraphDatabase.Driver(
    "neo4j://example.com:7687",
    AuthTokens.Basic(dbUser, dbPassword),
    conf => conf.WithResolver(new ListAddressResolver(address))
);
await driver.VerifyConnectivityAsync();

class ListAddressResolver : IServerAddressResolver {
    private readonly ServerAddress[] _servers;

    public ListAddressResolver(params ServerAddress[] servers) {
        _servers = servers;
    }

    public ISet<ServerAddress> Resolve(ServerAddress address) {
        return new HashSet<ServerAddress>(_servers);
    }
}

Further connection parameters

You can find all IDriver configuration parameters in the API documentation → ConfigBuilder.

Glossary

LTS

A Long Term Support release is one guaranteed to be supported for a number of years. Neo4j 4.4 is LTS, and Neo4j 5 will also have an LTS version.

Aura

Aura is Neo4j’s fully managed cloud service. It comes with both free and paid plans.

Cypher

Cypher is Neo4j’s graph query language that lets you retrieve data from the database. It is like SQL, but for graphs.

APOC

Awesome Procedures On Cypher (APOC) is a library of (many) functions that can not be easily expressed in Cypher itself.

Bolt

Bolt is the protocol used for interaction between Neo4j instances and drivers. It listens on port 7687 by default.

ACID

Atomicity, Consistency, Isolation, Durability (ACID) are properties guaranteeing that database transactions are processed reliably. An ACID-compliant DBMS ensures that the data in the database remains accurate and consistent despite failures.

eventual consistency

A database is eventually consistent if it provides the guarantee that all cluster members will, at some point in time, store the latest version of the data.

causal consistency

A database is causally consistent if read and write queries are seen by every member of the cluster in the same order. This is stronger than eventual consistency.

NULL

The null marker is not a type but a placeholder for absence of value. For more information, see Cypher → Working with null.

transaction

A transaction is a unit of work that is either committed in its entirety or rolled back on failure. An example is a bank transfer: it involves multiple steps, but they must all succeed or be reverted, to avoid money being subtracted from one account but not added to the other.

backpressure

Backpressure is a force opposing the flow of data. It ensures that the client is not being overwhelmed by data faster than it can handle.

transaction function

A transaction function is a callback executed by an .ExecuteReadAsync() or .ExecuteWriteAsync() call. The driver automatically re-executes the callback in case of server failure.

IDriver

A IDriver object holds the details required to establish connections with a Neo4j database.