Skip to content

Commit

Permalink
Rotating authentication tokens (#444)
Browse files Browse the repository at this point in the history
  • Loading branch information
stefano-ottolenghi committed Nov 21, 2024
1 parent 8f971f2 commit 13d8c25
Show file tree
Hide file tree
Showing 4 changed files with 182 additions and 0 deletions.
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].

[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

0 comments on commit 13d8c25

Please sign in to comment.