diff --git a/go-manual/modules/ROOT/pages/connect-advanced.adoc b/go-manual/modules/ROOT/pages/connect-advanced.adoc index a12b3dcb..12fcf00c 100644 --- a/go-manual/modules/ROOT/pages/connect-advanced.adoc +++ b/go-manual/modules/ROOT/pages/connect-advanced.adoc @@ -64,6 +64,43 @@ driver, err := neo4j.NewDriverWithContext(dbUri, neo4j.NoAuth()) ---- +[role=label--new-5.14] +== Rotating authentication tokens + +It is possible to rotate authentication tokens that are expected to expire (e.g. SSO). +You need to provide a link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v5/neo4j/auth#TokenManager[`TokenManager`] instance when instantiating the `Driver`, rather than a static authentication token. + +The easiest way to get started is to use one of built-in implementations: link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v5/neo4j/auth#BasicTokenManager[`BasicTokenManager`] and link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v5/neo4j/auth#BearerTokenManager[`BearerTokenManager`]. + +.Rotating a bearer token expiring every 60 seconds +[source, go, test-skip] +---- +fetchAuthTokenFromProvider := func(ctx context.Context) (neo4j.AuthToken, *time.Time, error) { + // some way of getting a token + token, err := getSsoToken(ctx) + if err != nil { + return neo4j.AuthToken{}, nil, err + } + // assume we know tokens expire every 60 seconds + expiresIn := time.Now().Add(60 * time.Second) + // include a little buffer so that we fetch a new token before the old one expires + expiresIn = expiresIn.Add(-10 * time.Second) + // or return nil instead of `&expiresIn` if we don't expect it to expire + return token, &expiresIn, nil +} + +// create a new driver with a bearer token manager +_, _ = neo4j.NewDriverWithContext(dbUri, auth.BearerTokenManager(fetchAuthTokenFromProvider)) +---- + +[NOTE] +This API must not be used for switching users. Auth managers must always return tokens for the same identity. +You can switch users at both xref:query-simple.adoc#impersonation[query level] and xref:transactions.adoc#impersonation[session level]. + +[WARNING] +`TokenManager` implementations and providers must not interact with the driver in any way, as this can cause deadlocks and undefined behavior. + + == Custom address resolver When creating a `DriverWithContext` object, you can specify a _resolver_ function to resolve the connection address the driver is initialized with. diff --git a/java-manual/modules/ROOT/pages/connect-advanced.adoc b/java-manual/modules/ROOT/pages/connect-advanced.adoc index 68864135..75d79c4c 100644 --- a/java-manual/modules/ROOT/pages/connect-advanced.adoc +++ b/java-manual/modules/ROOT/pages/connect-advanced.adoc @@ -72,6 +72,52 @@ GraphDatabase.driver(dbUri, AuthTokens.custom(principal, credentials, realm, sch If authentication is disabled on the server, the authentication parameter can be omitted entirely. +[role=label--new-5.18] +== Rotating authentication tokens + +It is possible to rotate authentication tokens that are expected to expire (e.g. SSO). +You need to provide a link:https://neo4j.com/docs/api/java-driver/current/org.neo4j.driver/org/neo4j/driver/AuthTokenManager.html[`AuthTokenManager`] instance when instantiating the `Driver`, rather than a static authentication token. + +The easiest way to get started is to use link:https://neo4j.com/docs/api/java-driver/current/org.neo4j.driver/org/neo4j/driver/AuthTokenManagers.html[one of built-in `AuthTokenManager` implementations]. AuthTokenManagers work with link:https://neo4j.com/docs/api/java-driver/current/org.neo4j.driver/org/neo4j/driver/AuthTokens.html[`AuthToken`] objects. + +.Rotating a bearer token expiring every 60 seconds +[source, java, test-skip] +---- +// import java.time.Clock; +// import java.util.concurrent.TimeUnit; +// import org.neo4j.driver.AuthTokenManagers; +// import org.neo4j.driver.AuthTokens; +// import org.neo4j.driver.GraphDatabase; + +void rotatingBearerDriver() { + var tokenManager = AuthTokenManagers.bearer(() -> { + var token = AuthTokens.bearer(getToken()); + + // Assume we know tokens expire every 60 seconds + var expiresIn = TimeUnit.SECONDS.toMillis(60); + // Include a little buffer so that new token is fetched before the old one expires + var expiration = Clock.systemUTC().millis() + (expiresIn - TimeUnit.SECONDS.toMillis(10)); + + return token.expiringAt(expiration); + }); + + var driver = GraphDatabase.driver(dbUri, tokenManager); +} + +// Some way to get a token +private String getToken() { + return "token-string"; +} +---- + +[NOTE] +This API must not be used for switching users. Auth managers must always return tokens for the same identity. +You can switch users at both xref:query-simple.adoc#impersonation[query level] and xref:transactions.adoc#impersonation[session level]. + +[WARNING] +`AuthTokenManager` objects must not interact with the driver in any way, as this can cause deadlocks and undefined behavior. + + == Logging By default, the driver logs `INFO` messages through the Java logging framework `java.util.logging`. diff --git a/javascript-manual/modules/ROOT/pages/connect-advanced.adoc b/javascript-manual/modules/ROOT/pages/connect-advanced.adoc index 26958417..244de1ef 100644 --- a/javascript-manual/modules/ROOT/pages/connect-advanced.adoc +++ b/javascript-manual/modules/ROOT/pages/connect-advanced.adoc @@ -65,6 +65,61 @@ const driver = neo4j.driver( If authentication is disabled on the server, the authentication parameter can be omitted entirely. +[role=label--new-5.14] +== Rotating authentication tokens + +It is possible to rotate authentication tokens that are expected to expire (e.g. SSO). +You need to provide a link:https://neo4j.com/docs/api/javascript-driver/current/class/lib6/auth-token-manager.js~AuthTokenManager.html[`AuthTokenManager`] instance when instantiating the `Driver`, rather than a static authentication token. + +The easiest way to get started is to use link:https://neo4j.com/docs/api/javascript-driver/current/class/lib6/auth-token-manager.js~AuthTokenManagers.html[one of built-in `AuthTokenManager` implementations]. + +.Rotating a bearer token expiring every 60 seconds +[source, javascript, test-skip] +---- +import neo4j, { AuthToken } from 'neo4j-driver' + +/** + * Method called whenever the driver needs to refresh the token. + * + * The refresh will happen if the driver is notified by the server + * of token expiration, or if `Date.now() > tokenData.expiry`. + * + * The driver will block creation of all connections until + * this function resolves the new auth token. + */ +async function generateAuthToken () { + const bearer = await getSSOToken() // some way to get a token + const token = neo4j.auth.bearer(bearer) + + // assume we know tokens expire every 60 seconds + const expiresIn = 60 + // Include a little buffer so that new token is fetched before the old one expires + const expiration = expiresIn - 10 + + return { + token, + // if expiration is not provided, + // the driver will only fetch a new token when an auth failure happens + expiration + } +} + +const driver = neo4j.driver( + URI, + neo4j.authTokenManagers.bearer({ + tokenProvider: generateAuthToken + }) +) +---- + +[NOTE] +This API must not be used for switching users. Auth managers must always return tokens for the same identity. +You can switch users at both xref:query-simple.adoc#impersonation[query level] and xref:transactions.adoc#impersonation[session level]. + +[WARNING] +`AuthManagers` (including provider functions passed to `expirationBasedAuthTokenManager()`) must not interact with the driver in any way, as this can cause deadlocks and undefined behavior. + + == Custom address resolver When creating a `Driver` object, you can specify a _resolver_ function to resolve the connection address the driver is initialized with. diff --git a/python-manual/modules/ROOT/pages/connect-advanced.adoc b/python-manual/modules/ROOT/pages/connect-advanced.adoc index bd88b5a0..ea052ecb 100644 --- a/python-manual/modules/ROOT/pages/connect-advanced.adoc +++ b/python-manual/modules/ROOT/pages/connect-advanced.adoc @@ -63,6 +63,50 @@ Use the function link:{neo4j-docs-base-uri}/api/python-driver/current/api.html#n If authentication is disabled on the server, the authentication parameter can be omitted entirely. +[role=label--new-5.14] +== Rotating authentication tokens + +It is possible to rotate authentication tokens that are expected to expire (e.g. SSO). +You need to provide a link:https://neo4j.com/docs/api/python-driver/current/api.html#neo4j.auth_management.AuthManager[`AuthManager`] instance (or link:https://neo4j.com/docs/api/python-driver/current/async_api.html#neo4j.auth_management.AsyncAuthManager[`AsyncAuthManager`] for the async driver) when instantiating the `Driver`, rather than a static authentication token. + +The easiest way to get started is to use link:https://neo4j.com/docs/api/python-driver/current/api.html#neo4j.auth_management.AuthManagers[one of built-in `AuthManager` implementations]. + +.Rotating a bearer token expiring every 60 seconds +[source, python, test-skip] +---- +import neo4j +from neo4j.auth_management import ( + AuthManagers, + ExpiringAuth, +) + + +def auth_provider(): + # Some way to get a token + sso_token = get_sso_token() + # Assume we know tokens expire every 60 seconds + expires_in = 60 + # Include a little buffer so that new token is fetched before the old one expires + expires_in -= 10 + + auth = neo4j.bearer_auth(sso_token) + return ExpiringAuth(auth=auth).expires_in(expires_in) + +with neo4j.GraphDatabase.driver( + URI, + auth=AuthManagers.expiration_based(auth_provider) +) as driver: + ... +---- + +[NOTE] +This API must not be used for switching users. Auth managers must always return tokens for the same identity. +You can switch users at both xref:query-simple.adoc#impersonation[query level] and xref:transactions.adoc#impersonation[session level]. + +[WARNING] +`AuthManagers` (including provider functions passed to `AuthManagers.expiration_based()`) must not interact with the driver in any way, as this can cause deadlocks and undefined behavior. + + == Custom address resolver When creating a `Driver` object, you can specify a _resolver_ function to resolve any addresses the driver receives ahead of DNS resolution.