in core/src/main/java/com/microsoft/alm/auth/oauth/OAuth2Authenticator.java [160:285]
public TokenPair getOAuth2TokenPair(final URI uri, final PromptBehavior promptBehavior) {
Debug.Assert(promptBehavior != null, "getOAuth2TokenPair promptBehavior cannot be null");
Debug.Assert(uri != null, "getOAuth2TokenPair uri cannot be null");
logger.debug("Retrieving OAuth2 TokenPair with prompt behavior: {}", promptBehavior.name());
final String key = getKey(APP_VSSPS_VISUALSTUDIO);
final SecretRetriever<TokenPair> secretRetriever = new SecretRetriever<TokenPair>() {
private boolean validateAccessToken(final Token accessToken, final URI validationEndpoint) {
final HttpClient client = Global.getHttpClientFactory().createHttpClient();
accessToken.contributeHeader(client.getHeaders());
try {
final String response = client.getGetResponseText(validationEndpoint);
return true;
} catch (IOException e) {
logger.debug("Validation failed with IOException.", e);
}
return false;
}
@Override
protected boolean tryGetValidated(final TokenPair tokenPair, final AtomicReference<TokenPair> holder) {
Debug.Assert(tokenPair != null, "TokenPair is null");
Debug.Assert(holder != null, "Holder is null");
final URI validationEndpoint = URI.create(VALIDATION_ENDPOINT);
boolean valid = false;
if (tokenPair.AccessToken != null && !StringHelper.isNullOrEmpty(tokenPair.AccessToken.Value)) {
logger.debug("Validating stored OAuth2 Access Token...");
valid = validateAccessToken(tokenPair.AccessToken, validationEndpoint);
}
if (!valid && tokenPair.RefreshToken != null
&& !StringHelper.isNullOrEmpty(tokenPair.RefreshToken.Value)) {
logger.debug("OAuth2 Access Token is not valid, and we have a refresh token, try refreshing...");
final TokenPair renewedTokenPair =
getAzureAuthority(uri).acquireTokenByRefreshToken(clientId, resource, tokenPair.RefreshToken);
if (renewedTokenPair != null
&& renewedTokenPair.AccessToken.Value != null
&& renewedTokenPair.RefreshToken.Value != null) {
logger.debug("OAuth2 Access Token refreshed successfully.");
valid = true;
holder.set(renewedTokenPair);
}
}
logger.debug("OAuth2 Access Token is {}.", valid ? "valid" : "invalid.");
return valid;
}
@Override
protected TokenPair doRetrieve() {
logger.info("Ready to launch browser flow to retrieve oauth2 token.");
final AtomicReference<File> swtRuntime = new AtomicReference<File>();
final String defaultProviderName
= SettingsHelper.getInstance().getProperty(USER_AGENT_PROVIDER_PROPERTY_NAME, JAVAFX_PROVIDER_NAME);
logger.info("Attempt to use oauth2-useragent provider: {}", defaultProviderName);
final boolean favorSwtBrowser = defaultProviderName.equals(SWT_PROIVDER_NAME);
final boolean favorDeviceFlow = defaultProviderName.equalsIgnoreCase("none");
try {
if (favorSwtBrowser) {
logger.debug("Prefer SWT Browser, download SWT Runtime if it is not available.");
if (oAuth2UseragentValidator.isOnlyMissingRuntimeFromSwtProvider()) {
SwtJarLoader.tryGetSwtJar(swtRuntime);
}
}
if (!favorDeviceFlow) {
if (oAuth2UseragentValidator.isOAuth2ProviderAvailable()
|| (oAuth2UseragentValidator.isOnlyMissingRuntimeFromSwtProvider()
&& SwtJarLoader.tryGetSwtJar(swtRuntime))) {
try {
logger.info("Using oauth2-useragent providers to retrieve AAD token.");
return getAzureAuthority(uri).acquireToken(clientId, resource, redirectUri, POPUP_QUERY_PARAM);
} catch (final AuthorizationException e) {
logError(logger, "Failed to launch oauth2-useragent.", e);
// unless we failed with unknown reasons (such as failed to load javafx) we probably should
// just return null
if (!"unknown_error".equalsIgnoreCase(e.getCode())) {
// This error code isn't exposed as a value, so just hardcode this string
return null;
}
}
}
}
} catch (IllegalArgumentException iae) {
if (iae.getMessage().startsWith("Unrecognized version string")) {
// On IBM jvm, oauth2-useragent library is not able to parse the jvm version string
// which is in the form of: "jvmwi3260sr9-20110218_76011".
// This is an expected error on IBM jvm.
logError(logger, "Could not parse JVM version, continue with device flow.", iae);
} else {
// This is not expected, throw it to fail fast.
throw iae;
}
}
// Fallback to Device Flow if there's a callback and the oauth2-useragent couldn't launch the
// browser properly
if (deviceFlowCallback != null) {
logger.info("Fallback to Device Flow.");
try {
return getAzureAuthority(uri).acquireToken(clientId, resource, redirectUri, deviceFlowCallback);
} catch (final AuthorizationException e) {
logError(logger, "Failed to use the Device Flow authenticator.", e);
}
}
return null;
}
};
return secretRetriever.retrieve(key, getStore(), promptBehavior);
}