From 0659ff9563e06eefe8eb683c2280cd457d1c632e Mon Sep 17 00:00:00 2001 From: Andy Stark Date: Fri, 14 Feb 2025 10:54:09 +0000 Subject: [PATCH 1/3] DOC-4819 added Lettuce AMR example --- content/develop/clients/lettuce/amr.md | 171 +++++++++++++++++++++++++ 1 file changed, 171 insertions(+) create mode 100644 content/develop/clients/lettuce/amr.md diff --git a/content/develop/clients/lettuce/amr.md b/content/develop/clients/lettuce/amr.md new file mode 100644 index 000000000..7b6dd884b --- /dev/null +++ b/content/develop/clients/lettuce/amr.md @@ -0,0 +1,171 @@ +--- +categories: +- docs +- develop +- stack +- oss +- rs +- rc +- oss +- kubernetes +- clients +description: Learn how to authenticate to an Azure Managed Redis (AMR) database +linkTitle: Connect to AMR +title: Connect to Azure Managed Redis +weight: 2 +--- + +The [`redis-authx-entraid`](https://github.com/redis/jvm-redis-authx-entraid) package +lets you authenticate your app to +[Azure Managed Redis (AMR)](https://azure.microsoft.com/en-us/products/managed-redis) +using [Microsoft Entra ID](https://learn.microsoft.com/en-us/entra/identity/). +You can authenticate using a system-assigned or user-assigned +[managed identity](https://learn.microsoft.com/en-us/entra/identity/managed-identities-azure-resources/overview) +or a [service principal](https://learn.microsoft.com/en-us/entra/identity-platform/app-objects-and-service-principals), +letting `redis-authx-entraid` fetch and renew the authentication tokens for you automatically. + +## Install + +Install [`lettuce`]({{< relref "/develop/clients/lettuce" >}}) first, +if you have not already done so. + +If you are using Maven, add +the following dependency to your `pom.xml` file: + +```xml + + redis.clients.authentication + redis-authx-entraid + 0.1.1-beta1 + +``` + +If you are using Gradle, add the following dependency to your +`build.gradle` file: + +```bash +implementation 'redis.clients.authentication:redis-authx-entraid:0.1.1-beta1' +``` + +## Create a `TokenAuthConfig` instance + +The `TokenAuthConfig` class contains the authentication details that you +must supply when you connect to Redis. Chain the methods of the +`EntraIDTokenAuthConfigBuilder` class together (starting with the `builder()` +method) to include the details you need, as shown in the following example: + +```java +TokenAuthConfig authConfig = EntraIDTokenAuthConfigBuilder.builder() + .secret("") + .authority("") + // Other options... + .build(); +``` + +Some of the details you can supply are common to different use cases: + +- `secret()`: A string containing the [authentication secret](https://learn.microsoft.com/en-us/purview/sit-defn-azure-ad-client-secret). +- `authority()`: A string containing the [authority](https://learn.microsoft.com/en-us/entra/identity-platform/msal-client-application-configuration#authority) + URL. +- `scopes()`: A set of strings defining the [scopes](https://learn.microsoft.com/en-us/entra/identity-platform/scopes-oidc) + you want to apply. + +You can also add configuration to authenticate with a [service principal](#serv-principal) +or a [managed identity](#mgd-identity) as described in the sections below. + +### Configuration for a service principal {#serv-principal} + +Add `clientId()` to the `EntraIDTokenAuthConfigBuilder` chain to specify +authentication via a service principal, passing the ID token string as +a parameter. (See the +[Microsoft EntraID docs](https://learn.microsoft.com/en-us/entra/identity-platform/app-objects-and-service-principals) +for more information about service principals.) + +```java +TokenAuthConfig authConfig = EntraIDTokenAuthConfigBuilder.builder() + .clientId("") + // ... + .build(); +``` + +### Configuration for a managed identity {#mgd-identity} + +You can also authenticate to AMR using a managed identity (see the +[Microsoft documentation](https://learn.microsoft.com/en-us/entra/identity/managed-identities-azure-resources/overview) to learn more about managed identities). + +For a system assigned managed identity, simply add the `systemAssignedManagedIdentity()` +method to the `EntraIDTokenAuthConfigBuilder` chain: + +```java +TokenAuthConfig authConfig = EntraIDTokenAuthConfigBuilder.builder() + .systemAssignedManagedIdentity() + // ... + .build(); +``` + +For a user assigned managed identity, add `userAssignedManagedIdentity()`. This +requires a member of the `UserManagedIdentityType` enum (to select a +`CLIENT_ID`, `OBJECT_ID`, or `RESOURCE_ID`) as well as the `id` string itself: + +```java +TokenAuthConfig authConfig = EntraIDTokenAuthConfigBuilder.builder() + .userAssignedManagedIdentity( + UserManagedIdentityType.CLIENT_ID, + "" + ) + // ... + .build(); + +``` + +## Connect using the `withAuthentication()` option + +When you have created your `TokenAuthConfig` instance, you are ready to +connect to AMR. +The example below shows how to include the `TokenAuthConfig` details in a +`TokenBasedRedisCredentialsProvider` instance and pass it to the `RedisURI.Builder` +using the `withAuthentication()` option. + +{{< note >}} Azure requires you to use +[Transport Layer Security (TLS)](https://en.wikipedia.org/wiki/Transport_Layer_Security) +when you connect, as shown in the example. +{{< /note >}} + +```java +TokenAuthConfig authConfig = EntraIDTokenAuthConfigBuilder.builder() + // Chain of options... + .build(); + +TokenBasedRedisCredentialsProvider credentialsProvider = + TokenBasedRedisCredentialsProvider.create(tokenAuthConfig); + +RedisURI uri = RedisURI.Builder.redis("", ) + .withAuthentication(credentialsProvider) + .withSsl(true) + .build(); + +RedisClient client = RedisClient.create(uri); + +SslOptions sslOptions = SslOptions.builder().jdkSslProvider() + .truststore(new File( + ""), + "" + ) + .build(); + +client.setOptions(ClientOptions.builder() + .sslOptions(sslOptions) + .build()); + +StatefulRedisConnection connection = client.connect(); +RedisAsyncCommands asyncCommands = connection.async(); + +// Test the connection. +CompletableFuture testDBSize = asyncCommands.dbsize() + .thenAccept(r -> { + System.out.println(String.format("Database size: %d", r)); + }) + .toCompletableFuture(); + +testDBSize.join(); +``` From 5adcc32ff9c9dbdc795b558f37875299df3b3a40 Mon Sep 17 00:00:00 2001 From: andy-stark-redis <164213578+andy-stark-redis@users.noreply.github.com> Date: Fri, 14 Feb 2025 14:48:18 +0000 Subject: [PATCH 2/3] Update content/develop/clients/lettuce/amr.md Co-authored-by: David Dougherty --- content/develop/clients/lettuce/amr.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/develop/clients/lettuce/amr.md b/content/develop/clients/lettuce/amr.md index 7b6dd884b..3adc2e1dd 100644 --- a/content/develop/clients/lettuce/amr.md +++ b/content/develop/clients/lettuce/amr.md @@ -26,7 +26,7 @@ letting `redis-authx-entraid` fetch and renew the authentication tokens for you ## Install -Install [`lettuce`]({{< relref "/develop/clients/lettuce" >}}) first, +Install [`Lettuce`]({{< relref "/develop/clients/lettuce" >}}) first, if you have not already done so. If you are using Maven, add From 241fc32c6f2b16d6889256aeffa412d62959cc39 Mon Sep 17 00:00:00 2001 From: Andy Stark Date: Wed, 19 Feb 2025 15:40:55 +0000 Subject: [PATCH 3/3] DOC-4819 implemented feedback --- content/develop/clients/lettuce/amr.md | 176 ++++++++++++++++--------- 1 file changed, 112 insertions(+), 64 deletions(-) diff --git a/content/develop/clients/lettuce/amr.md b/content/develop/clients/lettuce/amr.md index 3adc2e1dd..b7a1447ca 100644 --- a/content/develop/clients/lettuce/amr.md +++ b/content/develop/clients/lettuce/amr.md @@ -24,6 +24,10 @@ You can authenticate using a system-assigned or user-assigned or a [service principal](https://learn.microsoft.com/en-us/entra/identity-platform/app-objects-and-service-principals), letting `redis-authx-entraid` fetch and renew the authentication tokens for you automatically. +See +[Use Microsoft Entra for cache authentication](https://learn.microsoft.com/en-us/azure/azure-cache-for-redis/cache-azure-active-directory-for-authentication) +in the Microsoft docs to learn how to configure Azure to use Entra ID authentication. + ## Install Install [`Lettuce`]({{< relref "/develop/clients/lettuce" >}}) first, @@ -47,19 +51,24 @@ If you are using Gradle, add the following dependency to your implementation 'redis.clients.authentication:redis-authx-entraid:0.1.1-beta1' ``` -## Create a `TokenAuthConfig` instance +## Create a `TokenBasedRedisCredentialsProvider` instance -The `TokenAuthConfig` class contains the authentication details that you +The `TokenBasedRedisCredentialsProvider` class contains the authentication details that you must supply when you connect to Redis. Chain the methods of the `EntraIDTokenAuthConfigBuilder` class together (starting with the `builder()` method) to include the details you need, as shown in the following example: ```java -TokenAuthConfig authConfig = EntraIDTokenAuthConfigBuilder.builder() - .secret("") - .authority("") - // Other options... - .build(); +TokenBasedRedisCredentialsProvider credentials; + +try ( EntraIDTokenAuthConfigBuilder builder = EntraIDTokenAuthConfigBuilder.builder()) { + builder.clientId(CLIENT_ID) + .secret(CLIENT_SECRET) + .authority(AUTHORITY) // "https://login.microsoftonline.com/{YOUR_TENANT_ID}"; + .scopes(SCOPES); // "https://redis.azure.com/.default"; + + credentials = TokenBasedRedisCredentialsProvider.create(builder.build()); +} ``` Some of the details you can supply are common to different use cases: @@ -68,11 +77,23 @@ Some of the details you can supply are common to different use cases: - `authority()`: A string containing the [authority](https://learn.microsoft.com/en-us/entra/identity-platform/msal-client-application-configuration#authority) URL. - `scopes()`: A set of strings defining the [scopes](https://learn.microsoft.com/en-us/entra/identity-platform/scopes-oidc) - you want to apply. + you want to apply. Configure your client application to acquire a Microsoft Entra token for scope, `https://redis.azure.com/.default` or `acca5fbb-b7e4-4009-81f1-37e38fd66d78/.default` + with the + [Microsoft Authentication Library (MSAL)](https://learn.microsoft.com/en-us/entra/identity-platform/msal-overview) You can also add configuration to authenticate with a [service principal](#serv-principal) or a [managed identity](#mgd-identity) as described in the sections below. +When you have created your `TokenBasedRedisCredentialsProvider` instance, you may want to +test it by obtaining a token, as shown in the folowing example: + +```java +// Test that the Entra ID credentials provider can resolve credentials. +credentials.resolveCredentials() + .doOnNext(c-> System.out.println(c.getUsername())) + .block(); +``` + ### Configuration for a service principal {#serv-principal} Add `clientId()` to the `EntraIDTokenAuthConfigBuilder` chain to specify @@ -82,10 +103,16 @@ a parameter. (See the for more information about service principals.) ```java -TokenAuthConfig authConfig = EntraIDTokenAuthConfigBuilder.builder() - .clientId("") - // ... - .build(); +TokenBasedRedisCredentialsProvider credentials; + +try ( EntraIDTokenAuthConfigBuilder builder = EntraIDTokenAuthConfigBuilder.builder()) { + builder.clientId(CLIENT_ID) + .secret(CLIENT_SECRET) + .authority(AUTHORITY) // "https://login.microsoftonline.com/{YOUR_TENANT_ID}"; + .scopes(SCOPES); // "https://redis.azure.com/.default"; + + credentials = TokenBasedRedisCredentialsProvider.create(builder.build()); +} ``` ### Configuration for a managed identity {#mgd-identity} @@ -97,10 +124,15 @@ For a system assigned managed identity, simply add the `systemAssignedManagedIde method to the `EntraIDTokenAuthConfigBuilder` chain: ```java -TokenAuthConfig authConfig = EntraIDTokenAuthConfigBuilder.builder() - .systemAssignedManagedIdentity() - // ... - .build(); +TokenBasedRedisCredentialsProvider credentials; + +try ( EntraIDTokenAuthConfigBuilder builder = EntraIDTokenAuthConfigBuilder.builder()) { + builder.clientId(CLIENT_ID) + // ... + .systemAssignedManagedIdentity(); + + credentials = TokenBasedRedisCredentialsProvider.create(builder.build()); +} ``` For a user assigned managed identity, add `userAssignedManagedIdentity()`. This @@ -108,64 +140,80 @@ requires a member of the `UserManagedIdentityType` enum (to select a `CLIENT_ID`, `OBJECT_ID`, or `RESOURCE_ID`) as well as the `id` string itself: ```java -TokenAuthConfig authConfig = EntraIDTokenAuthConfigBuilder.builder() - .userAssignedManagedIdentity( - UserManagedIdentityType.CLIENT_ID, - "" - ) - // ... - .build(); - +TokenBasedRedisCredentialsProvider credentials; + +try ( EntraIDTokenAuthConfigBuilder builder = EntraIDTokenAuthConfigBuilder.builder()) { + builder.clientId(CLIENT_ID) + // ... + .userAssignedManagedIdentity( + UserManagedIdentityType.CLIENT_ID, + "" + ); + + credentials = TokenBasedRedisCredentialsProvider.create(builder.build()); +} ``` ## Connect using the `withAuthentication()` option -When you have created your `TokenAuthConfig` instance, you are ready to +When you have created your `TokenBasedRedisCredentialsProvider` instance, you are ready to connect to AMR. -The example below shows how to include the `TokenAuthConfig` details in a +The example below shows how to include the authentication details in a `TokenBasedRedisCredentialsProvider` instance and pass it to the `RedisURI.Builder` -using the `withAuthentication()` option. - -{{< note >}} Azure requires you to use -[Transport Layer Security (TLS)](https://en.wikipedia.org/wiki/Transport_Layer_Security) -when you connect, as shown in the example. +using the `withAuthentication()` option. It also uses a `ClientOptions` object to +enable automatic re-authentication. + +The connection uses +[Transport Layer Security (TLS)](https://en.wikipedia.org/wiki/Transport_Layer_Security), +which is recommended and enabled by default for managed identities. See +[TLS connection]({{< relref "/develop/clients/lettuce/connect#tls-connection" >}}) for more information. + +{{< note >}} The `Lettuce` client library doesn't manage the lifecycle of +the `TokenBasedRedisCredentialsProvider` instance for you. You can reuse the +same instance for as many clients and connections as you want. When you have +finished using the credentials provider, call its `close()` method, as shown +at the end of the example. {{< /note >}} ```java -TokenAuthConfig authConfig = EntraIDTokenAuthConfigBuilder.builder() - // Chain of options... - .build(); - -TokenBasedRedisCredentialsProvider credentialsProvider = - TokenBasedRedisCredentialsProvider.create(tokenAuthConfig); - -RedisURI uri = RedisURI.Builder.redis("", ) - .withAuthentication(credentialsProvider) +// Entra ID credentials provider for Service Principal Identity with Client Secret. +TokenBasedRedisCredentialsProvider credentialsSP; +try (EntraIDTokenAuthConfigBuilder builder = EntraIDTokenAuthConfigBuilder.builder()) { + builder + .clientId(CLIENT_ID) + .secret(CLIENT_SECRET).authority(AUTHORITY) // "https://login.microsoftonline.com/{YOUR_TENANT_ID}" + .scopes(SCOPES); // "https://redis.azure.com/.default" + + credentialsSP = TokenBasedRedisCredentialsProvider.create(builder.build()); +} + +// Optionally test the credentials provider. +// credentialsSP.resolveCredentials().doOnNext(c -> System.out.println("SPI ID :" + c.getUsername())).block(); + +// Enable automatic re-authentication. +ClientOptions clientOptions = ClientOptions.builder() + .reauthenticateBehavior( + ClientOptions.ReauthenticateBehavior.ON_NEW_CREDENTIALS + ).build(); + +// Use the Entra ID credentials provider. +RedisURI redisURI = RedisURI.builder() + .withHost(HOST) + .withPort(PORT) + .withAuthentication(credentialsSP) .withSsl(true) .build(); -RedisClient client = RedisClient.create(uri); - -SslOptions sslOptions = SslOptions.builder().jdkSslProvider() - .truststore(new File( - ""), - "" - ) - .build(); - -client.setOptions(ClientOptions.builder() - .sslOptions(sslOptions) - .build()); - -StatefulRedisConnection connection = client.connect(); -RedisAsyncCommands asyncCommands = connection.async(); - -// Test the connection. -CompletableFuture testDBSize = asyncCommands.dbsize() - .thenAccept(r -> { - System.out.println(String.format("Database size: %d", r)); - }) - .toCompletableFuture(); - -testDBSize.join(); +// Create the RedisClient and set the re-authentication options. +RedisClient redisClient = RedisClient.create(redisURI); +redisClient.setOptions(clientOptions); + +// Connect with the credentials provider. +try (StatefulRedisConnection user1 = redisClient.connect(StringCodec.UTF8)) { + System.out.println("Connected to redis as :" + user1.sync().aclWhoami()); + System.out.println("Db size :" + user1.sync().dbsize()); +} finally { + redisClient.shutdown(); // Shutdown Redis client and close connections. + credentialsSP.close(); // Shutdown Entra ID Credentials provider. +} ```