Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rotating authentication tokens #444

Merged
merged 9 commits into from
Nov 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions go-manual/modules/ROOT/pages/connect-advanced.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -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].
stefano-ottolenghi marked this conversation as resolved.
Show resolved Hide resolved

[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.
Expand Down
46 changes: 46 additions & 0 deletions java-manual/modules/ROOT/pages/connect-advanced.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -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`.
Expand Down
55 changes: 55 additions & 0 deletions javascript-manual/modules/ROOT/pages/connect-advanced.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
44 changes: 44 additions & 0 deletions python-manual/modules/ROOT/pages/connect-advanced.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down