public TokenPair getOAuth2TokenPair()

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);
    }