diff --git a/eng/jacoco-test-coverage/pom.xml b/eng/jacoco-test-coverage/pom.xml
index 90fa3592f9bf2..6275c8b0c39ff 100644
--- a/eng/jacoco-test-coverage/pom.xml
+++ b/eng/jacoco-test-coverage/pom.xml
@@ -134,7 +134,7 @@
com.azure
azure-identity
- 1.1.0-beta.7
+ 1.1.0-beta.8
com.azure
diff --git a/eng/versioning/version_client.txt b/eng/versioning/version_client.txt
index 9488f46f47144..fb38ca6aca0d0 100644
--- a/eng/versioning/version_client.txt
+++ b/eng/versioning/version_client.txt
@@ -27,7 +27,7 @@ com.azure:azure-data-schemaregistry;1.0.0-beta.2;1.0.0-beta.3
com.azure:azure-data-schemaregistry-avro;1.0.0-beta.2;1.0.0-beta.3
com.azure:azure-data-tables;1.0.0-beta.1;1.0.0-beta.1
com.azure:azure-e2e;1.0.0-beta.1;1.0.0-beta.1
-com.azure:azure-identity;1.0.9;1.1.0-beta.7
+com.azure:azure-identity;1.0.9;1.1.0-beta.8
com.azure:azure-identity-perf;1.0.0-beta.1;1.0.0-beta.1
com.azure:azure-messaging-eventhubs;5.1.2;5.2.0-beta.1
com.azure:azure-messaging-eventhubs-checkpointstore-blob;1.1.2;1.2.0-beta.2
@@ -75,7 +75,7 @@ com.microsoft.azure:spring-data-cosmosdb;3.0.0-beta.1;3.0.0-beta.1
# Format;
# unreleased_:;dependency-version
# note: The unreleased dependencies will not be manipulated with the automatic PR creation code.
-
+unreleased_com.azure:azure-core;1.7.0-beta.3
unreleased_com.azure:azure-core-test;1.4.0-beta.1
unreleased_com.azure:azure-messaging-servicebus;7.0.0-beta.4
unreleased_com.azure:azure-security-keyvault-keys;4.2.0-beta.6
diff --git a/sdk/core/azure-core-amqp/src/test/java/com/azure/core/amqp/implementation/ClaimsBasedSecurityChannelTest.java b/sdk/core/azure-core-amqp/src/test/java/com/azure/core/amqp/implementation/ClaimsBasedSecurityChannelTest.java
index 5a847b48b0630..685de40052424 100644
--- a/sdk/core/azure-core-amqp/src/test/java/com/azure/core/amqp/implementation/ClaimsBasedSecurityChannelTest.java
+++ b/sdk/core/azure-core-amqp/src/test/java/com/azure/core/amqp/implementation/ClaimsBasedSecurityChannelTest.java
@@ -107,8 +107,7 @@ public void teardown() {
@Test
public void authorizesSasToken() {
// Arrange
- // Subtracting two minutes because the AccessToken does this internally.
- final Date expectedDate = Date.from(validUntil.minusMinutes(2).toInstant());
+ final Date expectedDate = Date.from(validUntil.toInstant());
final ClaimsBasedSecurityChannel cbsChannel = new ClaimsBasedSecurityChannel(Mono.just(requestResponseChannel),
tokenCredential, CbsAuthorizationType.SHARED_ACCESS_SIGNATURE, options);
diff --git a/sdk/core/azure-core/src/main/java/com/azure/core/credential/AccessToken.java b/sdk/core/azure-core/src/main/java/com/azure/core/credential/AccessToken.java
index b8673a9fc960a..b12996a55f37b 100644
--- a/sdk/core/azure-core/src/main/java/com/azure/core/credential/AccessToken.java
+++ b/sdk/core/azure-core/src/main/java/com/azure/core/credential/AccessToken.java
@@ -19,7 +19,7 @@ public class AccessToken {
*/
public AccessToken(String token, OffsetDateTime expiresAt) {
this.token = token;
- this.expiresAt = expiresAt.minusMinutes(2); // 2 minutes before token expires
+ this.expiresAt = expiresAt;
}
/**
diff --git a/sdk/core/azure-core/src/main/java/com/azure/core/credential/SimpleTokenCache.java b/sdk/core/azure-core/src/main/java/com/azure/core/credential/SimpleTokenCache.java
index b80416ca99528..cfba9ace91bc6 100644
--- a/sdk/core/azure-core/src/main/java/com/azure/core/credential/SimpleTokenCache.java
+++ b/sdk/core/azure-core/src/main/java/com/azure/core/credential/SimpleTokenCache.java
@@ -3,25 +3,30 @@
package com.azure.core.credential;
-import reactor.core.publisher.FluxSink;
-import reactor.core.publisher.FluxSink.OverflowStrategy;
+import com.azure.core.util.logging.ClientLogger;
import reactor.core.publisher.Mono;
-import reactor.core.publisher.ReplayProcessor;
+import reactor.core.publisher.MonoProcessor;
-import java.util.concurrent.atomic.AtomicBoolean;
+import java.time.Duration;
+import java.time.OffsetDateTime;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Predicate;
import java.util.function.Supplier;
/**
* A token cache that supports caching a token and refreshing it.
*/
public class SimpleTokenCache {
- private static final int REFRESH_TIMEOUT_SECONDS = 30;
-
- private final AtomicBoolean wip;
- private AccessToken cache;
- private final ReplayProcessor emitterProcessor = ReplayProcessor.create(1);
- private final FluxSink sink = emitterProcessor.sink(OverflowStrategy.BUFFER);
+ // The delay after a refresh to attempt another token refresh
+ private static final Duration REFRESH_DELAY = Duration.ofSeconds(30);
+ // the offset before token expiry to attempt proactive token refresh
+ private static final Duration REFRESH_OFFSET = Duration.ofMinutes(5);
+ private final AtomicReference> wip;
+ private volatile AccessToken cache;
+ private volatile OffsetDateTime nextTokenRefresh = OffsetDateTime.now();
private final Supplier> tokenSupplier;
+ private final Predicate shouldRefresh;
+ private final ClientLogger logger = new ClientLogger(SimpleTokenCache.class);
/**
* Creates an instance of RefreshableTokenCredential with default scheme "Bearer".
@@ -29,8 +34,10 @@ public class SimpleTokenCache {
* @param tokenSupplier a method to get a new token
*/
public SimpleTokenCache(Supplier> tokenSupplier) {
- this.wip = new AtomicBoolean(false);
+ this.wip = new AtomicReference<>();
this.tokenSupplier = tokenSupplier;
+ this.shouldRefresh = accessToken -> OffsetDateTime.now()
+ .isAfter(accessToken.getExpiresAt().minus(REFRESH_OFFSET));
}
/**
@@ -38,18 +45,97 @@ public SimpleTokenCache(Supplier> tokenSupplier) {
* @return a Publisher that emits an AccessToken
*/
public Mono getToken() {
- if (cache != null && !cache.isExpired()) {
- return Mono.just(cache);
- }
return Mono.defer(() -> {
- if (!wip.getAndSet(true)) {
- return tokenSupplier.get().doOnNext(ac -> cache = ac)
- .doOnNext(sink::next)
- .doOnError(sink::error)
- .doOnTerminate(() -> wip.set(false));
- } else {
- return emitterProcessor.next();
+ try {
+ if (wip.compareAndSet(null, MonoProcessor.create())) {
+ final MonoProcessor monoProcessor = wip.get();
+ OffsetDateTime now = OffsetDateTime.now();
+ Mono tokenRefresh;
+ Mono fallback;
+ if (cache != null && !shouldRefresh.test(cache)) {
+ // fresh cache & no need to refresh
+ tokenRefresh = Mono.empty();
+ fallback = Mono.just(cache);
+ } else if (cache == null || cache.isExpired()) {
+ // no token to use
+ if (now.isAfter(nextTokenRefresh)) {
+ // refresh immediately
+ tokenRefresh = Mono.defer(tokenSupplier);
+ } else {
+ // wait for timeout, then refresh
+ tokenRefresh = Mono.defer(tokenSupplier)
+ .delaySubscription(Duration.between(now, nextTokenRefresh));
+ }
+ // cache doesn't exist or expired, no fallback
+ fallback = Mono.empty();
+ } else {
+ // token available, but close to expiry
+ if (now.isAfter(nextTokenRefresh)) {
+ // refresh immediately
+ tokenRefresh = Mono.defer(tokenSupplier);
+ } else {
+ // still in timeout, do not refresh
+ tokenRefresh = Mono.empty();
+ }
+ // cache hasn't expired, ignore refresh error this time
+ fallback = Mono.just(cache);
+ }
+ return tokenRefresh
+ .materialize()
+ .flatMap(signal -> {
+ AccessToken accessToken = signal.get();
+ Throwable error = signal.getThrowable();
+ if (signal.isOnNext() && accessToken != null) { // SUCCESS
+ logger.info(refreshLog(cache, now, "Acquired a new access token"));
+ cache = accessToken;
+ monoProcessor.onNext(accessToken);
+ monoProcessor.onComplete();
+ nextTokenRefresh = OffsetDateTime.now().plus(REFRESH_DELAY);
+ return Mono.just(accessToken);
+ } else if (signal.isOnError() && error != null) { // ERROR
+ logger.error(refreshLog(cache, now, "Failed to acquire a new access token"));
+ nextTokenRefresh = OffsetDateTime.now().plus(REFRESH_DELAY);
+ return fallback.switchIfEmpty(Mono.error(error));
+ } else { // NO REFRESH
+ monoProcessor.onComplete();
+ return fallback;
+ }
+ })
+ .doOnError(monoProcessor::onError)
+ .doOnTerminate(() -> wip.set(null));
+ } else if (cache != null && !cache.isExpired()) {
+ // another thread might be refreshing the token proactively, but the current token is still valid
+ return Mono.just(cache);
+ } else {
+ // another thread is definitely refreshing the expired token
+ MonoProcessor monoProcessor = wip.get();
+ if (monoProcessor == null) {
+ // the refreshing thread has finished
+ return Mono.just(cache);
+ } else {
+ // wait for refreshing thread to finish but defer to updated cache in case just missed onNext()
+ return monoProcessor.switchIfEmpty(Mono.defer(() -> Mono.just(cache)));
+ }
+ }
+ } catch (Throwable t) {
+ return Mono.error(t);
}
});
}
+
+ private String refreshLog(AccessToken cache, OffsetDateTime now, String log) {
+ StringBuilder info = new StringBuilder(log);
+ if (cache == null) {
+ info.append(".");
+ } else {
+ Duration tte = Duration.between(now, cache.getExpiresAt());
+ info.append(" at ").append(tte.abs().getSeconds()).append(" seconds ")
+ .append(tte.isNegative() ? "after" : "before").append(" expiry. ")
+ .append("Retry may be attempted after ").append(REFRESH_DELAY.getSeconds()).append(" seconds.");
+ if (!tte.isNegative()) {
+ info.append(" The token currently cached will be used.");
+ }
+ }
+ return info.toString();
+ }
}
diff --git a/sdk/e2e/pom.xml b/sdk/e2e/pom.xml
index 89c6e4da0620f..cf032813d2285 100644
--- a/sdk/e2e/pom.xml
+++ b/sdk/e2e/pom.xml
@@ -33,7 +33,7 @@
com.azure
azure-identity
- 1.1.0-beta.7
+ 1.1.0-beta.8
com.azure
diff --git a/sdk/identity/azure-identity-perf/pom.xml b/sdk/identity/azure-identity-perf/pom.xml
index 56d2e7379410a..61af9d2214cb7 100644
--- a/sdk/identity/azure-identity-perf/pom.xml
+++ b/sdk/identity/azure-identity-perf/pom.xml
@@ -27,7 +27,7 @@
com.azure
azure-identity
- 1.1.0-beta.7
+ 1.1.0-beta.8
com.azure
diff --git a/sdk/identity/azure-identity-perf/src/main/java/com/azure/identity/perf/WriteCache.java b/sdk/identity/azure-identity-perf/src/main/java/com/azure/identity/perf/WriteCache.java
index edee2684493f2..3c7351218aae7 100644
--- a/sdk/identity/azure-identity-perf/src/main/java/com/azure/identity/perf/WriteCache.java
+++ b/sdk/identity/azure-identity-perf/src/main/java/com/azure/identity/perf/WriteCache.java
@@ -9,8 +9,6 @@
import com.azure.perf.test.core.PerfStressOptions;
import reactor.core.publisher.Mono;
-import java.time.Duration;
-
public class WriteCache extends ServiceTest {
private final SharedTokenCacheCredential credential;
@@ -18,7 +16,6 @@ public WriteCache(PerfStressOptions options) {
super(options);
credential = new SharedTokenCacheCredentialBuilder()
.clientId(CLI_CLIENT_ID)
- .tokenRefreshOffset(Duration.ofMinutes(60))
.build();
}
diff --git a/sdk/identity/azure-identity/CHANGELOG.md b/sdk/identity/azure-identity/CHANGELOG.md
index 660f6942e3de4..7ea05a1bef72f 100644
--- a/sdk/identity/azure-identity/CHANGELOG.md
+++ b/sdk/identity/azure-identity/CHANGELOG.md
@@ -1,8 +1,23 @@
# Release History
-## 1.1.0-beta.7 (Unreleased)
-- Added support for web apps (confidential apps) for `InteractiveBrowserCredential` and `AuthorizationCodeCredential`. A client secret is required on the builder for web apps.
+## 1.1.0-beta.8 (Unreleased)
+
+
+## 1.1.0-beta.7 (2020-07-23)
+
+### Features
+- Added support for web apps (confidential apps) for `AuthorizationCodeCredential`. A client secret is required on the builder for web apps.
- Added support for user assigned managed identities for `DefaultAzureCredential` with `.managedIdentityClientId()`.
+- Added`AzureAuthorityHosts` to access well knwon authority hosts.
+- Added `getClientId()` method in `AuthenticationRecord`
+
+### Breaking Changes
+- Removed persistent caching support from `AuthorizationCodeCredential`.
+- Removed `KnownAuthorityHosts`
+- Removed `getCredentials()` method in `ChainedTokenCredential` & `DefaultAzureCredential`
+- Changed return type of `serialize` method in `AuthenticationRecord` to `Mono`.
+- Changed method signatures`enablePersistentCache(boolean)` and `allowUnencryptedCache(boolean)` on credential builders to `enablePersistentCache()` and `allowUnencryptedCache()`
+
## 1.1.0-beta.6 (2020-07-10)
- Added `.getCredentials()` method to `DefaultAzureCredential` and `ChainedTokenCredential` and added option `.addAll(Collection extends TokenCredential>)` on `ChainedtokenCredentialBuilder`.
diff --git a/sdk/identity/azure-identity/pom.xml b/sdk/identity/azure-identity/pom.xml
index 0092b19261600..7cbded89bd719 100644
--- a/sdk/identity/azure-identity/pom.xml
+++ b/sdk/identity/azure-identity/pom.xml
@@ -6,7 +6,7 @@
com.azure
azure-identity
- 1.1.0-beta.7
+ 1.1.0-beta.8
Microsoft Azure client library for Identity
This module contains client library for Microsoft Azure Identity.
@@ -27,7 +27,7 @@
com.azure
azure-core
- 1.6.0
+ 1.7.0-beta.3
com.microsoft.azure
diff --git a/sdk/identity/azure-identity/src/main/java/com/azure/identity/CredentialBuilderBase.java b/sdk/identity/azure-identity/src/main/java/com/azure/identity/CredentialBuilderBase.java
index 13c76e29c1bed..73922c7a2fff4 100644
--- a/sdk/identity/azure-identity/src/main/java/com/azure/identity/CredentialBuilderBase.java
+++ b/sdk/identity/azure-identity/src/main/java/com/azure/identity/CredentialBuilderBase.java
@@ -89,21 +89,4 @@ public T httpClient(HttpClient client) {
this.identityClientOptions.setHttpClient(client);
return (T) this;
}
-
- /**
- * Sets how long before the actual token expiry to refresh the token. The
- * token will be considered expired at and after the time of (actual
- * expiry - token refresh offset). The default offset is 2 minutes.
- *
- * This is useful when network is congested and a request containing the
- * token takes longer than normal to get to the server.
- *
- * @param tokenRefreshOffset the duration before the actual expiry of a token to refresh it
- * @return An updated instance of this builder with the token refresh offset set as specified.
- */
- @SuppressWarnings("unchecked")
- public T tokenRefreshOffset(Duration tokenRefreshOffset) {
- this.identityClientOptions.setTokenRefreshOffset(tokenRefreshOffset);
- return (T) this;
- }
}
diff --git a/sdk/identity/azure-identity/src/main/java/com/azure/identity/DefaultAzureCredential.java b/sdk/identity/azure-identity/src/main/java/com/azure/identity/DefaultAzureCredential.java
index 1b35dad0dd533..5b9ee03d44d9f 100644
--- a/sdk/identity/azure-identity/src/main/java/com/azure/identity/DefaultAzureCredential.java
+++ b/sdk/identity/azure-identity/src/main/java/com/azure/identity/DefaultAzureCredential.java
@@ -22,7 +22,6 @@
*/
@Immutable
public final class DefaultAzureCredential extends ChainedTokenCredential {
-
/**
* Creates default DefaultAzureCredential instance to use. This will use AZURE_CLIENT_ID,
* AZURE_CLIENT_SECRET, and AZURE_TENANT_ID environment variables to create a
diff --git a/sdk/identity/azure-identity/src/main/java/com/azure/identity/EnvironmentCredential.java b/sdk/identity/azure-identity/src/main/java/com/azure/identity/EnvironmentCredential.java
index 6b7f3f6d93792..81f2caf6193a7 100644
--- a/sdk/identity/azure-identity/src/main/java/com/azure/identity/EnvironmentCredential.java
+++ b/sdk/identity/azure-identity/src/main/java/com/azure/identity/EnvironmentCredential.java
@@ -37,7 +37,6 @@
@Immutable
public class EnvironmentCredential implements TokenCredential {
private final Configuration configuration;
- private final IdentityClientOptions identityClientOptions;
private final ClientLogger logger = new ClientLogger(EnvironmentCredential.class);
private final TokenCredential tokenCredential;
@@ -48,7 +47,6 @@ public class EnvironmentCredential implements TokenCredential {
*/
EnvironmentCredential(IdentityClientOptions identityClientOptions) {
this.configuration = Configuration.getGlobalConfiguration().clone();
- this.identityClientOptions = identityClientOptions;
TokenCredential targetCredential = null;
String clientId = configuration.get(Configuration.PROPERTY_AZURE_CLIENT_ID);
diff --git a/sdk/identity/azure-identity/src/main/java/com/azure/identity/InteractiveBrowserCredential.java b/sdk/identity/azure-identity/src/main/java/com/azure/identity/InteractiveBrowserCredential.java
index 15bd1807f9df2..aad793b2f5c65 100644
--- a/sdk/identity/azure-identity/src/main/java/com/azure/identity/InteractiveBrowserCredential.java
+++ b/sdk/identity/azure-identity/src/main/java/com/azure/identity/InteractiveBrowserCredential.java
@@ -122,5 +122,4 @@ private AccessToken updateCache(MsalToken msalToken) {
identityClient.getTenantId(), identityClient.getClientId())));
return msalToken;
}
-
}
diff --git a/sdk/identity/azure-identity/src/main/java/com/azure/identity/implementation/IdentityClient.java b/sdk/identity/azure-identity/src/main/java/com/azure/identity/implementation/IdentityClient.java
index 6842568ac80ca..65d649be92fc8 100644
--- a/sdk/identity/azure-identity/src/main/java/com/azure/identity/implementation/IdentityClient.java
+++ b/sdk/identity/azure-identity/src/main/java/com/azure/identity/implementation/IdentityClient.java
@@ -92,6 +92,7 @@ public class IdentityClient {
private static final String LINUX_MAC_PROCESS_ERROR_MESSAGE = "(.*)az:(.*)not found";
private static final String DEFAULT_WINDOWS_SYSTEM_ROOT = System.getenv("SystemRoot");
private static final String DEFAULT_MAC_LINUX_PATH = "/bin/";
+ private static final Duration REFRESH_OFFSET = Duration.ofMinutes(5);
private final ClientLogger logger = new ClientLogger(IdentityClient.class);
private final IdentityClientOptions options;
@@ -265,8 +266,7 @@ public Mono authenticateWithIntelliJ(TokenRequestContext request) {
ConfidentialClientApplication application = applicationBuilder.build();
return Mono.fromFuture(application.acquireToken(
ClientCredentialParameters.builder(new HashSet<>(request.getScopes()))
- .build()))
- .map(ar -> new MsalToken(ar, options));
+ .build())).map(MsalToken::new);
} catch (MalformedURLException e) {
return Mono.error(e);
}
@@ -280,7 +280,7 @@ public Mono authenticateWithIntelliJ(TokenRequestContext request) {
.build();
return publicClientApplicationAccessor.getValue()
- .flatMap(pc -> Mono.fromFuture(pc.acquireToken(parameters)).map(ar -> new MsalToken(ar, options)));
+ .flatMap(pc -> Mono.fromFuture(pc.acquireToken(parameters)).map(MsalToken::new));
} else {
throw logger.logExceptionAsError(new CredentialUnavailableException(
@@ -381,7 +381,7 @@ public Mono authenticateWithAzureCli(TokenRequestContext request) {
OffsetDateTime expiresOn = LocalDateTime.parse(timeJoinedWithT, DateTimeFormatter.ISO_LOCAL_DATE_TIME)
.atZone(ZoneId.systemDefault())
.toOffsetDateTime().withOffsetSameInstant(ZoneOffset.UTC);
- token = new IdentityToken(accessToken, expiresOn, options);
+ token = new AccessToken(accessToken, expiresOn);
} catch (IOException | InterruptedException e) {
throw logger.logExceptionAsError(new IllegalStateException(e));
} catch (RuntimeException e) {
@@ -408,7 +408,7 @@ public Mono authenticateWithConfidentialClient(TokenRequestContext
return confidentialClientApplicationAccessor.getValue()
.flatMap(confidentialClient -> Mono.fromFuture(() -> confidentialClient.acquireToken(
ClientCredentialParameters.builder(new HashSet<>(request.getScopes())).build()))
- .map(ar -> new MsalToken(ar, options)));
+ .map(MsalToken::new));
}
private HttpPipeline setupPipeline(HttpClient httpClient) {
@@ -437,7 +437,7 @@ public Mono authenticateWithUsernamePassword(TokenRequestContext requ
new HashSet<>(request.getScopes()), username, password.toCharArray()).build()))
.onErrorMap(t -> new ClientAuthenticationException("Failed to acquire token with username and "
+ "password", null, t))
- .map(ar -> new MsalToken(ar, options)));
+ .map(MsalToken::new));
}
/**
@@ -449,31 +449,31 @@ public Mono authenticateWithUsernamePassword(TokenRequestContext requ
*/
public Mono authenticateWithPublicClientCache(TokenRequestContext request, IAccount account) {
return publicClientApplicationAccessor.getValue()
- .flatMap(pc -> Mono.fromFuture(() -> {
- SilentParameters.SilentParametersBuilder parametersBuilder = SilentParameters.builder(
- new HashSet<>(request.getScopes()));
+ .flatMap(pc -> Mono.fromFuture(() -> {
+ SilentParameters.SilentParametersBuilder parametersBuilder = SilentParameters.builder(
+ new HashSet<>(request.getScopes()));
+ if (account != null) {
+ parametersBuilder = parametersBuilder.account(account);
+ }
+ try {
+ return pc.acquireTokenSilently(parametersBuilder.build());
+ } catch (MalformedURLException e) {
+ return getFailedCompletableFuture(logger.logExceptionAsError(new RuntimeException(e)));
+ }
+ }).map(MsalToken::new)
+ .filter(t -> OffsetDateTime.now().isBefore(t.getExpiresAt().minus(REFRESH_OFFSET)))
+ .switchIfEmpty(Mono.fromFuture(() -> {
+ SilentParameters.SilentParametersBuilder forceParametersBuilder = SilentParameters.builder(
+ new HashSet<>(request.getScopes())).forceRefresh(true);
if (account != null) {
- parametersBuilder = parametersBuilder.account(account);
+ forceParametersBuilder = forceParametersBuilder.account(account);
}
try {
- return pc.acquireTokenSilently(parametersBuilder.build());
+ return pc.acquireTokenSilently(forceParametersBuilder.build());
} catch (MalformedURLException e) {
return getFailedCompletableFuture(logger.logExceptionAsError(new RuntimeException(e)));
}
- }).map(ar -> new MsalToken(ar, options))
- .filter(t -> !t.isExpired())
- .switchIfEmpty(Mono.fromFuture(() -> {
- SilentParameters.SilentParametersBuilder forceParametersBuilder = SilentParameters.builder(
- new HashSet<>(request.getScopes())).forceRefresh(true);
- if (account != null) {
- forceParametersBuilder = forceParametersBuilder.account(account);
- }
- try {
- return pc.acquireTokenSilently(forceParametersBuilder.build());
- } catch (MalformedURLException e) {
- return getFailedCompletableFuture(logger.logExceptionAsError(new RuntimeException(e)));
- }
- }).map(result -> new MsalToken(result, options))));
+ }).map(MsalToken::new)));
}
/**
@@ -484,16 +484,16 @@ public Mono authenticateWithPublicClientCache(TokenRequestContext req
*/
public Mono authenticateWithConfidentialClientCache(TokenRequestContext request) {
return confidentialClientApplicationAccessor.getValue()
- .flatMap(confidentialClient -> Mono.fromFuture(() -> {
- SilentParameters.SilentParametersBuilder parametersBuilder = SilentParameters.builder(
- new HashSet<>(request.getScopes()));
- try {
- return confidentialClient.acquireTokenSilently(parametersBuilder.build());
- } catch (MalformedURLException e) {
- return getFailedCompletableFuture(logger.logExceptionAsError(new RuntimeException(e)));
- }
- }).map(ar -> (AccessToken) new MsalToken(ar, options))
- .filter(t -> !t.isExpired()));
+ .flatMap(confidentialClient -> Mono.fromFuture(() -> {
+ SilentParameters.SilentParametersBuilder parametersBuilder = SilentParameters.builder(
+ new HashSet<>(request.getScopes()));
+ try {
+ return confidentialClient.acquireTokenSilently(parametersBuilder.build());
+ } catch (MalformedURLException e) {
+ return getFailedCompletableFuture(logger.logExceptionAsError(new RuntimeException(e)));
+ }
+ }).map(ar -> (AccessToken) new MsalToken(ar))
+ .filter(t -> OffsetDateTime.now().isBefore(t.getExpiresAt().minus(REFRESH_OFFSET))));
}
/**
@@ -516,7 +516,7 @@ public Mono authenticateWithDeviceCode(TokenRequestContext request,
OffsetDateTime.now().plusSeconds(dc.expiresIn()), dc.message()))).build();
return pc.acquireToken(parameters);
}).onErrorMap(t -> new ClientAuthenticationException("Failed to acquire token with device code", null, t))
- .map(ar -> new MsalToken(ar, options)));
+ .map(MsalToken::new));
}
/**
@@ -536,8 +536,7 @@ public Mono authenticateWithVsCodeCredential(TokenRequestContext requ
.build();
return publicClientApplicationAccessor.getValue()
- .flatMap(pc -> Mono.fromFuture(pc.acquireToken(parameters))
- .map(ar -> new MsalToken(ar, options)));
+ .flatMap(pc -> Mono.fromFuture(pc.acquireToken(parameters)).map(MsalToken::new));
}
/**
@@ -562,7 +561,7 @@ public Mono authenticateWithAuthorizationCode(TokenRequestContext req
.flatMap(pc -> Mono.fromFuture(() -> pc.acquireToken(parameters)));
}
return acquireToken.onErrorMap(t -> new ClientAuthenticationException(
- "Failed to acquire token with authorization code", null, t)).map(ar -> new MsalToken(ar, options));
+ "Failed to acquire token with authorization code", null, t)).map(MsalToken::new);
}
@@ -699,8 +698,7 @@ public Mono authenticateToManagedIdentityEndpoint(String msiEndpoin
.useDelimiter("\\A");
String result = s.hasNext() ? s.next() : "";
- MSIToken msiToken = SERIALIZER_ADAPTER.deserialize(result, MSIToken.class, SerializerEncoding.JSON);
- return new IdentityToken(msiToken.getToken(), msiToken.getExpiresAt(), options);
+ return SERIALIZER_ADAPTER.deserialize(result, MSIToken.class, SerializerEncoding.JSON);
} finally {
if (connection != null) {
connection.disconnect();
@@ -752,9 +750,7 @@ public Mono authenticateToIMDSEndpoint(TokenRequestContext request)
.useDelimiter("\\A");
String result = s.hasNext() ? s.next() : "";
- MSIToken msiToken = SERIALIZER_ADAPTER.deserialize(result,
- MSIToken.class, SerializerEncoding.JSON);
- return new IdentityToken(msiToken.getToken(), msiToken.getExpiresAt(), options);
+ return SERIALIZER_ADAPTER.deserialize(result, MSIToken.class, SerializerEncoding.JSON);
} catch (IOException exception) {
if (connection == null) {
throw logger.logExceptionAsError(new RuntimeException(
diff --git a/sdk/identity/azure-identity/src/main/java/com/azure/identity/implementation/IdentityClientOptions.java b/sdk/identity/azure-identity/src/main/java/com/azure/identity/implementation/IdentityClientOptions.java
index eccf9e9f11bfe..25462ac68d3c1 100644
--- a/sdk/identity/azure-identity/src/main/java/com/azure/identity/implementation/IdentityClientOptions.java
+++ b/sdk/identity/azure-identity/src/main/java/com/azure/identity/implementation/IdentityClientOptions.java
@@ -15,7 +15,6 @@
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.Duration;
-import java.util.Objects;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ForkJoinPool;
import java.util.function.Function;
@@ -46,7 +45,6 @@ public final class IdentityClientOptions {
private ProxyOptions proxyOptions;
private HttpPipeline httpPipeline;
private ExecutorService executorService;
- private Duration tokenRefreshOffset = Duration.ofMinutes(2);
private HttpClient httpClient;
private boolean allowUnencryptedCache;
private boolean sharedTokenCacheEnabled;
@@ -184,31 +182,6 @@ public ExecutorService getExecutorService() {
return executorService;
}
- /**
- * @return how long before the actual token expiry to refresh the token.
- */
- public Duration getTokenRefreshOffset() {
- return tokenRefreshOffset;
- }
-
- /**
- * Sets how long before the actual token expiry to refresh the token. The
- * token will be considered expired at and after the time of (actual
- * expiry - token refresh offset). The default offset is 2 minutes.
- *
- * This is useful when network is congested and a request containing the
- * token takes longer than normal to get to the server.
- *
- * @param tokenRefreshOffset the duration before the actual expiry of a token to refresh it
- * @return IdentityClientOptions
- * @throws NullPointerException If {@code tokenRefreshOffset} is null.
- */
- public IdentityClientOptions setTokenRefreshOffset(Duration tokenRefreshOffset) {
- Objects.requireNonNull(tokenRefreshOffset, "The token refresh offset cannot be null.");
- this.tokenRefreshOffset = tokenRefreshOffset;
- return this;
- }
-
/**
* Specifies the HttpClient to send use for requests.
* @param httpClient the http client to use for requests
diff --git a/sdk/identity/azure-identity/src/main/java/com/azure/identity/implementation/IdentityToken.java b/sdk/identity/azure-identity/src/main/java/com/azure/identity/implementation/IdentityToken.java
deleted file mode 100644
index c6973b3d99a8f..0000000000000
--- a/sdk/identity/azure-identity/src/main/java/com/azure/identity/implementation/IdentityToken.java
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright (c) Microsoft Corporation. All rights reserved.
-// Licensed under the MIT License.
-
-package com.azure.identity.implementation;
-
-import com.azure.core.credential.AccessToken;
-
-import java.time.OffsetDateTime;
-
-/**
- * Type representing authentication result from the azure-identity client.
- */
-public class IdentityToken extends AccessToken {
- /**
- * Creates an identity token instance.
- *
- * @param token the token string.
- * @param expiresAt the expiration time.
- * @param options the identity client options.
- */
- public IdentityToken(String token, OffsetDateTime expiresAt, IdentityClientOptions options) {
- super(token, expiresAt.plusMinutes(2).minus(options.getTokenRefreshOffset()));
- }
-}
diff --git a/sdk/identity/azure-identity/src/main/java/com/azure/identity/implementation/MsalToken.java b/sdk/identity/azure-identity/src/main/java/com/azure/identity/implementation/MsalToken.java
index e8eca5066bd1a..8299c42422d21 100644
--- a/sdk/identity/azure-identity/src/main/java/com/azure/identity/implementation/MsalToken.java
+++ b/sdk/identity/azure-identity/src/main/java/com/azure/identity/implementation/MsalToken.java
@@ -3,6 +3,7 @@
package com.azure.identity.implementation;
+import com.azure.core.credential.AccessToken;
import com.microsoft.aad.msal4j.IAccount;
import com.microsoft.aad.msal4j.IAuthenticationResult;
@@ -12,7 +13,7 @@
/**
* Type representing authentication result from the MSAL (Microsoft Authentication Library).
*/
-public final class MsalToken extends IdentityToken {
+public final class MsalToken extends AccessToken {
private IAuthenticationResult authenticationResult;
@@ -21,10 +22,9 @@ public final class MsalToken extends IdentityToken {
*
* @param msalResult the raw authentication result returned by MSAL
*/
- public MsalToken(IAuthenticationResult msalResult, IdentityClientOptions options) {
+ public MsalToken(IAuthenticationResult msalResult) {
super(msalResult.accessToken(),
- OffsetDateTime.ofInstant(msalResult.expiresOnDate().toInstant(), ZoneOffset.UTC),
- options);
+ OffsetDateTime.ofInstant(msalResult.expiresOnDate().toInstant(), ZoneOffset.UTC));
authenticationResult = msalResult;
}
diff --git a/sdk/identity/azure-identity/src/test/java/com/azure/identity/ClientSecretCredentialTest.java b/sdk/identity/azure-identity/src/test/java/com/azure/identity/ClientSecretCredentialTest.java
index ac446bc06ad8e..ba71ce4ce2bfc 100644
--- a/sdk/identity/azure-identity/src/test/java/com/azure/identity/ClientSecretCredentialTest.java
+++ b/sdk/identity/azure-identity/src/test/java/com/azure/identity/ClientSecretCredentialTest.java
@@ -17,7 +17,6 @@
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
-import java.time.Duration;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.util.UUID;
@@ -66,41 +65,6 @@ public void testValidSecrets() throws Exception {
.verifyComplete();
}
- @Test
- public void testValidSecretsWithTokenRefreshOffset() throws Exception {
- // setup
- String secret = "secret";
- String token1 = "token1";
- String token2 = "token2";
- TokenRequestContext request1 = new TokenRequestContext().addScopes("https://management.azure.com");
- TokenRequestContext request2 = new TokenRequestContext().addScopes("https://vault.azure.net");
- OffsetDateTime expiresAt = OffsetDateTime.now(ZoneOffset.UTC).plusHours(1);
- Duration offset = Duration.ofMinutes(10);
-
- // mock
- IdentityClient identityClient = PowerMockito.mock(IdentityClient.class);
- when(identityClient.authenticateWithConfidentialClientCache(any())).thenReturn(Mono.empty());
- when(identityClient.authenticateWithConfidentialClient(request1)).thenReturn(TestUtils.getMockAccessToken(token1, expiresAt, offset));
- when(identityClient.authenticateWithConfidentialClient(request2)).thenReturn(TestUtils.getMockAccessToken(token2, expiresAt, offset));
- PowerMockito.whenNew(IdentityClient.class).withAnyArguments().thenReturn(identityClient);
-
- // test
- ClientSecretCredential credential = new ClientSecretCredentialBuilder()
- .tenantId(tenantId)
- .clientId(clientId)
- .clientSecret(secret)
- .tokenRefreshOffset(offset)
- .build();
- StepVerifier.create(credential.getToken(request1))
- .expectNextMatches(accessToken -> token1.equals(accessToken.getToken())
- && expiresAt.getSecond() == accessToken.getExpiresAt().getSecond())
- .verifyComplete();
- StepVerifier.create(credential.getToken(request2))
- .expectNextMatches(accessToken -> token2.equals(accessToken.getToken())
- && expiresAt.getSecond() == accessToken.getExpiresAt().getSecond())
- .verifyComplete();
- }
-
@Test
public void testInvalidSecrets() throws Exception {
// setup
diff --git a/sdk/identity/azure-identity/src/test/java/com/azure/identity/ManagedIdentityCredentialTest.java b/sdk/identity/azure-identity/src/test/java/com/azure/identity/ManagedIdentityCredentialTest.java
index ebaf988af2221..92567a00f008a 100644
--- a/sdk/identity/azure-identity/src/test/java/com/azure/identity/ManagedIdentityCredentialTest.java
+++ b/sdk/identity/azure-identity/src/test/java/com/azure/identity/ManagedIdentityCredentialTest.java
@@ -16,7 +16,6 @@
import org.powermock.modules.junit4.PowerMockRunner;
import reactor.test.StepVerifier;
-import java.time.Duration;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.util.UUID;
@@ -88,64 +87,4 @@ public void testIMDS() throws Exception {
&& expiresOn.getSecond() == token.getExpiresAt().getSecond())
.verifyComplete();
}
-
- @Test
- public void testMSIEndpointWithTokenRefreshOffset() throws Exception {
- Configuration configuration = Configuration.getGlobalConfiguration();
-
- try {
- // setup
- String endpoint = "http://localhost";
- String secret = "secret";
- String token1 = "token1";
- TokenRequestContext request1 = new TokenRequestContext().addScopes("https://management.azure.com");
- OffsetDateTime expiresAt = OffsetDateTime.now(ZoneOffset.UTC).plusHours(1);
- configuration.put("MSI_ENDPOINT", endpoint);
- configuration.put("MSI_SECRET", secret);
- Duration offset = Duration.ofMinutes(10);
-
- // mock
- IdentityClient identityClient = PowerMockito.mock(IdentityClient.class);
- when(identityClient.authenticateToManagedIdentityEndpoint(endpoint, secret, request1)).thenReturn(TestUtils.getMockAccessToken(token1, expiresAt, offset));
- PowerMockito.whenNew(IdentityClient.class).withAnyArguments().thenReturn(identityClient);
-
- // test
- ManagedIdentityCredential credential = new ManagedIdentityCredentialBuilder()
- .clientId(clientId)
- .tokenRefreshOffset(offset)
- .build();
- StepVerifier.create(credential.getToken(request1))
- .expectNextMatches(token -> token1.equals(token.getToken())
- && expiresAt.getSecond() == token.getExpiresAt().getSecond())
- .verifyComplete();
- } finally {
- // clean up
- configuration.remove("MSI_ENDPOINT");
- configuration.remove("MSI_SECRET");
- }
- }
-
- @Test
- public void testIMDSWithTokenRefreshOffset() throws Exception {
- // setup
- String token1 = "token1";
- TokenRequestContext request = new TokenRequestContext().addScopes("https://management.azure.com");
- OffsetDateTime expiresOn = OffsetDateTime.now(ZoneOffset.UTC).plusHours(1);
- Duration offset = Duration.ofMinutes(10);
-
- // mock
- IdentityClient identityClient = PowerMockito.mock(IdentityClient.class);
- when(identityClient.authenticateToIMDSEndpoint(request)).thenReturn(TestUtils.getMockAccessToken(token1, expiresOn, offset));
- PowerMockito.whenNew(IdentityClient.class).withAnyArguments().thenReturn(identityClient);
-
- // test
- ManagedIdentityCredential credential = new ManagedIdentityCredentialBuilder()
- .clientId(clientId)
- .tokenRefreshOffset(offset)
- .build();
- StepVerifier.create(credential.getToken(request))
- .expectNextMatches(token -> token1.equals(token.getToken())
- && expiresOn.getSecond() == token.getExpiresAt().getSecond())
- .verifyComplete();
- }
}
diff --git a/sdk/identity/azure-identity/src/test/java/com/azure/identity/implementation/MSITokenTests.java b/sdk/identity/azure-identity/src/test/java/com/azure/identity/implementation/MSITokenTests.java
index 3284fc197e46b..ec8e6a590ba64 100644
--- a/sdk/identity/azure-identity/src/test/java/com/azure/identity/implementation/MSITokenTests.java
+++ b/sdk/identity/azure-identity/src/test/java/com/azure/identity/implementation/MSITokenTests.java
@@ -10,7 +10,7 @@
import java.time.ZoneOffset;
public class MSITokenTests {
- private OffsetDateTime expected = OffsetDateTime.of(2020, 1, 10, 15, 1, 28, 0, ZoneOffset.UTC);
+ private OffsetDateTime expected = OffsetDateTime.of(2020, 1, 10, 15, 3, 28, 0, ZoneOffset.UTC);
@Test
public void canParseLong() {
@@ -30,11 +30,11 @@ public void canParseDateTime12Hr() {
Assert.assertEquals(expected.toEpochSecond(), token.getExpiresAt().toEpochSecond());
token = new MSIToken("fake_token", "12/20/2019 4:58:20 AM +00:00");
- expected = OffsetDateTime.of(2019, 12, 20, 4, 56, 20, 0, ZoneOffset.UTC);
+ expected = OffsetDateTime.of(2019, 12, 20, 4, 58, 20, 0, ZoneOffset.UTC);
Assert.assertEquals(expected.toEpochSecond(), token.getExpiresAt().toEpochSecond());
token = new MSIToken("fake_token", "1/1/2020 0:00:00 PM +00:00");
- expected = OffsetDateTime.of(2020, 1, 1, 11, 58, 0, 0, ZoneOffset.UTC);
+ expected = OffsetDateTime.of(2020, 1, 1, 12, 0, 0, 0, ZoneOffset.UTC);
Assert.assertEquals(expected.toEpochSecond(), token.getExpiresAt().toEpochSecond());
}
}
diff --git a/sdk/identity/azure-identity/src/test/java/com/azure/identity/util/TestUtils.java b/sdk/identity/azure-identity/src/test/java/com/azure/identity/util/TestUtils.java
index 229e127344043..e26574ebb32f2 100644
--- a/sdk/identity/azure-identity/src/test/java/com/azure/identity/util/TestUtils.java
+++ b/sdk/identity/azure-identity/src/test/java/com/azure/identity/util/TestUtils.java
@@ -4,7 +4,6 @@
package com.azure.identity.util;
import com.azure.core.credential.AccessToken;
-import com.azure.identity.implementation.IdentityClientOptions;
import com.azure.identity.implementation.MsalToken;
import com.microsoft.aad.msal4j.IAccount;
import com.microsoft.aad.msal4j.IAuthenticationResult;
@@ -84,7 +83,7 @@ public Date expiresOnDate() {
*/
public static Mono getMockMsalToken(String accessToken, OffsetDateTime expiresOn) {
return Mono.fromFuture(getMockAuthenticationResult(accessToken, expiresOn))
- .map(ar -> new MsalToken(ar, new IdentityClientOptions()));
+ .map(MsalToken::new);
}
/**