ChainedTokenCredential.java
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.azure.identity.credential;
import com.azure.core.annotation.Immutable;
import com.azure.core.credentials.AccessToken;
import com.azure.core.credentials.TokenCredential;
import com.azure.core.credentials.TokenRequest;
import com.azure.core.exception.ClientAuthenticationException;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.util.Deque;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
/**
* A token credential provider that can provide a credential from a list of providers.
*
* <p><strong>Sample: Construct a ChainedTokenCredential with silent username+password login tried first, then
* interactive browser login as needed (e.g. when 2FA is turned on in the directory).</strong></p>
* {@codesnippet com.azure.identity.credential.chainedtokencredential.construct}
*/
@Immutable
public class ChainedTokenCredential implements TokenCredential {
private final Deque<TokenCredential> credentials;
/**
* Create an instance of chained token credential that aggregates a list of token
* credentials.
*/
ChainedTokenCredential(Deque<TokenCredential> credentials) {
this.credentials = credentials;
}
@Override
public Mono<AccessToken> getToken(TokenRequest request) {
AtomicReference<Throwable> cause = new AtomicReference<>();
return Flux.fromIterable(credentials)
.flatMap(p -> p.getToken(request).onErrorResume(t -> {
if (cause.get() != null) {
t.initCause(cause.get());
}
cause.set(t);
return Mono.empty();
}))
.next()
.switchIfEmpty(Mono.defer(() -> Mono.error(new ClientAuthenticationException("Tried "
+ credentials.stream().map(c -> c.getClass().getSimpleName()).collect(Collectors.joining(", "))
+ " but failed to acquire a token for any of them. Please verify the environment for either of them"
+ " and see more details in the causes below.", null, cause.get()))));
}
}